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"
)
// 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
}

View file

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