| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181 |
- // Copyright (c) 2013-2014 The btcsuite developers
- // Use of this source code is governed by an ISC
- // license that can be found in the LICENSE file.
- package btcec
- import (
- "crypto/ecdsa"
- "errors"
- "fmt"
- "math/big"
- )
- // These constants define the lengths of serialized public keys.
- const (
- PubKeyBytesLenCompressed = 33
- PubKeyBytesLenUncompressed = 65
- PubKeyBytesLenHybrid = 65
- )
- func isOdd(a *big.Int) bool {
- return a.Bit(0) == 1
- }
- // decompressPoint decompresses a point on the given curve given the X point and
- // the solution to use.
- func decompressPoint(curve *KoblitzCurve, x *big.Int, ybit bool) (*big.Int, error) {
- // TODO: This will probably only work for secp256k1 due to
- // optimizations.
- // Y = +-sqrt(x^3 + B)
- x3 := new(big.Int).Mul(x, x)
- x3.Mul(x3, x)
- x3.Add(x3, curve.Params().B)
- // now calculate sqrt mod p of x2 + B
- // 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
- y := new(big.Int).Exp(x3, curve.QPlus1Div4(), curve.Params().P)
- if ybit != isOdd(y) {
- y.Sub(curve.Params().P, y)
- }
- if ybit != isOdd(y) {
- return nil, fmt.Errorf("ybit doesn't match oddness")
- }
- return y, nil
- }
- const (
- pubkeyCompressed byte = 0x2 // y_bit + x coord
- pubkeyUncompressed byte = 0x4 // x coord + y coord
- pubkeyHybrid byte = 0x6 // y_bit + x coord + y coord
- )
- // 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)
- }
- // 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.
- func ParsePubKey(pubKeyStr []byte, curve *KoblitzCurve) (key *PublicKey, err error) {
- pubkey := PublicKey{}
- pubkey.Curve = curve
- if len(pubKeyStr) == 0 {
- return nil, errors.New("pubkey string is empty")
- }
- format := pubKeyStr[0]
- ybit := (format & 0x1) == 0x1
- format &= ^byte(0x1)
- switch len(pubKeyStr) {
- case PubKeyBytesLenUncompressed:
- 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")
- }
- case PubKeyBytesLenCompressed:
- // 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])
- pubkey.Y, err = decompressPoint(curve, pubkey.X, ybit)
- if err != nil {
- return nil, err
- }
- default: // wrong!
- return nil, fmt.Errorf("invalid pub key length %d",
- len(pubKeyStr))
- }
- 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")
- }
- return &pubkey, nil
- }
- // PublicKey is an ecdsa.PublicKey with additional functions to
- // serialize in uncompressed, compressed, and hybrid formats.
- type PublicKey ecdsa.PublicKey
- // ToECDSA returns the public key as a *ecdsa.PublicKey.
- func (p *PublicKey) ToECDSA() *ecdsa.PublicKey {
- return (*ecdsa.PublicKey)(p)
- }
- // SerializeUncompressed serializes a public key in a 65-byte uncompressed
- // format.
- func (p *PublicKey) SerializeUncompressed() []byte {
- b := make([]byte, 0, PubKeyBytesLenUncompressed)
- b = append(b, pubkeyUncompressed)
- b = paddedAppend(32, b, p.X.Bytes())
- return paddedAppend(32, b, p.Y.Bytes())
- }
- // SerializeCompressed serializes a public key in a 33-byte compressed format.
- func (p *PublicKey) SerializeCompressed() []byte {
- b := make([]byte, 0, PubKeyBytesLenCompressed)
- format := pubkeyCompressed
- if isOdd(p.Y) {
- format |= 0x1
- }
- b = append(b, format)
- return paddedAppend(32, b, p.X.Bytes())
- }
- // SerializeHybrid serializes a public key in a 65-byte hybrid format.
- func (p *PublicKey) SerializeHybrid() []byte {
- b := make([]byte, 0, PubKeyBytesLenHybrid)
- format := pubkeyHybrid
- if isOdd(p.Y) {
- format |= 0x1
- }
- b = append(b, format)
- b = paddedAppend(32, b, p.X.Bytes())
- return paddedAppend(32, b, p.Y.Bytes())
- }
- // 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
- }
- // 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)
- }
- return append(dst, src...)
- }
|