diff --git a/crypto/crypto_test.go b/crypto/crypto_test.go index 5d4ab9d0..57595172 100644 --- a/crypto/crypto_test.go +++ b/crypto/crypto_test.go @@ -28,6 +28,9 @@ func TestSignature(t *testing.T) { func() neofscrypto.Signer { return neofsecdsa.SignerRFC6979(k.PrivateKey) }, + func() neofscrypto.Signer { + return neofsecdsa.SignerWalletConnect(k.PrivateKey) + }, } { signer := f() @@ -39,6 +42,6 @@ func TestSignature(t *testing.T) { s.ReadFromV2(m) valid := s.Verify(data) - require.True(t, valid) + require.True(t, valid, "type %T", signer) } } diff --git a/crypto/ecdsa/init.go b/crypto/ecdsa/init.go index 7ed440ab..f0e5c252 100644 --- a/crypto/ecdsa/init.go +++ b/crypto/ecdsa/init.go @@ -10,4 +10,8 @@ func init() { neofscrypto.RegisterScheme(neofscrypto.ECDSA_DETERMINISTIC_SHA256, func() neofscrypto.PublicKey { return new(PublicKeyRFC6979) }) + + neofscrypto.RegisterScheme(neofscrypto.ECDSA_WALLETCONNECT, func() neofscrypto.PublicKey { + return new(PublicKeyWalletConnect) + }) } diff --git a/crypto/ecdsa/wallet_connect.go b/crypto/ecdsa/wallet_connect.go new file mode 100644 index 00000000..5c46f69a --- /dev/null +++ b/crypto/ecdsa/wallet_connect.go @@ -0,0 +1,85 @@ +package neofsecdsa + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "encoding/base64" + "fmt" + + "github.com/nspcc-dev/neo-go/pkg/crypto/keys" + "github.com/nspcc-dev/neofs-api-go/v2/util/signature/walletconnect" + neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto" +) + +// SignerWalletConnect is similar to SignerRFC6979 with 2 changes: +// 1. The data is base64 encoded before signing/verifying. +// 2. The signature is a concatenation of the signature itself and 16-byte salt. +// +// Instances MUST be initialized from ecdsa.PrivateKey using type conversion. +type SignerWalletConnect ecdsa.PrivateKey + +// Scheme returns neofscrypto.ECDSA_WALLETCONNECT. +// Implements neofscrypto.Signer. +func (x SignerWalletConnect) Scheme() neofscrypto.Scheme { + return neofscrypto.ECDSA_WALLETCONNECT +} + +// Sign signs data using ECDSA algorithm with SHA-512 hashing. +// Implements neofscrypto.Signer. +func (x SignerWalletConnect) Sign(data []byte) ([]byte, error) { + b64 := make([]byte, base64.StdEncoding.EncodedLen(len(data))) + base64.StdEncoding.Encode(b64, data) + return walletconnect.Sign((*ecdsa.PrivateKey)(&x), b64) +} + +// Public initializes PublicKey and returns it as neofscrypto.PublicKey. +// Implements neofscrypto.Signer. +func (x SignerWalletConnect) Public() neofscrypto.PublicKey { + return (*PublicKeyWalletConnect)(&x.PublicKey) +} + +// PublicKeyWalletConnect is a wrapper over ecdsa.PublicKey used for NeoFS needs. +// Provides neofscrypto.PublicKey interface. +// +// Instances MUST be initialized from ecdsa.PublicKey using type conversion. +type PublicKeyWalletConnect ecdsa.PublicKey + +// MaxEncodedSize returns size of the compressed ECDSA public key. +func (x PublicKeyWalletConnect) MaxEncodedSize() int { + return 33 +} + +// Encode encodes ECDSA public key in compressed form into buf. +// Uses exactly MaxEncodedSize bytes of the buf. +// +// Encode panics if buf length is less than MaxEncodedSize. +// +// See also Decode. +func (x PublicKeyWalletConnect) Encode(buf []byte) int { + if len(buf) < 33 { + panic(fmt.Sprintf("too short buffer %d", len(buf))) + } + + return copy(buf, (*keys.PublicKey)(&x).Bytes()) +} + +// Decode decodes compressed binary representation of the PublicKeyWalletConnect. +// +// See also Encode. +func (x *PublicKeyWalletConnect) Decode(data []byte) error { + pub, err := keys.NewPublicKeyFromBytes(data, elliptic.P256()) + if err != nil { + return err + } + + *x = (PublicKeyWalletConnect)(*pub) + + return nil +} + +// Verify verifies data signature calculated by ECDSA algorithm with SHA-512 hashing. +func (x PublicKeyWalletConnect) Verify(data, signature []byte) bool { + b64 := make([]byte, base64.StdEncoding.EncodedLen(len(data))) + base64.StdEncoding.Encode(b64, data) + return walletconnect.Verify((*ecdsa.PublicKey)(&x), b64, signature) +} diff --git a/crypto/signer.go b/crypto/signer.go index 7ca2faca..b1e5388a 100644 --- a/crypto/signer.go +++ b/crypto/signer.go @@ -17,6 +17,7 @@ const ( ECDSA_SHA512 // ECDSA with SHA-512 hashing (FIPS 186-3) ECDSA_DETERMINISTIC_SHA256 // Deterministic ECDSA with SHA-256 hashing (RFC 6979) + ECDSA_WALLETCONNECT // Wallet Connect signature scheme ) // String implements fmt.Stringer. diff --git a/go.mod b/go.mod index e6e5474a..6bb7a8fa 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/mr-tron/base58 v1.2.0 github.com/nspcc-dev/hrw v1.0.9 github.com/nspcc-dev/neo-go v0.98.2 - github.com/nspcc-dev/neofs-api-go/v2 v2.12.2 + github.com/nspcc-dev/neofs-api-go/v2 v2.12.3-0.20220621170933-dd233c3fbc84 github.com/nspcc-dev/neofs-contract v0.15.1 github.com/nspcc-dev/tzhash v1.5.2 github.com/stretchr/testify v1.7.0 diff --git a/go.sum b/go.sum index d65d9a23..9d5e5838 100644 --- a/go.sum +++ b/go.sum @@ -182,8 +182,8 @@ github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20220321113211-526c423a6152 h1:JK github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20220321113211-526c423a6152/go.mod h1:QBE0I30F2kOAISNpT5oks82yF4wkkUq3SCfI3Hqgx/Y= github.com/nspcc-dev/neofs-api-go/v2 v2.11.0-pre.0.20211201134523-3604d96f3fe1/go.mod h1:oS8dycEh8PPf2Jjp6+8dlwWyEv2Dy77h/XhhcdxYEFs= github.com/nspcc-dev/neofs-api-go/v2 v2.11.1/go.mod h1:oS8dycEh8PPf2Jjp6+8dlwWyEv2Dy77h/XhhcdxYEFs= -github.com/nspcc-dev/neofs-api-go/v2 v2.12.2 h1:ifV/c0bW1TPiEKZlNqhfZl8lzX0f6FokjYUaze/hlBk= -github.com/nspcc-dev/neofs-api-go/v2 v2.12.2/go.mod h1:73j09Xa7I2zQbM3HCvAHnDHPYiiWnEHa1d6Z6RDMBLU= +github.com/nspcc-dev/neofs-api-go/v2 v2.12.3-0.20220621170933-dd233c3fbc84 h1:4tZSQ2DL/oatbte35+vDSt2nYpQ0G2et1DrpxodGwRM= +github.com/nspcc-dev/neofs-api-go/v2 v2.12.3-0.20220621170933-dd233c3fbc84/go.mod h1:73j09Xa7I2zQbM3HCvAHnDHPYiiWnEHa1d6Z6RDMBLU= github.com/nspcc-dev/neofs-contract v0.15.1 h1:1r27t4SGKF7W1PRPOIfircEXHvALThNYNagT+SIabcA= github.com/nspcc-dev/neofs-contract v0.15.1/go.mod h1:kxO5ZTqdzFnRM5RMvM+Fhd+3GGrJo6AmG2ZyA9OCqqQ= github.com/nspcc-dev/neofs-crypto v0.2.0/go.mod h1:F/96fUzPM3wR+UGsPi3faVNmFlA9KAEAUQR7dMxZmNA=