github.com/lbryio/lbcd@v0.22.119/btcec/pubkey.go (about) 1 // Copyright (c) 2013-2014 The btcsuite developers 2 // Use of this source code is governed by an ISC 3 // license that can be found in the LICENSE file. 4 5 package btcec 6 7 import ( 8 "crypto/ecdsa" 9 "errors" 10 "fmt" 11 "math/big" 12 ) 13 14 // These constants define the lengths of serialized public keys. 15 const ( 16 PubKeyBytesLenCompressed = 33 17 PubKeyBytesLenUncompressed = 65 18 PubKeyBytesLenHybrid = 65 19 ) 20 21 func isOdd(a *big.Int) bool { 22 return a.Bit(0) == 1 23 } 24 25 // decompressPoint decompresses a point on the secp256k1 curve given the X point and 26 // the solution to use. 27 func decompressPoint(curve *KoblitzCurve, bigX *big.Int, ybit bool) (*big.Int, error) { 28 var x fieldVal 29 x.SetByteSlice(bigX.Bytes()) 30 31 // Compute x^3 + B mod p. 32 var x3 fieldVal 33 x3.SquareVal(&x).Mul(&x) 34 x3.Add(curve.fieldB).Normalize() 35 36 // Now calculate sqrt mod p of x^3 + B 37 // This code used to do a full sqrt based on tonelli/shanks, 38 // but this was replaced by the algorithms referenced in 39 // https://bitcointalk.org/index.php?topic=162805.msg1712294#msg1712294 40 var y fieldVal 41 y.SqrtVal(&x3).Normalize() 42 if ybit != y.IsOdd() { 43 y.Negate(1).Normalize() 44 } 45 46 // Check that y is a square root of x^3 + B. 47 var y2 fieldVal 48 y2.SquareVal(&y).Normalize() 49 if !y2.Equals(&x3) { 50 return nil, fmt.Errorf("invalid square root") 51 } 52 53 // Verify that y-coord has expected parity. 54 if ybit != y.IsOdd() { 55 return nil, fmt.Errorf("ybit doesn't match oddness") 56 } 57 58 return new(big.Int).SetBytes(y.Bytes()[:]), nil 59 } 60 61 const ( 62 pubkeyCompressed byte = 0x2 // y_bit + x coord 63 pubkeyUncompressed byte = 0x4 // x coord + y coord 64 pubkeyHybrid byte = 0x6 // y_bit + x coord + y coord 65 ) 66 67 // IsCompressedPubKey returns true the the passed serialized public key has 68 // been encoded in compressed format, and false otherwise. 69 func IsCompressedPubKey(pubKey []byte) bool { 70 // The public key is only compressed if it is the correct length and 71 // the format (first byte) is one of the compressed pubkey values. 72 return len(pubKey) == PubKeyBytesLenCompressed && 73 (pubKey[0]&^byte(0x1) == pubkeyCompressed) 74 } 75 76 // ParsePubKey parses a public key for a koblitz curve from a bytestring into a 77 // ecdsa.Publickey, verifying that it is valid. It supports compressed, 78 // uncompressed and hybrid signature formats. 79 func ParsePubKey(pubKeyStr []byte, curve *KoblitzCurve) (key *PublicKey, err error) { 80 pubkey := PublicKey{} 81 pubkey.Curve = curve 82 83 if len(pubKeyStr) == 0 { 84 return nil, errors.New("pubkey string is empty") 85 } 86 87 format := pubKeyStr[0] 88 ybit := (format & 0x1) == 0x1 89 format &= ^byte(0x1) 90 91 switch len(pubKeyStr) { 92 case PubKeyBytesLenUncompressed: 93 if format != pubkeyUncompressed && format != pubkeyHybrid { 94 return nil, fmt.Errorf("invalid magic in pubkey str: "+ 95 "%d", pubKeyStr[0]) 96 } 97 98 pubkey.X = new(big.Int).SetBytes(pubKeyStr[1:33]) 99 pubkey.Y = new(big.Int).SetBytes(pubKeyStr[33:]) 100 // hybrid keys have extra information, make use of it. 101 if format == pubkeyHybrid && ybit != isOdd(pubkey.Y) { 102 return nil, fmt.Errorf("ybit doesn't match oddness") 103 } 104 105 if pubkey.X.Cmp(pubkey.Curve.Params().P) >= 0 { 106 return nil, fmt.Errorf("pubkey X parameter is >= to P") 107 } 108 if pubkey.Y.Cmp(pubkey.Curve.Params().P) >= 0 { 109 return nil, fmt.Errorf("pubkey Y parameter is >= to P") 110 } 111 if !pubkey.Curve.IsOnCurve(pubkey.X, pubkey.Y) { 112 return nil, fmt.Errorf("pubkey isn't on secp256k1 curve") 113 } 114 115 case PubKeyBytesLenCompressed: 116 // format is 0x2 | solution, <X coordinate> 117 // solution determines which solution of the curve we use. 118 /// y^2 = x^3 + Curve.B 119 if format != pubkeyCompressed { 120 return nil, fmt.Errorf("invalid magic in compressed "+ 121 "pubkey string: %d", pubKeyStr[0]) 122 } 123 pubkey.X = new(big.Int).SetBytes(pubKeyStr[1:33]) 124 pubkey.Y, err = decompressPoint(curve, pubkey.X, ybit) 125 if err != nil { 126 return nil, err 127 } 128 129 default: // wrong! 130 return nil, fmt.Errorf("invalid pub key length %d", 131 len(pubKeyStr)) 132 } 133 134 return &pubkey, nil 135 } 136 137 // PublicKey is an ecdsa.PublicKey with additional functions to 138 // serialize in uncompressed, compressed, and hybrid formats. 139 type PublicKey ecdsa.PublicKey 140 141 // ToECDSA returns the public key as a *ecdsa.PublicKey. 142 func (p *PublicKey) ToECDSA() *ecdsa.PublicKey { 143 return (*ecdsa.PublicKey)(p) 144 } 145 146 // SerializeUncompressed serializes a public key in a 65-byte uncompressed 147 // format. 148 func (p *PublicKey) SerializeUncompressed() []byte { 149 b := make([]byte, 0, PubKeyBytesLenUncompressed) 150 b = append(b, pubkeyUncompressed) 151 b = paddedAppend(32, b, p.X.Bytes()) 152 return paddedAppend(32, b, p.Y.Bytes()) 153 } 154 155 // SerializeCompressed serializes a public key in a 33-byte compressed format. 156 func (p *PublicKey) SerializeCompressed() []byte { 157 b := make([]byte, 0, PubKeyBytesLenCompressed) 158 format := pubkeyCompressed 159 if isOdd(p.Y) { 160 format |= 0x1 161 } 162 b = append(b, format) 163 return paddedAppend(32, b, p.X.Bytes()) 164 } 165 166 // SerializeHybrid serializes a public key in a 65-byte hybrid format. 167 func (p *PublicKey) SerializeHybrid() []byte { 168 b := make([]byte, 0, PubKeyBytesLenHybrid) 169 format := pubkeyHybrid 170 if isOdd(p.Y) { 171 format |= 0x1 172 } 173 b = append(b, format) 174 b = paddedAppend(32, b, p.X.Bytes()) 175 return paddedAppend(32, b, p.Y.Bytes()) 176 } 177 178 // IsEqual compares this PublicKey instance to the one passed, returning true if 179 // both PublicKeys are equivalent. A PublicKey is equivalent to another, if they 180 // both have the same X and Y coordinate. 181 func (p *PublicKey) IsEqual(otherPubKey *PublicKey) bool { 182 return p.X.Cmp(otherPubKey.X) == 0 && 183 p.Y.Cmp(otherPubKey.Y) == 0 184 } 185 186 // paddedAppend appends the src byte slice to dst, returning the new slice. 187 // If the length of the source is smaller than the passed size, leading zero 188 // bytes are appended to the dst slice before appending src. 189 func paddedAppend(size uint, dst, src []byte) []byte { 190 for i := 0; i < int(size)-len(src); i++ { 191 dst = append(dst, 0) 192 } 193 return append(dst, src...) 194 }