You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

159 lines
4.5 KiB

  1. package secp256k1
  2. import (
  3. "bytes"
  4. "crypto/sha256"
  5. "crypto/subtle"
  6. "fmt"
  7. "io"
  8. "math/big"
  9. secp256k1 "github.com/btcsuite/btcd/btcec"
  10. "golang.org/x/crypto/ripemd160" // nolint: staticcheck // necessary for Bitcoin address format
  11. "github.com/tendermint/tendermint/crypto"
  12. )
  13. var _ crypto.PrivKey = PrivKey{}
  14. const (
  15. PrivKeySize = 32
  16. keyType = "secp256k1"
  17. )
  18. // PrivKey implements PrivKey.
  19. type PrivKey []byte
  20. // Bytes marshalls the private key using amino encoding.
  21. func (privKey PrivKey) Bytes() []byte {
  22. return []byte(privKey)
  23. }
  24. // PubKey performs the point-scalar multiplication from the privKey on the
  25. // generator point to get the pubkey.
  26. func (privKey PrivKey) PubKey() crypto.PubKey {
  27. _, pubkeyObject := secp256k1.PrivKeyFromBytes(secp256k1.S256(), privKey)
  28. return PubKey(pubkeyObject.SerializeCompressed())
  29. }
  30. // Equals - you probably don't need to use this.
  31. // Runs in constant time based on length of the keys.
  32. func (privKey PrivKey) Equals(other crypto.PrivKey) bool {
  33. if otherSecp, ok := other.(PrivKey); ok {
  34. return subtle.ConstantTimeCompare(privKey[:], otherSecp[:]) == 1
  35. }
  36. return false
  37. }
  38. func (privKey PrivKey) Type() string {
  39. return keyType
  40. }
  41. // GenPrivKey generates a new ECDSA private key on curve secp256k1 private key.
  42. // It uses OS randomness to generate the private key.
  43. func GenPrivKey() PrivKey {
  44. return genPrivKey(crypto.CReader())
  45. }
  46. // genPrivKey generates a new secp256k1 private key using the provided reader.
  47. func genPrivKey(rand io.Reader) PrivKey {
  48. var privKeyBytes [PrivKeySize]byte
  49. d := new(big.Int)
  50. for {
  51. privKeyBytes = [PrivKeySize]byte{}
  52. _, err := io.ReadFull(rand, privKeyBytes[:])
  53. if err != nil {
  54. panic(err)
  55. }
  56. d.SetBytes(privKeyBytes[:])
  57. // break if we found a valid point (i.e. > 0 and < N == curverOrder)
  58. isValidFieldElement := 0 < d.Sign() && d.Cmp(secp256k1.S256().N) < 0
  59. if isValidFieldElement {
  60. break
  61. }
  62. }
  63. return PrivKey(privKeyBytes[:])
  64. }
  65. var one = new(big.Int).SetInt64(1)
  66. // GenPrivKeySecp256k1 hashes the secret with SHA2, and uses
  67. // that 32 byte output to create the private key.
  68. //
  69. // It makes sure the private key is a valid field element by setting:
  70. //
  71. // c = sha256(secret)
  72. // k = (c mod (n − 1)) + 1, where n = curve order.
  73. //
  74. // NOTE: secret should be the output of a KDF like bcrypt,
  75. // if it's derived from user input.
  76. func GenPrivKeySecp256k1(secret []byte) PrivKey {
  77. secHash := sha256.Sum256(secret)
  78. // to guarantee that we have a valid field element, we use the approach of:
  79. // "Suite B Implementer’s Guide to FIPS 186-3", A.2.1
  80. // https://apps.nsa.gov/iaarchive/library/ia-guidance/ia-solutions-for-classified/algorithm-guidance/suite-b-implementers-guide-to-fips-186-3-ecdsa.cfm
  81. // see also https://github.com/golang/go/blob/0380c9ad38843d523d9c9804fe300cb7edd7cd3c/src/crypto/ecdsa/ecdsa.go#L89-L101
  82. fe := new(big.Int).SetBytes(secHash[:])
  83. n := new(big.Int).Sub(secp256k1.S256().N, one)
  84. fe.Mod(fe, n)
  85. fe.Add(fe, one)
  86. feB := fe.Bytes()
  87. privKey32 := make([]byte, PrivKeySize)
  88. // copy feB over to fixed 32 byte privKey32 and pad (if necessary)
  89. copy(privKey32[32-len(feB):32], feB)
  90. return PrivKey(privKey32)
  91. }
  92. //-------------------------------------
  93. var _ crypto.PubKey = PubKey{}
  94. // PubKeySize is comprised of 32 bytes for one field element
  95. // (the x-coordinate), plus one byte for the parity of the y-coordinate.
  96. const PubKeySize = 33
  97. // PubKey implements crypto.PubKey.
  98. // It is the compressed form of the pubkey. The first byte depends is a 0x02 byte
  99. // if the y-coordinate is the lexicographically largest of the two associated with
  100. // the x-coordinate. Otherwise the first byte is a 0x03.
  101. // This prefix is followed with the x-coordinate.
  102. type PubKey []byte
  103. // Address returns a Bitcoin style addresses: RIPEMD160(SHA256(pubkey))
  104. func (pubKey PubKey) Address() crypto.Address {
  105. if len(pubKey) != PubKeySize {
  106. panic("length of pubkey is incorrect")
  107. }
  108. hasherSHA256 := sha256.New()
  109. hasherSHA256.Write(pubKey) // does not error
  110. sha := hasherSHA256.Sum(nil)
  111. hasherRIPEMD160 := ripemd160.New()
  112. hasherRIPEMD160.Write(sha) // does not error
  113. return crypto.Address(hasherRIPEMD160.Sum(nil))
  114. }
  115. // Bytes returns the pubkey byte format.
  116. func (pubKey PubKey) Bytes() []byte {
  117. return []byte(pubKey)
  118. }
  119. func (pubKey PubKey) String() string {
  120. return fmt.Sprintf("PubKeySecp256k1{%X}", []byte(pubKey))
  121. }
  122. func (pubKey PubKey) Type() string {
  123. return keyType
  124. }
  125. func (pubKey PubKey) Equals(other crypto.PubKey) bool {
  126. if otherSecp, ok := other.(PubKey); ok {
  127. return bytes.Equal(pubKey[:], otherSecp[:])
  128. }
  129. return false
  130. }