pubkey.go 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. // Copyright (c) 2013-2014 The btcsuite developers
  2. // Use of this source code is governed by an ISC
  3. // license that can be found in the LICENSE file.
  4. package btcec
  5. import (
  6. "crypto/ecdsa"
  7. "errors"
  8. "fmt"
  9. "math/big"
  10. )
  11. // These constants define the lengths of serialized public keys.
  12. const (
  13. PubKeyBytesLenCompressed = 33
  14. PubKeyBytesLenUncompressed = 65
  15. PubKeyBytesLenHybrid = 65
  16. )
  17. func isOdd(a *big.Int) bool {
  18. return a.Bit(0) == 1
  19. }
  20. // decompressPoint decompresses a point on the given curve given the X point and
  21. // the solution to use.
  22. func decompressPoint(curve *KoblitzCurve, x *big.Int, ybit bool) (*big.Int, error) {
  23. // TODO: This will probably only work for secp256k1 due to
  24. // optimizations.
  25. // Y = +-sqrt(x^3 + B)
  26. x3 := new(big.Int).Mul(x, x)
  27. x3.Mul(x3, x)
  28. x3.Add(x3, curve.Params().B)
  29. // now calculate sqrt mod p of x2 + B
  30. // This code used to do a full sqrt based on tonelli/shanks,
  31. // but this was replaced by the algorithms referenced in
  32. // https://bitcointalk.org/index.php?topic=162805.msg1712294#msg1712294
  33. y := new(big.Int).Exp(x3, curve.QPlus1Div4(), curve.Params().P)
  34. if ybit != isOdd(y) {
  35. y.Sub(curve.Params().P, y)
  36. }
  37. if ybit != isOdd(y) {
  38. return nil, fmt.Errorf("ybit doesn't match oddness")
  39. }
  40. return y, nil
  41. }
  42. const (
  43. pubkeyCompressed byte = 0x2 // y_bit + x coord
  44. pubkeyUncompressed byte = 0x4 // x coord + y coord
  45. pubkeyHybrid byte = 0x6 // y_bit + x coord + y coord
  46. )
  47. // IsCompressedPubKey returns true the the passed serialized public key has
  48. // been encoded in compressed format, and false otherwise.
  49. func IsCompressedPubKey(pubKey []byte) bool {
  50. // The public key is only compressed if it is the correct length and
  51. // the format (first byte) is one of the compressed pubkey values.
  52. return len(pubKey) == PubKeyBytesLenCompressed &&
  53. (pubKey[0]&^byte(0x1) == pubkeyCompressed)
  54. }
  55. // ParsePubKey parses a public key for a koblitz curve from a bytestring into a
  56. // ecdsa.Publickey, verifying that it is valid. It supports compressed,
  57. // uncompressed and hybrid signature formats.
  58. func ParsePubKey(pubKeyStr []byte, curve *KoblitzCurve) (key *PublicKey, err error) {
  59. pubkey := PublicKey{}
  60. pubkey.Curve = curve
  61. if len(pubKeyStr) == 0 {
  62. return nil, errors.New("pubkey string is empty")
  63. }
  64. format := pubKeyStr[0]
  65. ybit := (format & 0x1) == 0x1
  66. format &= ^byte(0x1)
  67. switch len(pubKeyStr) {
  68. case PubKeyBytesLenUncompressed:
  69. if format != pubkeyUncompressed && format != pubkeyHybrid {
  70. return nil, fmt.Errorf("invalid magic in pubkey str: "+
  71. "%d", pubKeyStr[0])
  72. }
  73. pubkey.X = new(big.Int).SetBytes(pubKeyStr[1:33])
  74. pubkey.Y = new(big.Int).SetBytes(pubKeyStr[33:])
  75. // hybrid keys have extra information, make use of it.
  76. if format == pubkeyHybrid && ybit != isOdd(pubkey.Y) {
  77. return nil, fmt.Errorf("ybit doesn't match oddness")
  78. }
  79. case PubKeyBytesLenCompressed:
  80. // format is 0x2 | solution, <X coordinate>
  81. // solution determines which solution of the curve we use.
  82. /// y^2 = x^3 + Curve.B
  83. if format != pubkeyCompressed {
  84. return nil, fmt.Errorf("invalid magic in compressed "+
  85. "pubkey string: %d", pubKeyStr[0])
  86. }
  87. pubkey.X = new(big.Int).SetBytes(pubKeyStr[1:33])
  88. pubkey.Y, err = decompressPoint(curve, pubkey.X, ybit)
  89. if err != nil {
  90. return nil, err
  91. }
  92. default: // wrong!
  93. return nil, fmt.Errorf("invalid pub key length %d",
  94. len(pubKeyStr))
  95. }
  96. if pubkey.X.Cmp(pubkey.Curve.Params().P) >= 0 {
  97. return nil, fmt.Errorf("pubkey X parameter is >= to P")
  98. }
  99. if pubkey.Y.Cmp(pubkey.Curve.Params().P) >= 0 {
  100. return nil, fmt.Errorf("pubkey Y parameter is >= to P")
  101. }
  102. if !pubkey.Curve.IsOnCurve(pubkey.X, pubkey.Y) {
  103. return nil, fmt.Errorf("pubkey isn't on secp256k1 curve")
  104. }
  105. return &pubkey, nil
  106. }
  107. // PublicKey is an ecdsa.PublicKey with additional functions to
  108. // serialize in uncompressed, compressed, and hybrid formats.
  109. type PublicKey ecdsa.PublicKey
  110. // ToECDSA returns the public key as a *ecdsa.PublicKey.
  111. func (p *PublicKey) ToECDSA() *ecdsa.PublicKey {
  112. return (*ecdsa.PublicKey)(p)
  113. }
  114. // SerializeUncompressed serializes a public key in a 65-byte uncompressed
  115. // format.
  116. func (p *PublicKey) SerializeUncompressed() []byte {
  117. b := make([]byte, 0, PubKeyBytesLenUncompressed)
  118. b = append(b, pubkeyUncompressed)
  119. b = paddedAppend(32, b, p.X.Bytes())
  120. return paddedAppend(32, b, p.Y.Bytes())
  121. }
  122. // SerializeCompressed serializes a public key in a 33-byte compressed format.
  123. func (p *PublicKey) SerializeCompressed() []byte {
  124. b := make([]byte, 0, PubKeyBytesLenCompressed)
  125. format := pubkeyCompressed
  126. if isOdd(p.Y) {
  127. format |= 0x1
  128. }
  129. b = append(b, format)
  130. return paddedAppend(32, b, p.X.Bytes())
  131. }
  132. // SerializeHybrid serializes a public key in a 65-byte hybrid format.
  133. func (p *PublicKey) SerializeHybrid() []byte {
  134. b := make([]byte, 0, PubKeyBytesLenHybrid)
  135. format := pubkeyHybrid
  136. if isOdd(p.Y) {
  137. format |= 0x1
  138. }
  139. b = append(b, format)
  140. b = paddedAppend(32, b, p.X.Bytes())
  141. return paddedAppend(32, b, p.Y.Bytes())
  142. }
  143. // IsEqual compares this PublicKey instance to the one passed, returning true if
  144. // both PublicKeys are equivalent. A PublicKey is equivalent to another, if they
  145. // both have the same X and Y coordinate.
  146. func (p *PublicKey) IsEqual(otherPubKey *PublicKey) bool {
  147. return p.X.Cmp(otherPubKey.X) == 0 &&
  148. p.Y.Cmp(otherPubKey.Y) == 0
  149. }
  150. // paddedAppend appends the src byte slice to dst, returning the new slice.
  151. // If the length of the source is smaller than the passed size, leading zero
  152. // bytes are appended to the dst slice before appending src.
  153. func paddedAppend(size uint, dst, src []byte) []byte {
  154. for i := 0; i < int(size)-len(src); i++ {
  155. dst = append(dst, 0)
  156. }
  157. return append(dst, src...)
  158. }