2015-05-01 08:28:01 +02:00
|
|
|
// Copyright (c) 2013-2014 The btcsuite developers
|
2013-06-13 20:27:23 +02:00
|
|
|
// Use of this source code is governed by an ISC
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
package btcec
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/ecdsa"
|
2014-03-17 18:47:27 +01:00
|
|
|
"errors"
|
2013-06-13 20:27:23 +02:00
|
|
|
"fmt"
|
|
|
|
"math/big"
|
|
|
|
)
|
|
|
|
|
Optimize public key serialization.
This change removes the internal pad function in favor a more opimized
paddedAppend function. Unlike pad, which would always alloate a new
slice of the desired size and copy the bytes into it, paddedAppend
only appends the leading padding when necesary, and uses the builtin
append to copy the remaining source bytes. pad was also used in
combination with another call to the builtin copy func to copy into a
zeroed byte slice. As the slice is now created using make with an
initial length of zero, this copy can also be removed.
As confirmed by poking the bytes with the unsafe package, gc does not
zero array elements between the len and cap when allocating slices
with make(). In combination with the paddedAppend func, this results
in only a single copy of each byte, with no unnecssary zeroing, when
creating the serialized pubkeys. This has not been tested with other
Go compilers (namely, gccgo and llgo), but the new behavior is still
functionally correct regardless of compiler optimizations.
The TestPad function has been removed as the pad func it tested has
likewise been removed.
ok @davecgh
2014-05-20 04:24:48 +02:00
|
|
|
// These constants define the lengths of serialized public keys.
|
|
|
|
const (
|
|
|
|
PubKeyBytesLenCompressed = 33
|
|
|
|
PubKeyBytesLenUncompressed = 65
|
|
|
|
PubKeyBytesLenHybrid = 65
|
|
|
|
)
|
|
|
|
|
2013-06-13 20:27:23 +02:00
|
|
|
func isOdd(a *big.Int) bool {
|
|
|
|
return a.Bit(0) == 1
|
|
|
|
}
|
|
|
|
|
2019-05-15 07:46:59 +02:00
|
|
|
// decompressPoint decompresses a point on the secp256k1 curve given the X point and
|
2014-02-11 02:43:21 +01:00
|
|
|
// the solution to use.
|
2019-05-15 07:46:59 +02:00
|
|
|
func decompressPoint(curve *KoblitzCurve, bigX *big.Int, ybit bool) (*big.Int, error) {
|
|
|
|
var x fieldVal
|
|
|
|
x.SetByteSlice(bigX.Bytes())
|
2014-02-11 02:43:21 +01:00
|
|
|
|
2019-05-15 07:46:59 +02:00
|
|
|
// Compute x^3 + B mod p.
|
|
|
|
var x3 fieldVal
|
|
|
|
x3.SquareVal(&x).Mul(&x)
|
|
|
|
x3.Add(curve.fieldB).Normalize()
|
2014-02-11 02:43:21 +01:00
|
|
|
|
2018-05-30 02:06:32 +02:00
|
|
|
// Now calculate sqrt mod p of x^3 + B
|
2014-02-11 02:43:21 +01:00
|
|
|
// This code used to do a full sqrt based on tonelli/shanks,
|
|
|
|
// but this was replaced by the algorithms referenced in
|
|
|
|
// https://bitcointalk.org/index.php?topic=162805.msg1712294#msg1712294
|
2019-05-15 07:46:59 +02:00
|
|
|
var y fieldVal
|
|
|
|
y.SqrtVal(&x3)
|
|
|
|
if ybit != y.IsOdd() {
|
|
|
|
y.Negate(1)
|
2014-02-11 02:43:21 +01:00
|
|
|
}
|
2019-05-15 07:46:59 +02:00
|
|
|
y.Normalize()
|
2018-05-30 02:06:32 +02:00
|
|
|
|
|
|
|
// Check that y is a square root of x^3 + B.
|
2019-05-15 07:46:59 +02:00
|
|
|
var y2 fieldVal
|
|
|
|
y2.SquareVal(&y).Normalize()
|
|
|
|
if !y2.Equals(&x3) {
|
2018-05-30 02:06:32 +02:00
|
|
|
return nil, fmt.Errorf("invalid square root")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify that y-coord has expected parity.
|
2019-05-15 07:46:59 +02:00
|
|
|
if ybit != y.IsOdd() {
|
2014-02-11 02:43:21 +01:00
|
|
|
return nil, fmt.Errorf("ybit doesn't match oddness")
|
|
|
|
}
|
2018-05-30 02:06:32 +02:00
|
|
|
|
2019-05-15 07:46:59 +02:00
|
|
|
return new(big.Int).SetBytes(y.Bytes()[:]), nil
|
2014-02-11 02:43:21 +01:00
|
|
|
}
|
|
|
|
|
2013-06-13 20:27:23 +02:00
|
|
|
const (
|
|
|
|
pubkeyCompressed byte = 0x2 // y_bit + x coord
|
2013-09-11 05:14:30 +02:00
|
|
|
pubkeyUncompressed byte = 0x4 // x coord + y coord
|
|
|
|
pubkeyHybrid byte = 0x6 // y_bit + x coord + y coord
|
2013-06-13 20:27:23 +02:00
|
|
|
)
|
|
|
|
|
2017-04-27 01:21:40 +02:00
|
|
|
// IsCompressedPubKey returns true the the passed serialized public key has
|
|
|
|
// been encoded in compressed format, and false otherwise.
|
|
|
|
func IsCompressedPubKey(pubKey []byte) bool {
|
|
|
|
// The public key is only compressed if it is the correct length and
|
|
|
|
// the format (first byte) is one of the compressed pubkey values.
|
|
|
|
return len(pubKey) == PubKeyBytesLenCompressed &&
|
|
|
|
(pubKey[0]&^byte(0x1) == pubkeyCompressed)
|
|
|
|
}
|
|
|
|
|
2013-08-06 19:22:16 +02:00
|
|
|
// ParsePubKey parses a public key for a koblitz curve from a bytestring into a
|
|
|
|
// ecdsa.Publickey, verifying that it is valid. It supports compressed,
|
|
|
|
// uncompressed and hybrid signature formats.
|
2014-04-09 20:27:56 +02:00
|
|
|
func ParsePubKey(pubKeyStr []byte, curve *KoblitzCurve) (key *PublicKey, err error) {
|
|
|
|
pubkey := PublicKey{}
|
2013-06-13 20:27:23 +02:00
|
|
|
pubkey.Curve = curve
|
|
|
|
|
2014-03-17 18:47:27 +01:00
|
|
|
if len(pubKeyStr) == 0 {
|
|
|
|
return nil, errors.New("pubkey string is empty")
|
|
|
|
}
|
|
|
|
|
2013-06-13 20:27:23 +02:00
|
|
|
format := pubKeyStr[0]
|
|
|
|
ybit := (format & 0x1) == 0x1
|
|
|
|
format &= ^byte(0x1)
|
|
|
|
|
|
|
|
switch len(pubKeyStr) {
|
Optimize public key serialization.
This change removes the internal pad function in favor a more opimized
paddedAppend function. Unlike pad, which would always alloate a new
slice of the desired size and copy the bytes into it, paddedAppend
only appends the leading padding when necesary, and uses the builtin
append to copy the remaining source bytes. pad was also used in
combination with another call to the builtin copy func to copy into a
zeroed byte slice. As the slice is now created using make with an
initial length of zero, this copy can also be removed.
As confirmed by poking the bytes with the unsafe package, gc does not
zero array elements between the len and cap when allocating slices
with make(). In combination with the paddedAppend func, this results
in only a single copy of each byte, with no unnecssary zeroing, when
creating the serialized pubkeys. This has not been tested with other
Go compilers (namely, gccgo and llgo), but the new behavior is still
functionally correct regardless of compiler optimizations.
The TestPad function has been removed as the pad func it tested has
likewise been removed.
ok @davecgh
2014-05-20 04:24:48 +02:00
|
|
|
case PubKeyBytesLenUncompressed:
|
2013-06-13 20:27:23 +02:00
|
|
|
if format != pubkeyUncompressed && format != pubkeyHybrid {
|
|
|
|
return nil, fmt.Errorf("invalid magic in pubkey str: "+
|
|
|
|
"%d", pubKeyStr[0])
|
|
|
|
}
|
|
|
|
|
|
|
|
pubkey.X = new(big.Int).SetBytes(pubKeyStr[1:33])
|
|
|
|
pubkey.Y = new(big.Int).SetBytes(pubKeyStr[33:])
|
|
|
|
// hybrid keys have extra information, make use of it.
|
|
|
|
if format == pubkeyHybrid && ybit != isOdd(pubkey.Y) {
|
|
|
|
return nil, fmt.Errorf("ybit doesn't match oddness")
|
|
|
|
}
|
2019-05-15 07:46:54 +02:00
|
|
|
|
|
|
|
if pubkey.X.Cmp(pubkey.Curve.Params().P) >= 0 {
|
|
|
|
return nil, fmt.Errorf("pubkey X parameter is >= to P")
|
|
|
|
}
|
|
|
|
if pubkey.Y.Cmp(pubkey.Curve.Params().P) >= 0 {
|
|
|
|
return nil, fmt.Errorf("pubkey Y parameter is >= to P")
|
|
|
|
}
|
|
|
|
if !pubkey.Curve.IsOnCurve(pubkey.X, pubkey.Y) {
|
|
|
|
return nil, fmt.Errorf("pubkey isn't on secp256k1 curve")
|
|
|
|
}
|
|
|
|
|
Optimize public key serialization.
This change removes the internal pad function in favor a more opimized
paddedAppend function. Unlike pad, which would always alloate a new
slice of the desired size and copy the bytes into it, paddedAppend
only appends the leading padding when necesary, and uses the builtin
append to copy the remaining source bytes. pad was also used in
combination with another call to the builtin copy func to copy into a
zeroed byte slice. As the slice is now created using make with an
initial length of zero, this copy can also be removed.
As confirmed by poking the bytes with the unsafe package, gc does not
zero array elements between the len and cap when allocating slices
with make(). In combination with the paddedAppend func, this results
in only a single copy of each byte, with no unnecssary zeroing, when
creating the serialized pubkeys. This has not been tested with other
Go compilers (namely, gccgo and llgo), but the new behavior is still
functionally correct regardless of compiler optimizations.
The TestPad function has been removed as the pad func it tested has
likewise been removed.
ok @davecgh
2014-05-20 04:24:48 +02:00
|
|
|
case PubKeyBytesLenCompressed:
|
2013-06-13 20:27:23 +02:00
|
|
|
// format is 0x2 | solution, <X coordinate>
|
|
|
|
// solution determines which solution of the curve we use.
|
|
|
|
/// y^2 = x^3 + Curve.B
|
|
|
|
if format != pubkeyCompressed {
|
|
|
|
return nil, fmt.Errorf("invalid magic in compressed "+
|
|
|
|
"pubkey string: %d", pubKeyStr[0])
|
|
|
|
}
|
|
|
|
pubkey.X = new(big.Int).SetBytes(pubKeyStr[1:33])
|
2014-02-11 02:43:21 +01:00
|
|
|
pubkey.Y, err = decompressPoint(curve, pubkey.X, ybit)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2013-06-13 20:27:23 +02:00
|
|
|
}
|
2019-05-15 07:46:54 +02:00
|
|
|
|
2013-06-13 20:27:23 +02:00
|
|
|
default: // wrong!
|
|
|
|
return nil, fmt.Errorf("invalid pub key length %d",
|
|
|
|
len(pubKeyStr))
|
|
|
|
}
|
|
|
|
|
|
|
|
return &pubkey, nil
|
|
|
|
}
|
2013-09-11 05:14:30 +02:00
|
|
|
|
|
|
|
// PublicKey is an ecdsa.PublicKey with additional functions to
|
|
|
|
// serialize in uncompressed, compressed, and hybrid formats.
|
|
|
|
type PublicKey ecdsa.PublicKey
|
|
|
|
|
2014-04-09 20:27:56 +02:00
|
|
|
// ToECDSA returns the public key as a *ecdsa.PublicKey.
|
|
|
|
func (p *PublicKey) ToECDSA() *ecdsa.PublicKey {
|
|
|
|
return (*ecdsa.PublicKey)(p)
|
|
|
|
}
|
|
|
|
|
2013-09-11 05:14:30 +02:00
|
|
|
// SerializeUncompressed serializes a public key in a 65-byte uncompressed
|
|
|
|
// format.
|
|
|
|
func (p *PublicKey) SerializeUncompressed() []byte {
|
Optimize public key serialization.
This change removes the internal pad function in favor a more opimized
paddedAppend function. Unlike pad, which would always alloate a new
slice of the desired size and copy the bytes into it, paddedAppend
only appends the leading padding when necesary, and uses the builtin
append to copy the remaining source bytes. pad was also used in
combination with another call to the builtin copy func to copy into a
zeroed byte slice. As the slice is now created using make with an
initial length of zero, this copy can also be removed.
As confirmed by poking the bytes with the unsafe package, gc does not
zero array elements between the len and cap when allocating slices
with make(). In combination with the paddedAppend func, this results
in only a single copy of each byte, with no unnecssary zeroing, when
creating the serialized pubkeys. This has not been tested with other
Go compilers (namely, gccgo and llgo), but the new behavior is still
functionally correct regardless of compiler optimizations.
The TestPad function has been removed as the pad func it tested has
likewise been removed.
ok @davecgh
2014-05-20 04:24:48 +02:00
|
|
|
b := make([]byte, 0, PubKeyBytesLenUncompressed)
|
|
|
|
b = append(b, pubkeyUncompressed)
|
|
|
|
b = paddedAppend(32, b, p.X.Bytes())
|
|
|
|
return paddedAppend(32, b, p.Y.Bytes())
|
2013-09-11 05:14:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// SerializeCompressed serializes a public key in a 33-byte compressed format.
|
|
|
|
func (p *PublicKey) SerializeCompressed() []byte {
|
Optimize public key serialization.
This change removes the internal pad function in favor a more opimized
paddedAppend function. Unlike pad, which would always alloate a new
slice of the desired size and copy the bytes into it, paddedAppend
only appends the leading padding when necesary, and uses the builtin
append to copy the remaining source bytes. pad was also used in
combination with another call to the builtin copy func to copy into a
zeroed byte slice. As the slice is now created using make with an
initial length of zero, this copy can also be removed.
As confirmed by poking the bytes with the unsafe package, gc does not
zero array elements between the len and cap when allocating slices
with make(). In combination with the paddedAppend func, this results
in only a single copy of each byte, with no unnecssary zeroing, when
creating the serialized pubkeys. This has not been tested with other
Go compilers (namely, gccgo and llgo), but the new behavior is still
functionally correct regardless of compiler optimizations.
The TestPad function has been removed as the pad func it tested has
likewise been removed.
ok @davecgh
2014-05-20 04:24:48 +02:00
|
|
|
b := make([]byte, 0, PubKeyBytesLenCompressed)
|
2013-09-11 05:14:30 +02:00
|
|
|
format := pubkeyCompressed
|
|
|
|
if isOdd(p.Y) {
|
|
|
|
format |= 0x1
|
|
|
|
}
|
Optimize public key serialization.
This change removes the internal pad function in favor a more opimized
paddedAppend function. Unlike pad, which would always alloate a new
slice of the desired size and copy the bytes into it, paddedAppend
only appends the leading padding when necesary, and uses the builtin
append to copy the remaining source bytes. pad was also used in
combination with another call to the builtin copy func to copy into a
zeroed byte slice. As the slice is now created using make with an
initial length of zero, this copy can also be removed.
As confirmed by poking the bytes with the unsafe package, gc does not
zero array elements between the len and cap when allocating slices
with make(). In combination with the paddedAppend func, this results
in only a single copy of each byte, with no unnecssary zeroing, when
creating the serialized pubkeys. This has not been tested with other
Go compilers (namely, gccgo and llgo), but the new behavior is still
functionally correct regardless of compiler optimizations.
The TestPad function has been removed as the pad func it tested has
likewise been removed.
ok @davecgh
2014-05-20 04:24:48 +02:00
|
|
|
b = append(b, format)
|
|
|
|
return paddedAppend(32, b, p.X.Bytes())
|
2013-09-11 05:14:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// SerializeHybrid serializes a public key in a 65-byte hybrid format.
|
|
|
|
func (p *PublicKey) SerializeHybrid() []byte {
|
Optimize public key serialization.
This change removes the internal pad function in favor a more opimized
paddedAppend function. Unlike pad, which would always alloate a new
slice of the desired size and copy the bytes into it, paddedAppend
only appends the leading padding when necesary, and uses the builtin
append to copy the remaining source bytes. pad was also used in
combination with another call to the builtin copy func to copy into a
zeroed byte slice. As the slice is now created using make with an
initial length of zero, this copy can also be removed.
As confirmed by poking the bytes with the unsafe package, gc does not
zero array elements between the len and cap when allocating slices
with make(). In combination with the paddedAppend func, this results
in only a single copy of each byte, with no unnecssary zeroing, when
creating the serialized pubkeys. This has not been tested with other
Go compilers (namely, gccgo and llgo), but the new behavior is still
functionally correct regardless of compiler optimizations.
The TestPad function has been removed as the pad func it tested has
likewise been removed.
ok @davecgh
2014-05-20 04:24:48 +02:00
|
|
|
b := make([]byte, 0, PubKeyBytesLenHybrid)
|
2013-09-11 05:14:30 +02:00
|
|
|
format := pubkeyHybrid
|
|
|
|
if isOdd(p.Y) {
|
|
|
|
format |= 0x1
|
|
|
|
}
|
Optimize public key serialization.
This change removes the internal pad function in favor a more opimized
paddedAppend function. Unlike pad, which would always alloate a new
slice of the desired size and copy the bytes into it, paddedAppend
only appends the leading padding when necesary, and uses the builtin
append to copy the remaining source bytes. pad was also used in
combination with another call to the builtin copy func to copy into a
zeroed byte slice. As the slice is now created using make with an
initial length of zero, this copy can also be removed.
As confirmed by poking the bytes with the unsafe package, gc does not
zero array elements between the len and cap when allocating slices
with make(). In combination with the paddedAppend func, this results
in only a single copy of each byte, with no unnecssary zeroing, when
creating the serialized pubkeys. This has not been tested with other
Go compilers (namely, gccgo and llgo), but the new behavior is still
functionally correct regardless of compiler optimizations.
The TestPad function has been removed as the pad func it tested has
likewise been removed.
ok @davecgh
2014-05-20 04:24:48 +02:00
|
|
|
b = append(b, format)
|
|
|
|
b = paddedAppend(32, b, p.X.Bytes())
|
|
|
|
return paddedAppend(32, b, p.Y.Bytes())
|
2013-09-11 05:14:30 +02:00
|
|
|
}
|
2013-11-01 21:09:46 +01:00
|
|
|
|
2016-04-14 04:56:10 +02:00
|
|
|
// IsEqual compares this PublicKey instance to the one passed, returning true if
|
|
|
|
// both PublicKeys are equivalent. A PublicKey is equivalent to another, if they
|
|
|
|
// both have the same X and Y coordinate.
|
|
|
|
func (p *PublicKey) IsEqual(otherPubKey *PublicKey) bool {
|
|
|
|
return p.X.Cmp(otherPubKey.X) == 0 &&
|
|
|
|
p.Y.Cmp(otherPubKey.Y) == 0
|
|
|
|
}
|
|
|
|
|
Optimize public key serialization.
This change removes the internal pad function in favor a more opimized
paddedAppend function. Unlike pad, which would always alloate a new
slice of the desired size and copy the bytes into it, paddedAppend
only appends the leading padding when necesary, and uses the builtin
append to copy the remaining source bytes. pad was also used in
combination with another call to the builtin copy func to copy into a
zeroed byte slice. As the slice is now created using make with an
initial length of zero, this copy can also be removed.
As confirmed by poking the bytes with the unsafe package, gc does not
zero array elements between the len and cap when allocating slices
with make(). In combination with the paddedAppend func, this results
in only a single copy of each byte, with no unnecssary zeroing, when
creating the serialized pubkeys. This has not been tested with other
Go compilers (namely, gccgo and llgo), but the new behavior is still
functionally correct regardless of compiler optimizations.
The TestPad function has been removed as the pad func it tested has
likewise been removed.
ok @davecgh
2014-05-20 04:24:48 +02:00
|
|
|
// paddedAppend appends the src byte slice to dst, returning the new slice.
|
|
|
|
// If the length of the source is smaller than the passed size, leading zero
|
|
|
|
// bytes are appended to the dst slice before appending src.
|
|
|
|
func paddedAppend(size uint, dst, src []byte) []byte {
|
|
|
|
for i := 0; i < int(size)-len(src); i++ {
|
|
|
|
dst = append(dst, 0)
|
2013-11-01 21:09:46 +01:00
|
|
|
}
|
Optimize public key serialization.
This change removes the internal pad function in favor a more opimized
paddedAppend function. Unlike pad, which would always alloate a new
slice of the desired size and copy the bytes into it, paddedAppend
only appends the leading padding when necesary, and uses the builtin
append to copy the remaining source bytes. pad was also used in
combination with another call to the builtin copy func to copy into a
zeroed byte slice. As the slice is now created using make with an
initial length of zero, this copy can also be removed.
As confirmed by poking the bytes with the unsafe package, gc does not
zero array elements between the len and cap when allocating slices
with make(). In combination with the paddedAppend func, this results
in only a single copy of each byte, with no unnecssary zeroing, when
creating the serialized pubkeys. This has not been tested with other
Go compilers (namely, gccgo and llgo), but the new behavior is still
functionally correct regardless of compiler optimizations.
The TestPad function has been removed as the pad func it tested has
likewise been removed.
ok @davecgh
2014-05-20 04:24:48 +02:00
|
|
|
return append(dst, src...)
|
2013-11-01 21:09:46 +01:00
|
|
|
}
|