1 // Copyright (c) 2013-2014 The btcsuite developers
2 // Copyright (c) 2015-2024 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 181 default:
182 str := fmt.Sprintf("malformed public key: invalid length: %d",
183 len(serialized))
184 return nil, makeError(ErrPubKeyInvalidLen, str)
185 }
186 187 return NewPublicKey(&x, &y), nil
188 }
189 190 // SerializeUncompressed serializes a public key in the 65-byte uncompressed
191 // format.
192 func (p PublicKey) SerializeUncompressed() []byte {
193 // 0x04 || 32-byte x coordinate || 32-byte y coordinate
194 var b [PubKeyBytesLenUncompressed]byte
195 b[0] = PubKeyFormatUncompressed
196 p.x.PutBytesUnchecked(b[1:33])
197 p.y.PutBytesUnchecked(b[33:65])
198 return b[:]
199 }
200 201 // SerializeCompressed serializes a public key in the 33-byte compressed format.
202 func (p PublicKey) SerializeCompressed() []byte {
203 // Choose the format byte depending on the oddness of the Y coordinate.
204 format := PubKeyFormatCompressedEven
205 if p.y.IsOdd() {
206 format = PubKeyFormatCompressedOdd
207 }
208 209 // 0x02 or 0x03 || 32-byte x coordinate
210 var b [PubKeyBytesLenCompressed]byte
211 b[0] = format
212 p.x.PutBytesUnchecked(b[1:33])
213 return b[:]
214 }
215 216 // IsEqual compares this public key instance to the one passed, returning true
217 // if both public keys are equivalent. A public key is equivalent to another,
218 // if they both have the same X and Y coordinates.
219 func (p *PublicKey) IsEqual(otherPubKey *PublicKey) bool {
220 return p.x.Equals(&otherPubKey.x) && p.y.Equals(&otherPubKey.y)
221 }
222 223 // AsJacobian converts the public key into a Jacobian point with Z=1 and stores
224 // the result in the provided result param. This allows the public key to be
225 // treated a Jacobian point in the secp256k1 group in calculations.
226 func (p *PublicKey) AsJacobian(result *JacobianPoint) {
227 result.X.Set(&p.x)
228 result.Y.Set(&p.y)
229 result.Z.SetInt(1)
230 }
231 232 // IsOnCurve returns whether or not the public key represents a point on the
233 // secp256k1 curve.
234 func (p *PublicKey) IsOnCurve() bool {
235 return isOnCurve(&p.x, &p.y)
236 }
237