From 1c7dd03cf5814c8b31f800cbbd277ead6493976c Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Thu, 24 Feb 2022 14:05:05 +0300 Subject: [PATCH] [#150] signature: Add scheme Allow `SignOption` to set 2 parameters: 1. Default signature scheme, which is used in case scheme is unspecified. 2. Restrict scheme option which also checks that scheme is either unspecified or equal to the restricted scheme. This is only used for verification and is necessary because some of the signatures are used in smart-contracts. Also provide signature struct to sign/verify functions in helpers. The constant names differ a bit from those in API because of linter complaints. Signed-off-by: Evgenii Stratonikov --- client/container.go | 22 +++------- object/fmt.go | 11 ++--- reputation/trust.go | 22 ++++------ session/session.go | 18 ++------ signature/signature.go | 20 +++++++++ token/bearer.go | 16 +++---- util/signature/data.go | 64 ++++++++++++++-------------- util/signature/options.go | 87 ++++++++++++++++++++++----------------- 8 files changed, 129 insertions(+), 131 deletions(-) diff --git a/client/container.go b/client/container.go index ef7c0e88..4e2aa7de 100644 --- a/client/container.go +++ b/client/container.go @@ -4,7 +4,6 @@ import ( "context" v2container "github.com/nspcc-dev/neofs-api-go/v2/container" - "github.com/nspcc-dev/neofs-api-go/v2/refs" rpcapi "github.com/nspcc-dev/neofs-api-go/v2/rpc" "github.com/nspcc-dev/neofs-api-go/v2/rpc/client" v2session "github.com/nspcc-dev/neofs-api-go/v2/session" @@ -87,11 +86,8 @@ func (c *Client) ContainerPut(ctx context.Context, prm PrmContainerPut) (*ResCon // sign container signWrapper := v2signature.StableMarshalerWrapper{SM: reqBody.GetContainer()} - err := sigutil.SignDataWithHandler(c.opts.key, signWrapper, func(key []byte, sig []byte) { - containerSignature := new(refs.Signature) - containerSignature.SetKey(key) - containerSignature.SetSign(sig) - reqBody.SetSignature(containerSignature) + err := sigutil.SignDataWithHandler(c.opts.key, signWrapper, func(sig *signature.Signature) { + reqBody.SetSignature(sig.ToV2()) }, sigutil.SignWithRFC6979()) if err != nil { return nil, err @@ -402,11 +398,8 @@ func (c *Client) ContainerDelete(ctx context.Context, prm PrmContainerDelete) (* delContainerSignWrapper{ body: reqBody, }, - func(key []byte, sig []byte) { - containerSignature := new(refs.Signature) - containerSignature.SetKey(key) - containerSignature.SetSign(sig) - reqBody.SetSignature(containerSignature) + func(sig *signature.Signature) { + reqBody.SetSignature(sig.ToV2()) }, sigutil.SignWithRFC6979()) if err != nil { @@ -599,11 +592,8 @@ func (c *Client) ContainerSetEACL(ctx context.Context, prm PrmContainerSetEACL) // sign the eACL table signWrapper := v2signature.StableMarshalerWrapper{SM: reqBody.GetEACL()} - err := sigutil.SignDataWithHandler(c.opts.key, signWrapper, func(key []byte, sig []byte) { - eaclSignature := new(refs.Signature) - eaclSignature.SetKey(key) - eaclSignature.SetSign(sig) - reqBody.SetSignature(eaclSignature) + err := sigutil.SignDataWithHandler(c.opts.key, signWrapper, func(sig *signature.Signature) { + reqBody.SetSignature(sig.ToV2()) }, sigutil.SignWithRFC6979()) if err != nil { return nil, err diff --git a/object/fmt.go b/object/fmt.go index 8229c198..2600d84a 100644 --- a/object/fmt.go +++ b/object/fmt.go @@ -94,9 +94,8 @@ func CalculateIDSignature(key *ecdsa.PrivateKey, id *oid.ID) (*signature.Signatu signatureV2.StableMarshalerWrapper{ SM: id.ToV2(), }, - func(key, sign []byte) { - sig.SetKey(key) - sig.SetSign(sign) + func(s *signature.Signature) { + *sig = *s }, ); err != nil { return nil, err @@ -121,11 +120,7 @@ func VerifyIDSignature(obj *Object) error { signatureV2.StableMarshalerWrapper{ SM: obj.ID().ToV2(), }, - func() ([]byte, []byte) { - sig := obj.Signature() - - return sig.Key(), sig.Sign() - }, + obj.Signature, ) } diff --git a/reputation/trust.go b/reputation/trust.go index bfa91721..034b937b 100644 --- a/reputation/trust.go +++ b/reputation/trust.go @@ -6,7 +6,8 @@ import ( "github.com/nspcc-dev/neofs-api-go/v2/refs" "github.com/nspcc-dev/neofs-api-go/v2/reputation" signatureV2 "github.com/nspcc-dev/neofs-api-go/v2/signature" - "github.com/nspcc-dev/neofs-sdk-go/util/signature" + "github.com/nspcc-dev/neofs-sdk-go/signature" + sigutil "github.com/nspcc-dev/neofs-sdk-go/util/signature" "github.com/nspcc-dev/neofs-sdk-go/version" ) @@ -259,18 +260,11 @@ func (x *GlobalTrust) Trust() *Trust { func (x *GlobalTrust) Sign(key *ecdsa.PrivateKey) error { v2 := (*reputation.GlobalTrust)(x) - sigV2 := v2.GetSignature() - if sigV2 == nil { - sigV2 = new(refs.Signature) - v2.SetSignature(sigV2) - } - - return signature.SignDataWithHandler( + return sigutil.SignDataWithHandler( key, signatureV2.StableMarshalerWrapper{SM: v2.GetBody()}, - func(key, sig []byte) { - sigV2.SetKey(key) - sigV2.SetSign(sig) + func(sig *signature.Signature) { + v2.SetSignature(sig.ToV2()) }, ) } @@ -284,10 +278,10 @@ func (x *GlobalTrust) VerifySignature() error { sigV2 = new(refs.Signature) } - return signature.VerifyDataWithSource( + return sigutil.VerifyDataWithSource( signatureV2.StableMarshalerWrapper{SM: v2.GetBody()}, - func() ([]byte, []byte) { - return sigV2.GetKey(), sigV2.GetSign() + func() *signature.Signature { + return signature.NewFromV2(sigV2) }, ) } diff --git a/session/session.go b/session/session.go index b926ba88..fe5a8ae7 100644 --- a/session/session.go +++ b/session/session.go @@ -3,7 +3,6 @@ package session import ( "crypto/ecdsa" - "github.com/nspcc-dev/neofs-api-go/v2/refs" "github.com/nspcc-dev/neofs-api-go/v2/session" v2signature "github.com/nspcc-dev/neofs-api-go/v2/signature" "github.com/nspcc-dev/neofs-sdk-go/owner" @@ -169,16 +168,8 @@ func (t *Token) Sign(key *ecdsa.PrivateKey) error { SM: tV2.GetBody(), } - return sigutil.SignDataWithHandler(key, signedData, func(key, sig []byte) { - tSig := tV2.GetSignature() - if tSig == nil { - tSig = new(refs.Signature) - } - - tSig.SetKey(key) - tSig.SetSign(sig) - - tV2.SetSignature(tSig) + return sigutil.SignDataWithHandler(key, signedData, func(sig *signature.Signature) { + tV2.SetSignature(sig.ToV2()) }) } @@ -191,10 +182,7 @@ func (t *Token) VerifySignature() bool { SM: tV2.GetBody(), } - return sigutil.VerifyDataWithSource(signedData, func() (key, sig []byte) { - tSig := tV2.GetSignature() - return tSig.GetKey(), tSig.GetSign() - }) == nil + return sigutil.VerifyDataWithSource(signedData, t.Signature) == nil } // Signature returns Token signature. diff --git a/signature/signature.go b/signature/signature.go index c0007ccf..a79b5bef 100644 --- a/signature/signature.go +++ b/signature/signature.go @@ -7,6 +7,16 @@ import ( // Signature represents v2-compatible signature. type Signature refs.Signature +// Scheme represents signature scheme. +type Scheme uint32 + +// Supported signature schemes. +const ( + Unspecified Scheme = iota + ECDSAWithSHA512 + RFC6979WithSHA256 +) + // NewFromV2 wraps v2 Signature message to Signature. // // Nil refs.Signature converts to nil. @@ -43,6 +53,16 @@ func (s *Signature) SetSign(v []byte) { (*refs.Signature)(s).SetSign(v) } +// Scheme returns signature scheme. +func (s *Signature) Scheme() Scheme { + return Scheme((*refs.Signature)(s).GetScheme()) +} + +// SetScheme sets signature scheme. +func (s *Signature) SetScheme(v Scheme) { + (*refs.Signature)(s).SetScheme(refs.SignatureScheme(v)) +} + // ToV2 converts Signature to v2 Signature message. // // Nil Signature converts to nil. diff --git a/token/bearer.go b/token/bearer.go index d2a82f7e..b40159b8 100644 --- a/token/bearer.go +++ b/token/bearer.go @@ -7,12 +7,11 @@ import ( "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neofs-api-go/v2/acl" - "github.com/nspcc-dev/neofs-api-go/v2/refs" v2signature "github.com/nspcc-dev/neofs-api-go/v2/signature" "github.com/nspcc-dev/neofs-sdk-go/eacl" "github.com/nspcc-dev/neofs-sdk-go/owner" "github.com/nspcc-dev/neofs-sdk-go/signature" - util "github.com/nspcc-dev/neofs-sdk-go/util/signature" + sigutil "github.com/nspcc-dev/neofs-sdk-go/util/signature" ) var ( @@ -103,11 +102,8 @@ func (b *BearerToken) SignToken(key *ecdsa.PrivateKey) error { signWrapper := v2signature.StableMarshalerWrapper{SM: b.token.GetBody()} - return util.SignDataWithHandler(key, signWrapper, func(key []byte, sig []byte) { - bearerSignature := new(refs.Signature) - bearerSignature.SetKey(key) - bearerSignature.SetSign(sig) - b.token.SetSignature(bearerSignature) + return sigutil.SignDataWithHandler(key, signWrapper, func(sig *signature.Signature) { + b.token.SetSignature(sig.ToV2()) }) } @@ -120,11 +116,11 @@ func (b BearerToken) VerifySignature() error { return nil } - return util.VerifyDataWithSource( + return sigutil.VerifyDataWithSource( v2signature.StableMarshalerWrapper{SM: b.token.GetBody()}, - func() (key, sig []byte) { + func() *signature.Signature { sigV2 := b.token.GetSignature() - return sigV2.GetKey(), sigV2.GetSign() + return signature.NewFromV2(sigV2) }) } diff --git a/util/signature/data.go b/util/signature/data.go index 783c66ee..3ded29be 100644 --- a/util/signature/data.go +++ b/util/signature/data.go @@ -7,6 +7,7 @@ import ( "fmt" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" + "github.com/nspcc-dev/neofs-sdk-go/signature" ) type DataSource interface { @@ -16,15 +17,15 @@ type DataSource interface { type DataWithSignature interface { DataSource - GetSignatureWithKey() (key, sig []byte) - SetSignatureWithKey(key, sig []byte) + GetSignature() *signature.Signature + SetSignature(*signature.Signature) } type SignOption func(*cfg) -type KeySignatureHandler func(key []byte, sig []byte) +type KeySignatureHandler func(*signature.Signature) -type KeySignatureSource func() (key, sig []byte) +type KeySignatureSource func() *signature.Signature const ( // PrivateKeyCompressedSize is constant with compressed size of private key (SK). @@ -49,34 +50,30 @@ var ( ErrInvalidSignature = errors.New("invalid 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, ErrEmptyPrivateKey + return ErrEmptyPrivateKey } data, err := dataForSignature(src) if err != nil { - return nil, err + return err } defer bytesPool.Put(&data) - cfg := defaultCfg() + cfg := getConfig(opts...) - for i := range opts { - 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.defaultScheme, key, data) if err != nil { return err } - pub := (*keys.PublicKey)(&key.PublicKey) - handler(pub.Bytes(), sig) + sig := signature.New() + sig.SetKey((*keys.PublicKey)(&key.PublicKey).Bytes()) + sig.SetSign(sigData) + sig.SetScheme(cfg.defaultScheme) + + handler(sig) return nil } @@ -88,33 +85,38 @@ func VerifyDataWithSource(dataSrc DataSource, sigSrc KeySignatureSource, opts .. } defer bytesPool.Put(&data) - cfg := defaultCfg() + cfg := getConfig(opts...) - for i := range opts { - opts[i](cfg) - } - - key, sig := sigSrc() + sig := sigSrc() var pub *keys.PublicKey - if len(key) != 0 { - pub, err = keys.NewPublicKeyFromBytes(key, elliptic.P256()) + if len(sig.Key()) != 0 { + pub, err = keys.NewPublicKeyFromBytes(sig.Key(), elliptic.P256()) if err != nil { return fmt.Errorf("%w: %v", ErrInvalidPublicKey, err) } } - return cfg.verifyFunc( + scheme := sig.Scheme() + if scheme == signature.Unspecified { + scheme = cfg.defaultScheme + } + if cfg.restrictScheme != signature.Unspecified && scheme != cfg.restrictScheme { + return fmt.Errorf("%w: unexpected signature scheme", ErrInvalidSignature) + } + + return verify( + scheme, (*ecdsa.PublicKey)(pub), data, - sig, + sig.Sign(), ) } 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 c1cefeed..9b6b0e73 100644 --- a/util/signature/options.go +++ b/util/signature/options.go @@ -9,38 +9,65 @@ import ( "math/big" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" + "github.com/nspcc-dev/neofs-sdk-go/signature" ) var curve = elliptic.P256() type cfg struct { - signFunc func(key *ecdsa.PrivateKey, msg []byte) ([]byte, error) - verifyFunc func(key *ecdsa.PublicKey, msg []byte, sig []byte) error + defaultScheme signature.Scheme + restrictScheme signature.Scheme } -func defaultCfg() *cfg { - return &cfg{ - signFunc: sign, - verifyFunc: verify, +func getConfig(opts ...SignOption) *cfg { + cfg := &cfg{ + defaultScheme: signature.ECDSAWithSHA512, + restrictScheme: signature.Unspecified, + } + + for i := range opts { + opts[i](cfg) + } + + return cfg +} + +func sign(scheme signature.Scheme, key *ecdsa.PrivateKey, msg []byte) ([]byte, error) { + switch scheme { + case signature.ECDSAWithSHA512: + h := sha512.Sum512(msg) + x, y, err := ecdsa.Sign(rand.Reader, key, h[:]) + if err != nil { + return nil, err + } + return elliptic.Marshal(elliptic.P256(), x, y), nil + case signature.RFC6979WithSHA256: + p := &keys.PrivateKey{PrivateKey: *key} + return p.Sign(msg), nil + default: + panic("unsupported scheme") } } -func sign(key *ecdsa.PrivateKey, msg []byte) ([]byte, error) { - h := sha512.Sum512(msg) - x, y, err := ecdsa.Sign(rand.Reader, key, h[:]) - if err != nil { - return nil, err +func verify(scheme signature.Scheme, key *ecdsa.PublicKey, msg []byte, sig []byte) error { + switch scheme { + case signature.ECDSAWithSHA512: + h := sha512.Sum512(msg) + r, s := unmarshalXY(sig) + if r != nil && s != nil && ecdsa.Verify(key, h[:], r, s) { + return nil + } + return ErrInvalidSignature + case signature.RFC6979WithSHA256: + p := (*keys.PublicKey)(key) + h := sha256.Sum256(msg) + if p.Verify(sig, h[:]) { + return nil + } + return ErrInvalidSignature + default: + return ErrInvalidSignature } - return elliptic.Marshal(elliptic.P256(), x, y), nil -} - -func verify(key *ecdsa.PublicKey, msg []byte, sig []byte) error { - h := sha512.Sum512(msg) - r, s := unmarshalXY(sig) - if r != nil && s != nil && ecdsa.Verify(key, h[:], r, s) { - return nil - } - return ErrInvalidSignature } // unmarshalXY converts a point, serialized by Marshal, into an x, y pair. @@ -71,21 +98,7 @@ func unmarshalXY(data []byte) (x *big.Int, y *big.Int) { func SignWithRFC6979() SignOption { return func(c *cfg) { - c.signFunc = signRFC6979 - c.verifyFunc = verifyRFC6979 + c.defaultScheme = signature.RFC6979WithSHA256 + c.restrictScheme = signature.RFC6979WithSHA256 } } - -func signRFC6979(key *ecdsa.PrivateKey, msg []byte) ([]byte, error) { - p := &keys.PrivateKey{PrivateKey: *key} - return p.Sign(msg), nil -} - -func verifyRFC6979(key *ecdsa.PublicKey, msg []byte, sig []byte) error { - p := (*keys.PublicKey)(key) - h := sha256.Sum256(msg) - if p.Verify(sig, h[:]) { - return nil - } - return ErrInvalidSignature -}