forked from TrueCloudLab/frostfs-sdk-go
1c7dd03cf5
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>
122 lines
3 KiB
Go
122 lines
3 KiB
Go
package signature
|
|
|
|
import (
|
|
"crypto/ecdsa"
|
|
"crypto/elliptic"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
|
"github.com/nspcc-dev/neofs-sdk-go/signature"
|
|
)
|
|
|
|
type DataSource interface {
|
|
ReadSignedData([]byte) ([]byte, error)
|
|
SignedDataSize() int
|
|
}
|
|
|
|
type DataWithSignature interface {
|
|
DataSource
|
|
GetSignature() *signature.Signature
|
|
SetSignature(*signature.Signature)
|
|
}
|
|
|
|
type SignOption func(*cfg)
|
|
|
|
type KeySignatureHandler func(*signature.Signature)
|
|
|
|
type KeySignatureSource func() *signature.Signature
|
|
|
|
const (
|
|
// PrivateKeyCompressedSize is constant with compressed size of private key (SK).
|
|
// D coordinate stored, recover PK by formula x, y = curve.ScalarBaseMul(d,bytes).
|
|
PrivateKeyCompressedSize = 32
|
|
|
|
// PublicKeyCompressedSize is constant with compressed size of public key (PK).
|
|
PublicKeyCompressedSize = 33
|
|
|
|
// PublicKeyUncompressedSize is constant with uncompressed size of public key (PK).
|
|
// First byte always should be 0x4 other 64 bytes is X and Y (32 bytes per coordinate).
|
|
// 2 * 32 + 1.
|
|
PublicKeyUncompressedSize = 65
|
|
)
|
|
|
|
var (
|
|
// ErrEmptyPrivateKey is returned when used private key is empty.
|
|
ErrEmptyPrivateKey = errors.New("empty private key")
|
|
// ErrInvalidPublicKey is returned when public key cannot be unmarshalled.
|
|
ErrInvalidPublicKey = errors.New("invalid public key")
|
|
// ErrInvalidSignature is returned if signature cannot be verified.
|
|
ErrInvalidSignature = errors.New("invalid signature")
|
|
)
|
|
|
|
func SignDataWithHandler(key *ecdsa.PrivateKey, src DataSource, handler KeySignatureHandler, opts ...SignOption) error {
|
|
if key == nil {
|
|
return ErrEmptyPrivateKey
|
|
}
|
|
|
|
data, err := dataForSignature(src)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer bytesPool.Put(&data)
|
|
|
|
cfg := getConfig(opts...)
|
|
|
|
sigData, err := sign(cfg.defaultScheme, key, data)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
sig := signature.New()
|
|
sig.SetKey((*keys.PublicKey)(&key.PublicKey).Bytes())
|
|
sig.SetSign(sigData)
|
|
sig.SetScheme(cfg.defaultScheme)
|
|
|
|
handler(sig)
|
|
|
|
return nil
|
|
}
|
|
|
|
func VerifyDataWithSource(dataSrc DataSource, sigSrc KeySignatureSource, opts ...SignOption) error {
|
|
data, err := dataForSignature(dataSrc)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer bytesPool.Put(&data)
|
|
|
|
cfg := getConfig(opts...)
|
|
|
|
sig := sigSrc()
|
|
|
|
var pub *keys.PublicKey
|
|
if len(sig.Key()) != 0 {
|
|
pub, err = keys.NewPublicKeyFromBytes(sig.Key(), elliptic.P256())
|
|
if err != nil {
|
|
return fmt.Errorf("%w: %v", ErrInvalidPublicKey, err)
|
|
}
|
|
}
|
|
|
|
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.Sign(),
|
|
)
|
|
}
|
|
|
|
func SignData(key *ecdsa.PrivateKey, v DataWithSignature, opts ...SignOption) error {
|
|
return SignDataWithHandler(key, v, v.SetSignature, opts...)
|
|
}
|
|
|
|
func VerifyData(src DataWithSignature, opts ...SignOption) error {
|
|
return VerifyDataWithSource(src, src.GetSignature, opts...)
|
|
}
|