[#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 <evgeniy@nspcc.ru>
This commit is contained in:
Evgenii Stratonikov 2022-02-24 14:05:05 +03:00 committed by LeL
parent 4fba1af6aa
commit 1c7dd03cf5
8 changed files with 129 additions and 131 deletions

View file

@ -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

View file

@ -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,
)
}

View file

@ -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)
},
)
}

View file

@ -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.

View file

@ -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.

View file

@ -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)
})
}

View file

@ -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...)
}

View file

@ -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,
}
func sign(key *ecdsa.PrivateKey, msg []byte) ([]byte, error) {
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 verify(key *ecdsa.PublicKey, msg []byte, sig []byte) error {
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
}
}
// 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
}