diff --git a/pkg/crypto/keys/private_key.go b/pkg/crypto/keys/private_key.go index 0c2dc7d5b..02f23256b 100644 --- a/pkg/crypto/keys/private_key.go +++ b/pkg/crypto/keys/private_key.go @@ -14,21 +14,31 @@ import ( "github.com/nspcc-dev/rfc6979" ) -// PrivateKey represents a NEO private key. +// PrivateKey represents a NEO private key and provides a high level API around +// ecdsa.PrivateKey. type PrivateKey struct { - b []byte + ecdsa.PrivateKey } -// NewPrivateKey creates a new random private key. +// NewPrivateKey creates a new random Secp256k1 private key. func NewPrivateKey() (*PrivateKey, error) { - priv, _, _, err := elliptic.GenerateKey(elliptic.P256(), rand.Reader) + priv, x, y, err := elliptic.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { return nil, err } - return &PrivateKey{b: priv}, nil + return &PrivateKey{ + ecdsa.PrivateKey{ + PublicKey: ecdsa.PublicKey{ + Curve: elliptic.P256(), + X: x, + Y: y, + }, + D: new(big.Int).SetBytes(priv), + }, + }, nil } -// NewPrivateKeyFromHex returns a PrivateKey created from the +// NewPrivateKeyFromHex returns a Secp256k1 PrivateKey created from the // given hex string. func NewPrivateKeyFromHex(str string) (*PrivateKey, error) { b, err := hex.DecodeString(str) @@ -38,17 +48,35 @@ func NewPrivateKeyFromHex(str string) (*PrivateKey, error) { return NewPrivateKeyFromBytes(b) } -// NewPrivateKeyFromBytes returns a NEO PrivateKey from the given byte slice. +// NewPrivateKeyFromBytes returns a NEO Secp256r1 PrivateKey from the given +// byte slice. func NewPrivateKeyFromBytes(b []byte) (*PrivateKey, error) { if len(b) != 32 { return nil, fmt.Errorf( "invalid byte length: expected %d bytes got %d", 32, len(b), ) } - return &PrivateKey{b}, nil + var ( + c = elliptic.P256() + d = new(big.Int).SetBytes(b) + ) + + x, y := c.ScalarBaseMult(d.Bytes()) + + return &PrivateKey{ + ecdsa.PrivateKey{ + PublicKey: ecdsa.PublicKey{ + Curve: c, + X: x, + Y: y, + }, + D: d, + }, + }, nil } -// NewPrivateKeyFromASN1 returns a NEO PrivateKey from the ASN.1 serialized key. +// NewPrivateKeyFromASN1 returns a NEO Secp256k1 PrivateKey from the ASN.1 +// serialized key. func NewPrivateKeyFromASN1(b []byte) (*PrivateKey, error) { privkey, err := x509.ParseECPrivateKey(b) if err != nil { @@ -59,14 +87,8 @@ func NewPrivateKeyFromASN1(b []byte) (*PrivateKey, error) { // PublicKey derives the public key from the private key. func (p *PrivateKey) PublicKey() *PublicKey { - var ( - c = elliptic.P256() - q = new(big.Int).SetBytes(p.b) - ) - - x, y := c.ScalarBaseMult(q.Bytes()) - - return &PublicKey{X: x, Y: y} + result := PublicKey(p.PrivateKey.PublicKey) + return &result } // NewPrivateKeyFromWIF returns a NEO PrivateKey from the given @@ -83,7 +105,7 @@ func NewPrivateKeyFromWIF(wif string) (*PrivateKey, error) { // Good documentation about this process can be found here: // https://en.bitcoin.it/wiki/Wallet_import_format func (p *PrivateKey) WIF() string { - w, err := WIFEncode(p.b, WIFVersion, true) + w, err := WIFEncode(p.Bytes(), WIFVersion, true) // The only way WIFEncode() can fail is if we're to give it a key of // wrong size, but we have a proper key here, aren't we? if err != nil { @@ -109,7 +131,7 @@ func (p *PrivateKey) GetScriptHash() util.Uint160 { // Sign signs arbitrary length data using the private key. func (p *PrivateKey) Sign(data []byte) []byte { var ( - privateKey = p.ecdsa() + privateKey = &p.PrivateKey digest = sha256.Sum256(data) ) @@ -125,21 +147,16 @@ func (p *PrivateKey) Sign(data []byte) []byte { return signature } -// ecsda converts the key to a usable ecsda.PrivateKey for signing data. -func (p *PrivateKey) ecdsa() *ecdsa.PrivateKey { - priv := new(ecdsa.PrivateKey) - priv.PublicKey.Curve = elliptic.P256() - priv.D = new(big.Int).SetBytes(p.b) - priv.PublicKey.X, priv.PublicKey.Y = priv.PublicKey.Curve.ScalarBaseMult(p.b) - return priv -} - // String implements the stringer interface. func (p *PrivateKey) String() string { - return hex.EncodeToString(p.b) + return hex.EncodeToString(p.Bytes()) } // Bytes returns the underlying bytes of the PrivateKey. func (p *PrivateKey) Bytes() []byte { - return p.b + bytes := p.D.Bytes() + result := make([]byte, 32) + copy(result[32-len(bytes):], bytes) + + return result } diff --git a/pkg/crypto/keys/publickey.go b/pkg/crypto/keys/publickey.go index a6d905826..0cde04a45 100644 --- a/pkg/crypto/keys/publickey.go +++ b/pkg/crypto/keys/publickey.go @@ -76,11 +76,8 @@ func (keys PublicKeys) Unique() PublicKeys { } // PublicKey represents a public key and provides a high level -// API around the X/Y point. -type PublicKey struct { - X *big.Int - Y *big.Int -} +// API around ecdsa.PublicKey. +type PublicKey ecdsa.PublicKey // Equal returns true in case public keys are equal. func (p *PublicKey) Equal(key *PublicKey) bool { @@ -173,16 +170,13 @@ func NewPublicKeyFromASN1(data []byte) (*PublicKey, error) { if !ok { return nil, errors.New("given bytes aren't ECDSA public key") } - key := PublicKey{ - X: pk.X, - Y: pk.Y, - } - return &key, nil + result := PublicKey(*pk) + return &result, nil } // decodeCompressedY performs decompression of Y coordinate for given X and Y's least significant bit. -func decodeCompressedY(x *big.Int, ylsb uint) (*big.Int, error) { - c := elliptic.P256() +func decodeCompressedY(x *big.Int, ylsb uint, curve elliptic.Curve) (*big.Int, error) { + c := curve cp := c.Params() three := big.NewInt(3) /* y**2 = x**3 + a*x + b % p */ @@ -210,7 +204,8 @@ func (p *PublicKey) DecodeBytes(data []byte) error { return b.Err } -// DecodeBinary decodes a PublicKey from the given BinReader. +// DecodeBinary decodes a PublicKey from the given BinReader using information +// about the EC curve to decompress Y point. Secp256r1 is a default value for EC curve. func (p *PublicKey) DecodeBinary(r *io.BinReader) { var prefix uint8 var x, y *big.Int @@ -221,8 +216,11 @@ func (p *PublicKey) DecodeBinary(r *io.BinReader) { return } - p256 := elliptic.P256() - p256Params := p256.Params() + if p.Curve == nil { + p.Curve = elliptic.P256() + } + curve := p.Curve + curveParams := p.Params() // Infinity switch prefix { case 0x00: @@ -237,7 +235,7 @@ func (p *PublicKey) DecodeBinary(r *io.BinReader) { } x = new(big.Int).SetBytes(xbytes) ylsb := uint(prefix & 0x1) - y, err = decodeCompressedY(x, ylsb) + y, err = decodeCompressedY(x, ylsb, curve) if err != nil { r.Err = err return @@ -252,7 +250,7 @@ func (p *PublicKey) DecodeBinary(r *io.BinReader) { } x = new(big.Int).SetBytes(xbytes) y = new(big.Int).SetBytes(ybytes) - if !p256.IsOnCurve(x, y) { + if !curve.IsOnCurve(x, y) { r.Err = errors.New("encoded point is not on the P256 curve") return } @@ -260,7 +258,7 @@ func (p *PublicKey) DecodeBinary(r *io.BinReader) { r.Err = errors.Errorf("invalid prefix %d", prefix) return } - if x.Cmp(p256Params.P) >= 0 || y.Cmp(p256Params.P) >= 0 { + if x.Cmp(curveParams.P) >= 0 || y.Cmp(curveParams.P) >= 0 { r.Err = errors.New("enccoded point is not correct (X or Y is bigger than P") return } @@ -303,17 +301,13 @@ func (p *PublicKey) Address() string { // Verify returns true if the signature is valid and corresponds // to the hash and public key. func (p *PublicKey) Verify(signature []byte, hash []byte) bool { - - publicKey := &ecdsa.PublicKey{} - publicKey.Curve = elliptic.P256() - publicKey.X = p.X - publicKey.Y = p.Y if p.X == nil || p.Y == nil { return false } rBytes := new(big.Int).SetBytes(signature[0:32]) sBytes := new(big.Int).SetBytes(signature[32:64]) - return ecdsa.Verify(publicKey, hash, rBytes, sBytes) + pk := ecdsa.PublicKey(*p) + return ecdsa.Verify(&pk, hash, rBytes, sBytes) } // IsInfinity checks if the key is infinite (null, basically).