From a4349f6692a52a5484a84070eda52ca860db4b9e Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Tue, 22 Feb 2022 14:25:43 +0300 Subject: [PATCH] [#55] refs: Add Scheme field to Signature Signed-off-by: Evgenii Stratonikov --- refs/convert.go | 2 ++ refs/grpc/types.go | 20 +++++++++++++++++ refs/grpc/types.pb.go | Bin 23686 -> 26473 bytes refs/marshal.go | 15 ++++++++++--- refs/string.go | 21 ++++++++++++++++++ refs/test/generate.go | 3 +++ refs/types.go | 22 +++++++++++++++++++ signature/sign.go | 19 ++++------------ util/signature/data.go | 39 ++++++++++++++------------------- util/signature/options.go | 45 +++++++++++++++++++++++++++++++++----- 10 files changed, 140 insertions(+), 46 deletions(-) diff --git a/refs/convert.go b/refs/convert.go index 80b03d2..bb6e988 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 7b0a9b9..b1c48c1 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 6c0345fc86f730a2d847373ac3dba86a09bba463..7df2f05a3a3705d71d243623267ed91df6df31bb 100644 GIT binary patch delta 2222 zcmah~O>7%Q6xKR%f?ZNmu$|u~(^QJ=)=74~UZ-}Giqt=`OZ=la2q~yIjy;Z-#%r@b zZK^6VNC?EK(04}attTWZOGps{aRC7mAPzlns6avpAr-X}Cr&UkYo}R<2p`7tzIpHa z-g`68erKQi<9qJ?CKtN@#sdx)_!2D%MN7 z+)xPI#7ad&WLqLEsxFs}osx<)T{X%qIZ8U`bXaL|@#SnGpNOTC=|sFo5~C%=J00`$ zC=rVnmeGxttk+N>wLB{cK~yVgHCmD{na&rGa3MS^`u~HTg%(K=jp@-Q`~u^TV^T|9 zR%DH&-Bs&~1*LFDY?-L7C1WTYS%~1%m-YVQFyX? z)=r&+z~@F_J~KQ(Einqk+!)}D2R_bt;dW*kekI>uWhP-b>xI8FQ!Ho!CbIL;BFJVI zF(d+S=0@SeYh%EwyT>V=`NKf3~8qs{c0#0 of=1p8r}KmGBsX*ve!&Wn=I2jXA(3Mg?fv|p4ldZfxA8OgFWVWCpa1{> delta 546 zcmY+8L1@!p7>4=NT9PbwI@i`wI>J!#-!hsuZ3OFKv&ux;v|Z|wG-4Gg2t!wAD-+#K zCU_9xJ&Bjf4uYaka@u`2vEV^GipPmCcGzWyZTYi{JACjz@B8q5|JH!4ZIHKZlIcJG zNk|cuLflL9b_lN`W9SXJ*pEcURGD~IVR84n6X$3j1M)6^@`49Zl2}S^FjW-dLf+4Dl*<2@n%qNAHjK( znN>3|cZxB*H%F1WDqzE$J|PO~N)G+vDE7?}UmK|h+KoEJ!K~mtuS*=4p_o~a(QESP zFWfm>e9*eT_@w)={;b`i7gegrvYbxKDo#6NShbS4YsK)@8V~zn7C$T%%|;xb90}*_ z7&w0_c2MTAY^%)D@zG9W-EVrjfL%L*U;cah3Xe%gCmQZKx45i=rWMD)sRqT1hAL_b wyX$I~HT-R)5=-4qyW44Y{BMcEv5x+)rrfFHC}Y_@Lo{#A4T&V9&`hfgP^mjD0& diff --git a/refs/marshal.go b/refs/marshal.go index 148ad58..c496221 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 b27e07f..1b98600 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 835f358..3912ffa 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 cd7ba5d..93a814a 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 7054416..c954891 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 00d591e..ee2d7ad 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 16d6330..c77c290 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 } }