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