diff --git a/refs/convert.go b/refs/convert.go index 80b03d2a..bb6e988c 100644 --- a/refs/convert.go +++ b/refs/convert.go @@ -260,6 +260,7 @@ func (s *Signature) ToGRPCMessage() grpc.Message { m.SetKey(s.key) m.SetSign(s.sign) + m.SetScheme(refs.SignatureScheme(s.scheme)) } return m @@ -273,6 +274,7 @@ func (s *Signature) FromGRPCMessage(m grpc.Message) error { s.key = v.GetKey() s.sign = v.GetSign() + s.scheme = SignatureScheme(v.GetScheme()) return nil } diff --git a/refs/grpc/types.go b/refs/grpc/types.go index 7b0a9b92..b1c48c1f 100644 --- a/refs/grpc/types.go +++ b/refs/grpc/types.go @@ -77,6 +77,26 @@ func (x *Signature) SetSign(v []byte) { } } +// SetScheme sets signature scheme. +func (x *Signature) SetScheme(s SignatureScheme) { + if x != nil { + x.Scheme = s + } +} + +// FromString parses SignatureScheme from a string representation, +// It is a reverse action to String(). +// +// Returns true if s was parsed successfully. +func (x *SignatureScheme) FromString(s string) bool { + i, ok := SignatureScheme_value[s] + if ok { + *x = SignatureScheme(i) + } + + return ok +} + // FromString parses ChecksumType from a string representation, // It is a reverse action to String(). // diff --git a/refs/grpc/types.pb.go b/refs/grpc/types.pb.go index 6c0345fc..7df2f05a 100644 Binary files a/refs/grpc/types.pb.go and b/refs/grpc/types.pb.go differ diff --git a/refs/marshal.go b/refs/marshal.go index 148ad583..c496221d 100644 --- a/refs/marshal.go +++ b/refs/marshal.go @@ -19,8 +19,9 @@ const ( checksumTypeField = 1 checksumValueField = 2 - signatureKeyField = 1 - signatureValueField = 2 + signatureKeyField = 1 + signatureValueField = 2 + signatureSchemeField = 3 versionMajorField = 1 versionMinorField = 2 @@ -250,7 +251,14 @@ func (s *Signature) StableMarshal(buf []byte) ([]byte, error) { offset += n - _, err = proto.BytesMarshal(signatureValueField, buf[offset:], s.sign) + n, err = proto.BytesMarshal(signatureValueField, buf[offset:], s.sign) + if err != nil { + return nil, err + } + + offset += n + + _, err = proto.EnumMarshal(signatureSchemeField, buf[offset:], int32(s.scheme)) if err != nil { return nil, err } @@ -265,6 +273,7 @@ func (s *Signature) StableSize() (size int) { size += proto.BytesSize(signatureKeyField, s.key) size += proto.BytesSize(signatureValueField, s.sign) + size += proto.EnumSize(signatureSchemeField, int32(s.scheme)) return size } diff --git a/refs/string.go b/refs/string.go index b27e07f4..1b986000 100644 --- a/refs/string.go +++ b/refs/string.go @@ -24,3 +24,24 @@ func (t *ChecksumType) FromString(s string) bool { return ok } + +// String returns string representation of SignatureScheme. +func (t SignatureScheme) String() string { + return refs.SignatureScheme(t).String() +} + +// FromString parses SignatureScheme from a string representation. +// It is a reverse action to String(). +// +// Returns true if s was parsed successfully. +func (t *SignatureScheme) FromString(s string) bool { + var g refs.SignatureScheme + + ok := g.FromString(s) + + if ok { + *t = SignatureScheme(g) + } + + return ok +} diff --git a/refs/test/generate.go b/refs/test/generate.go index 835f3580..3912ffaf 100644 --- a/refs/test/generate.go +++ b/refs/test/generate.go @@ -1,6 +1,8 @@ package refstest import ( + "math/rand" + "github.com/nspcc-dev/neofs-api-go/v2/refs" ) @@ -88,6 +90,7 @@ func GenerateSignature(empty bool) *refs.Signature { if !empty { m.SetKey([]byte{1}) m.SetSign([]byte{2}) + m.SetScheme(refs.SignatureScheme(rand.Int31() % 3)) } return m diff --git a/refs/types.go b/refs/types.go index cd7ba5d8..93a814ac 100644 --- a/refs/types.go +++ b/refs/types.go @@ -31,8 +31,17 @@ type Checksum struct { type ChecksumType uint32 +type SignatureScheme uint32 + +const ( + UnspecifiedScheme SignatureScheme = iota + ECDSA_SHA512 + ECDSA_RFC6979_SHA256 +) + type Signature struct { key, sign []byte + scheme SignatureScheme } type SubnetID struct { @@ -175,6 +184,19 @@ func (s *Signature) SetSign(v []byte) { } } +func (s *Signature) GetScheme() SignatureScheme { + if s != nil { + return s.scheme + } + return UnspecifiedScheme +} + +func (s *Signature) SetScheme(scheme SignatureScheme) { + if s != nil { + s.scheme = scheme + } +} + func (s *SubnetID) SetValue(id uint32) { if s != nil { s.value = id diff --git a/signature/sign.go b/signature/sign.go index 7054416e..c9548914 100644 --- a/signature/sign.go +++ b/signature/sign.go @@ -131,19 +131,6 @@ func (s StableMarshalerWrapper) SignedDataSize() int { return 0 } -func keySignatureHandler(s *refs.Signature) signature.KeySignatureHandler { - return func(key []byte, sig []byte) { - s.SetKey(key) - s.SetSign(sig) - } -} - -func keySignatureSource(s *refs.Signature) signature.KeySignatureSource { - return func() ([]byte, []byte) { - return s.GetKey(), s.GetSign() - } -} - func SignServiceMessage(key *ecdsa.PrivateKey, msg interface{}) error { var ( body, meta, verifyOrigin stableMarshaler @@ -213,7 +200,9 @@ func signServiceMessagePart(key *ecdsa.PrivateKey, part stableMarshaler, sigWrit if err := signature.SignDataWithHandler( key, &StableMarshalerWrapper{part}, - keySignatureHandler(sig), + func(s *refs.Signature) { + *sig = *s + }, ); err != nil { return err } @@ -285,7 +274,7 @@ func verifyMatryoshkaLevel(body stableMarshaler, meta metaHeader, verify verific func verifyServiceMessagePart(part stableMarshaler, sigRdr func() *refs.Signature) error { return signature.VerifyDataWithSource( &StableMarshalerWrapper{part}, - keySignatureSource(sigRdr()), + sigRdr, ) } diff --git a/util/signature/data.go b/util/signature/data.go index 00d591e2..ee2d7adf 100644 --- a/util/signature/data.go +++ b/util/signature/data.go @@ -3,6 +3,7 @@ package signature import ( "crypto/ecdsa" + "github.com/nspcc-dev/neofs-api-go/v2/refs" crypto "github.com/nspcc-dev/neofs-crypto" ) @@ -13,24 +14,24 @@ type DataSource interface { type DataWithSignature interface { DataSource - GetSignatureWithKey() (key, sig []byte) - SetSignatureWithKey(key, sig []byte) + GetSignature() *refs.Signature + SetSignature(*refs.Signature) } type SignOption func(*cfg) -type KeySignatureHandler func(key []byte, sig []byte) +type KeySignatureHandler func(*refs.Signature) -type KeySignatureSource func() (key, sig []byte) +type KeySignatureSource func() *refs.Signature -func DataSignature(key *ecdsa.PrivateKey, src DataSource, opts ...SignOption) ([]byte, error) { +func SignDataWithHandler(key *ecdsa.PrivateKey, src DataSource, handler KeySignatureHandler, opts ...SignOption) error { if key == nil { - return nil, crypto.ErrEmptyPrivateKey + return crypto.ErrEmptyPrivateKey } data, err := dataForSignature(src) if err != nil { - return nil, err + return err } defer bytesPool.Put(data) @@ -40,16 +41,16 @@ func DataSignature(key *ecdsa.PrivateKey, src DataSource, opts ...SignOption) ([ opts[i](cfg) } - return cfg.signFunc(key, data) -} - -func SignDataWithHandler(key *ecdsa.PrivateKey, src DataSource, handler KeySignatureHandler, opts ...SignOption) error { - sig, err := DataSignature(key, src, opts...) + sigData, err := sign(cfg, cfg.defaultScheme, key, data) if err != nil { return err } - handler(crypto.MarshalPublicKey(&key.PublicKey), sig) + sig := new(refs.Signature) + sig.SetScheme(cfg.defaultScheme) + sig.SetKey(crypto.MarshalPublicKey(&key.PublicKey)) + sig.SetSign(sigData) + handler(sig) return nil } @@ -67,19 +68,13 @@ func VerifyDataWithSource(dataSrc DataSource, sigSrc KeySignatureSource, opts .. opts[i](cfg) } - key, sig := sigSrc() - - return cfg.verifyFunc( - crypto.UnmarshalPublicKey(key), - data, - sig, - ) + return verify(cfg, data, sigSrc()) } func SignData(key *ecdsa.PrivateKey, v DataWithSignature, opts ...SignOption) error { - return SignDataWithHandler(key, v, v.SetSignatureWithKey, opts...) + return SignDataWithHandler(key, v, v.SetSignature, opts...) } func VerifyData(src DataWithSignature, opts ...SignOption) error { - return VerifyDataWithSource(src, src.GetSignatureWithKey, opts...) + return VerifyDataWithSource(src, src.GetSignature, opts...) } diff --git a/util/signature/options.go b/util/signature/options.go index 16d63304..c77c290e 100644 --- a/util/signature/options.go +++ b/util/signature/options.go @@ -2,25 +2,58 @@ package signature import ( "crypto/ecdsa" + "fmt" + "github.com/nspcc-dev/neofs-api-go/v2/refs" crypto "github.com/nspcc-dev/neofs-crypto" ) type cfg struct { - signFunc func(key *ecdsa.PrivateKey, msg []byte) ([]byte, error) - verifyFunc func(key *ecdsa.PublicKey, msg []byte, sig []byte) error + defaultScheme refs.SignatureScheme + restrictScheme refs.SignatureScheme } func defaultCfg() *cfg { return &cfg{ - signFunc: crypto.Sign, - verifyFunc: crypto.Verify, + defaultScheme: refs.ECDSA_SHA512, + restrictScheme: refs.UnspecifiedScheme, + } +} + +func verify(cfg *cfg, data []byte, sig *refs.Signature) error { + scheme := sig.GetScheme() + if scheme == refs.UnspecifiedScheme { + scheme = cfg.defaultScheme + } + if cfg.restrictScheme != refs.UnspecifiedScheme && scheme != cfg.restrictScheme { + return fmt.Errorf("%w: unexpected signature scheme", crypto.ErrInvalidSignature) + } + + pub := crypto.UnmarshalPublicKey(sig.GetKey()) + switch scheme { + case refs.ECDSA_SHA512: + return crypto.Verify(pub, data, sig.GetSign()) + case refs.ECDSA_RFC6979_SHA256: + return crypto.VerifyRFC6979(pub, data, sig.GetSign()) + default: + return crypto.ErrInvalidSignature + } +} + +func sign(cfg *cfg, scheme refs.SignatureScheme, key *ecdsa.PrivateKey, data []byte) ([]byte, error) { + switch scheme { + case refs.ECDSA_SHA512: + return crypto.Sign(key, data) + case refs.ECDSA_RFC6979_SHA256: + return crypto.SignRFC6979(key, data) + default: + panic("unsupported scheme") } } func SignWithRFC6979() SignOption { return func(c *cfg) { - c.signFunc = crypto.SignRFC6979 - c.verifyFunc = crypto.VerifyRFC6979 + c.defaultScheme = refs.ECDSA_RFC6979_SHA256 + c.restrictScheme = refs.ECDSA_RFC6979_SHA256 } }