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