pubkey.go 8.4 KB

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