mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-11-29 13:41:47 +00:00
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:
parent
4c23aa1d7c
commit
8f17c7fb05
2 changed files with 65 additions and 54 deletions
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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).
|
||||||
|
|
Loading…
Reference in a new issue