1 // Copyright (c) 2013-2014 The btcsuite developers 2 // Copyright (c) 2015-2022 The Decred developers 3 // Use of this source code is governed by an ISC 4 // license that can be found in the LICENSE file. 5 6 package secp256k1 7 8 // References: 9 // [SEC1] Elliptic Curve Cryptography 10 // https://www.secg.org/sec1-v2.pdf 11 // 12 // [SEC2] Recommended Elliptic Curve Domain Parameters 13 // https://www.secg.org/sec2-v2.pdf 14 // 15 // [ANSI X9.62-1998] Public Key Cryptography For The Financial Services 16 // Industry: The Elliptic Curve Digital Signature Algorithm (ECDSA) 17 18 import ( 19 "fmt" 20 ) 21 22 const ( 23 // PubKeyBytesLenCompressed is the number of bytes of a serialized 24 // compressed public key. 25 PubKeyBytesLenCompressed = 33 26 27 // PubKeyBytesLenUncompressed is the number of bytes of a serialized 28 // uncompressed public key. 29 PubKeyBytesLenUncompressed = 65 30 31 // PubKeyFormatCompressedEven is the identifier prefix byte for a public key 32 // whose Y coordinate is even when serialized in the compressed format per 33 // section 2.3.4 of [SEC1](https://secg.org/sec1-v2.pdf#subsubsection.2.3.4). 34 PubKeyFormatCompressedEven byte = 0x02 35 36 // PubKeyFormatCompressedOdd is the identifier prefix byte for a public key 37 // whose Y coordinate is odd when serialized in the compressed format per 38 // section 2.3.4 of [SEC1](https://secg.org/sec1-v2.pdf#subsubsection.2.3.4). 39 PubKeyFormatCompressedOdd byte = 0x03 40 41 // PubKeyFormatUncompressed is the identifier prefix byte for a public key 42 // when serialized according in the uncompressed format per section 2.3.3 of 43 // [SEC1](https://secg.org/sec1-v2.pdf#subsubsection.2.3.3). 44 PubKeyFormatUncompressed byte = 0x04 45 46 // PubKeyFormatHybridEven is the identifier prefix byte for a public key 47 // whose Y coordinate is even when serialized according to the hybrid format 48 // per section 4.3.6 of [ANSI X9.62-1998]. 49 // 50 // NOTE: This format makes little sense in practice an therefore this 51 // package will not produce public keys serialized in this format. However, 52 // it will parse them since they exist in the wild. 53 PubKeyFormatHybridEven byte = 0x06 54 55 // PubKeyFormatHybridOdd is the identifier prefix byte for a public key 56 // whose Y coordingate is odd when serialized according to the hybrid format 57 // per section 4.3.6 of [ANSI X9.62-1998]. 58 // 59 // NOTE: This format makes little sense in practice an therefore this 60 // package will not produce public keys serialized in this format. However, 61 // it will parse them since they exist in the wild. 62 PubKeyFormatHybridOdd byte = 0x07 63 ) 64 65 // PublicKey provides facilities for efficiently working with secp256k1 public 66 // keys within this package and includes functions to serialize in both 67 // uncompressed and compressed SEC (Standards for Efficient Cryptography) 68 // formats. 69 type PublicKey struct { 70 x FieldVal 71 y FieldVal 72 } 73 74 // NewPublicKey instantiates a new public key with the given x and y 75 // coordinates. 76 // 77 // It should be noted that, unlike ParsePubKey, since this accepts arbitrary x 78 // and y coordinates, it allows creation of public keys that are not valid 79 // points on the secp256k1 curve. The IsOnCurve method of the returned instance 80 // can be used to determine validity. 81 func NewPublicKey(x, y *FieldVal) *PublicKey { 82 var pubKey PublicKey 83 pubKey.x.Set(x) 84 pubKey.y.Set(y) 85 return &pubKey 86 } 87 88 // ParsePubKey parses a secp256k1 public key encoded according to the format 89 // specified by ANSI X9.62-1998, which means it is also compatible with the 90 // SEC (Standards for Efficient Cryptography) specification which is a subset of 91 // the former. In other words, it supports the uncompressed, compressed, and 92 // hybrid formats as follows: 93 // 94 // Compressed: 95 // 96 // <format byte = 0x02/0x03><32-byte X coordinate> 97 // 98 // Uncompressed: 99 // 100 // <format byte = 0x04><32-byte X coordinate><32-byte Y coordinate> 101 // 102 // Hybrid: 103 // 104 // <format byte = 0x05/0x06><32-byte X coordinate><32-byte Y coordinate> 105 // 106 // NOTE: The hybrid format makes little sense in practice an therefore this 107 // package will not produce public keys serialized in this format. However, 108 // this function will properly parse them since they exist in the wild. 109 func ParsePubKey(serialized []byte) (key *PublicKey, err error) { 110 var x, y FieldVal 111 switch len(serialized) { 112 case PubKeyBytesLenUncompressed: 113 // Reject unsupported public key formats for the given length. 114 format := serialized[0] 115 switch format { 116 case PubKeyFormatUncompressed: 117 case PubKeyFormatHybridEven, PubKeyFormatHybridOdd: 118 default: 119 str := fmt.Sprintf("invalid public key: unsupported format: %x", 120 format) 121 return nil, makeError(ErrPubKeyInvalidFormat, str) 122 } 123 124 // Parse the x and y coordinates while ensuring that they are in the 125 // allowed range. 126 if overflow := x.SetByteSlice(serialized[1:33]); overflow { 127 str := "invalid public key: x >= field prime" 128 return nil, makeError(ErrPubKeyXTooBig, str) 129 } 130 if overflow := y.SetByteSlice(serialized[33:]); overflow { 131 str := "invalid public key: y >= field prime" 132 return nil, makeError(ErrPubKeyYTooBig, str) 133 } 134 135 // Ensure the oddness of the y coordinate matches the specified format 136 // for hybrid public keys. 137 if format == PubKeyFormatHybridEven || format == PubKeyFormatHybridOdd { 138 wantOddY := format == PubKeyFormatHybridOdd 139 if y.IsOdd() != wantOddY { 140 str := fmt.Sprintf("invalid public key: y oddness does not "+ 141 "match specified value of %v", wantOddY) 142 return nil, makeError(ErrPubKeyMismatchedOddness, str) 143 } 144 } 145 146 // Reject public keys that are not on the secp256k1 curve. 147 if !isOnCurve(&x, &y) { 148 str := fmt.Sprintf("invalid public key: [%v,%v] not on secp256k1 "+ 149 "curve", x, y) 150 return nil, makeError(ErrPubKeyNotOnCurve, str) 151 } 152 153 case PubKeyBytesLenCompressed: 154 // Reject unsupported public key formats for the given length. 155 format := serialized[0] 156 switch format { 157 case PubKeyFormatCompressedEven, PubKeyFormatCompressedOdd: 158 default: 159 str := fmt.Sprintf("invalid public key: unsupported format: %x", 160 format) 161 return nil, makeError(ErrPubKeyInvalidFormat, str) 162 } 163 164 // Parse the x coordinate while ensuring that it is in the allowed 165 // range. 166 if overflow := x.SetByteSlice(serialized[1:33]); overflow { 167 str := "invalid public key: x >= field prime" 168 return nil, makeError(ErrPubKeyXTooBig, str) 169 } 170 171 // Attempt to calculate the y coordinate for the given x coordinate such 172 // that the result pair is a point on the secp256k1 curve and the 173 // solution with desired oddness is chosen. 174 wantOddY := format == PubKeyFormatCompressedOdd 175 if !DecompressY(&x, wantOddY, &y) { 176 str := fmt.Sprintf("invalid public key: x coordinate %v is not on "+ 177 "the secp256k1 curve", x) 178 return nil, makeError(ErrPubKeyNotOnCurve, str) 179 } 180 y.Normalize() 181 182 default: 183 str := fmt.Sprintf("malformed public key: invalid length: %d", 184 len(serialized)) 185 return nil, makeError(ErrPubKeyInvalidLen, str) 186 } 187 188 return NewPublicKey(&x, &y), nil 189 } 190 191 // SerializeUncompressed serializes a public key in the 65-byte uncompressed 192 // format. 193 func (p PublicKey) SerializeUncompressed() []byte { 194 // 0x04 || 32-byte x coordinate || 32-byte y coordinate 195 var b [PubKeyBytesLenUncompressed]byte 196 b[0] = PubKeyFormatUncompressed 197 p.x.PutBytesUnchecked(b[1:33]) 198 p.y.PutBytesUnchecked(b[33:65]) 199 return b[:] 200 } 201 202 // SerializeCompressed serializes a public key in the 33-byte compressed format. 203 func (p PublicKey) SerializeCompressed() []byte { 204 // Choose the format byte depending on the oddness of the Y coordinate. 205 format := PubKeyFormatCompressedEven 206 if p.y.IsOdd() { 207 format = PubKeyFormatCompressedOdd 208 } 209 210 // 0x02 or 0x03 || 32-byte x coordinate 211 var b [PubKeyBytesLenCompressed]byte 212 b[0] = format 213 p.x.PutBytesUnchecked(b[1:33]) 214 return b[:] 215 } 216 217 // IsEqual compares this public key instance to the one passed, returning true 218 // if both public keys are equivalent. A public key is equivalent to another, 219 // if they both have the same X and Y coordinates. 220 func (p *PublicKey) IsEqual(otherPubKey *PublicKey) bool { 221 return p.x.Equals(&otherPubKey.x) && p.y.Equals(&otherPubKey.y) 222 } 223 224 // AsJacobian converts the public key into a Jacobian point with Z=1 and stores 225 // the result in the provided result param. This allows the public key to be 226 // treated a Jacobian point in the secp256k1 group in calculations. 227 func (p *PublicKey) AsJacobian(result *JacobianPoint) { 228 result.X.Set(&p.x) 229 result.Y.Set(&p.y) 230 result.Z.SetInt(1) 231 } 232 233 // IsOnCurve returns whether or not the public key represents a point on the 234 // secp256k1 curve. 235 func (p *PublicKey) IsOnCurve() bool { 236 return isOnCurve(&p.x, &p.y) 237 } 238