crypto: switch to standard ecdsa keys

Now we have not only Random EC curve, but also Koblitz curve, so
it will be useful to have information about the curve for each
particular EC point. ecdsa.PublicKey has this information.
This commit is contained in:
Anna Shaleva 2020-07-14 08:40:39 +03:00
parent 4c23aa1d7c
commit 8f17c7fb05
2 changed files with 65 additions and 54 deletions

View file

@ -14,21 +14,31 @@ import (
"github.com/nspcc-dev/rfc6979" "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 { 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) { 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 { if err != nil {
return nil, err 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. // given hex string.
func NewPrivateKeyFromHex(str string) (*PrivateKey, error) { func NewPrivateKeyFromHex(str string) (*PrivateKey, error) {
b, err := hex.DecodeString(str) b, err := hex.DecodeString(str)
@ -38,17 +48,35 @@ func NewPrivateKeyFromHex(str string) (*PrivateKey, error) {
return NewPrivateKeyFromBytes(b) 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) { func NewPrivateKeyFromBytes(b []byte) (*PrivateKey, error) {
if len(b) != 32 { if len(b) != 32 {
return nil, fmt.Errorf( return nil, fmt.Errorf(
"invalid byte length: expected %d bytes got %d", 32, len(b), "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) { func NewPrivateKeyFromASN1(b []byte) (*PrivateKey, error) {
privkey, err := x509.ParseECPrivateKey(b) privkey, err := x509.ParseECPrivateKey(b)
if err != nil { if err != nil {
@ -59,14 +87,8 @@ func NewPrivateKeyFromASN1(b []byte) (*PrivateKey, error) {
// PublicKey derives the public key from the private key. // PublicKey derives the public key from the private key.
func (p *PrivateKey) PublicKey() *PublicKey { func (p *PrivateKey) PublicKey() *PublicKey {
var ( result := PublicKey(p.PrivateKey.PublicKey)
c = elliptic.P256() return &result
q = new(big.Int).SetBytes(p.b)
)
x, y := c.ScalarBaseMult(q.Bytes())
return &PublicKey{X: x, Y: y}
} }
// NewPrivateKeyFromWIF returns a NEO PrivateKey from the given // 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: // Good documentation about this process can be found here:
// https://en.bitcoin.it/wiki/Wallet_import_format // https://en.bitcoin.it/wiki/Wallet_import_format
func (p *PrivateKey) WIF() string { 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 // 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? // wrong size, but we have a proper key here, aren't we?
if err != nil { if err != nil {
@ -109,7 +131,7 @@ func (p *PrivateKey) GetScriptHash() util.Uint160 {
// Sign signs arbitrary length data using the private key. // Sign signs arbitrary length data using the private key.
func (p *PrivateKey) Sign(data []byte) []byte { func (p *PrivateKey) Sign(data []byte) []byte {
var ( var (
privateKey = p.ecdsa() privateKey = &p.PrivateKey
digest = sha256.Sum256(data) digest = sha256.Sum256(data)
) )
@ -125,21 +147,16 @@ func (p *PrivateKey) Sign(data []byte) []byte {
return signature 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. // String implements the stringer interface.
func (p *PrivateKey) String() string { func (p *PrivateKey) String() string {
return hex.EncodeToString(p.b) return hex.EncodeToString(p.Bytes())
} }
// Bytes returns the underlying bytes of the PrivateKey. // Bytes returns the underlying bytes of the PrivateKey.
func (p *PrivateKey) Bytes() []byte { func (p *PrivateKey) Bytes() []byte {
return p.b bytes := p.D.Bytes()
result := make([]byte, 32)
copy(result[32-len(bytes):], bytes)
return result
} }

View file

@ -76,11 +76,8 @@ func (keys PublicKeys) Unique() PublicKeys {
} }
// PublicKey represents a public key and provides a high level // PublicKey represents a public key and provides a high level
// API around the X/Y point. // API around ecdsa.PublicKey.
type PublicKey struct { type PublicKey ecdsa.PublicKey
X *big.Int
Y *big.Int
}
// Equal returns true in case public keys are equal. // Equal returns true in case public keys are equal.
func (p *PublicKey) Equal(key *PublicKey) bool { func (p *PublicKey) Equal(key *PublicKey) bool {
@ -173,16 +170,13 @@ func NewPublicKeyFromASN1(data []byte) (*PublicKey, error) {
if !ok { if !ok {
return nil, errors.New("given bytes aren't ECDSA public key") return nil, errors.New("given bytes aren't ECDSA public key")
} }
key := PublicKey{ result := PublicKey(*pk)
X: pk.X, return &result, nil
Y: pk.Y,
}
return &key, nil
} }
// decodeCompressedY performs decompression of Y coordinate for given X and Y's least significant bit. // 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) { func decodeCompressedY(x *big.Int, ylsb uint, curve elliptic.Curve) (*big.Int, error) {
c := elliptic.P256() c := curve
cp := c.Params() cp := c.Params()
three := big.NewInt(3) three := big.NewInt(3)
/* y**2 = x**3 + a*x + b % p */ /* y**2 = x**3 + a*x + b % p */
@ -210,7 +204,8 @@ func (p *PublicKey) DecodeBytes(data []byte) error {
return b.Err 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) { func (p *PublicKey) DecodeBinary(r *io.BinReader) {
var prefix uint8 var prefix uint8
var x, y *big.Int var x, y *big.Int
@ -221,8 +216,11 @@ func (p *PublicKey) DecodeBinary(r *io.BinReader) {
return return
} }
p256 := elliptic.P256() if p.Curve == nil {
p256Params := p256.Params() p.Curve = elliptic.P256()
}
curve := p.Curve
curveParams := p.Params()
// Infinity // Infinity
switch prefix { switch prefix {
case 0x00: case 0x00:
@ -237,7 +235,7 @@ func (p *PublicKey) DecodeBinary(r *io.BinReader) {
} }
x = new(big.Int).SetBytes(xbytes) x = new(big.Int).SetBytes(xbytes)
ylsb := uint(prefix & 0x1) ylsb := uint(prefix & 0x1)
y, err = decodeCompressedY(x, ylsb) y, err = decodeCompressedY(x, ylsb, curve)
if err != nil { if err != nil {
r.Err = err r.Err = err
return return
@ -252,7 +250,7 @@ func (p *PublicKey) DecodeBinary(r *io.BinReader) {
} }
x = new(big.Int).SetBytes(xbytes) x = new(big.Int).SetBytes(xbytes)
y = new(big.Int).SetBytes(ybytes) 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") r.Err = errors.New("encoded point is not on the P256 curve")
return return
} }
@ -260,7 +258,7 @@ func (p *PublicKey) DecodeBinary(r *io.BinReader) {
r.Err = errors.Errorf("invalid prefix %d", prefix) r.Err = errors.Errorf("invalid prefix %d", prefix)
return 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") r.Err = errors.New("enccoded point is not correct (X or Y is bigger than P")
return return
} }
@ -303,17 +301,13 @@ func (p *PublicKey) Address() string {
// Verify returns true if the signature is valid and corresponds // Verify returns true if the signature is valid and corresponds
// to the hash and public key. // to the hash and public key.
func (p *PublicKey) Verify(signature []byte, hash []byte) bool { 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 { if p.X == nil || p.Y == nil {
return false return false
} }
rBytes := new(big.Int).SetBytes(signature[0:32]) rBytes := new(big.Int).SetBytes(signature[0:32])
sBytes := new(big.Int).SetBytes(signature[32:64]) 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). // IsInfinity checks if the key is infinite (null, basically).