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.

170 lines
4.7 KiB

  1. package ed25519
  2. import (
  3. "bytes"
  4. "crypto/subtle"
  5. "fmt"
  6. "io"
  7. amino "github.com/tendermint/go-amino"
  8. "golang.org/x/crypto/ed25519"
  9. "github.com/tendermint/tendermint/crypto"
  10. "github.com/tendermint/tendermint/crypto/tmhash"
  11. )
  12. //-------------------------------------
  13. var _ crypto.PrivKey = PrivKeyEd25519{}
  14. const (
  15. PrivKeyAminoName = "tendermint/PrivKeyEd25519"
  16. PubKeyAminoName = "tendermint/PubKeyEd25519"
  17. // Size of an Edwards25519 signature. Namely the size of a compressed
  18. // Edwards25519 point, and a field element. Both of which are 32 bytes.
  19. SignatureSize = 64
  20. )
  21. var cdc = amino.NewCodec()
  22. func init() {
  23. cdc.RegisterInterface((*crypto.PubKey)(nil), nil)
  24. cdc.RegisterConcrete(PubKeyEd25519{},
  25. PubKeyAminoName, nil)
  26. cdc.RegisterInterface((*crypto.PrivKey)(nil), nil)
  27. cdc.RegisterConcrete(PrivKeyEd25519{},
  28. PrivKeyAminoName, nil)
  29. }
  30. // PrivKeyEd25519 implements crypto.PrivKey.
  31. type PrivKeyEd25519 [64]byte
  32. // Bytes marshals the privkey using amino encoding.
  33. func (privKey PrivKeyEd25519) Bytes() []byte {
  34. return cdc.MustMarshalBinaryBare(privKey)
  35. }
  36. // Sign produces a signature on the provided message.
  37. // This assumes the privkey is wellformed in the golang format.
  38. // The first 32 bytes should be random,
  39. // corresponding to the normal ed25519 private key.
  40. // The latter 32 bytes should be the compressed public key.
  41. // If these conditions aren't met, Sign will panic or produce an
  42. // incorrect signature.
  43. func (privKey PrivKeyEd25519) Sign(msg []byte) ([]byte, error) {
  44. signatureBytes := ed25519.Sign(privKey[:], msg)
  45. return signatureBytes[:], nil
  46. }
  47. // PubKey gets the corresponding public key from the private key.
  48. func (privKey PrivKeyEd25519) PubKey() crypto.PubKey {
  49. privKeyBytes := [64]byte(privKey)
  50. initialized := false
  51. // If the latter 32 bytes of the privkey are all zero, compute the pubkey
  52. // otherwise privkey is initialized and we can use the cached value inside
  53. // of the private key.
  54. for _, v := range privKeyBytes[32:] {
  55. if v != 0 {
  56. initialized = true
  57. break
  58. }
  59. }
  60. if !initialized {
  61. panic("Expected PrivKeyEd25519 to include concatenated pubkey bytes")
  62. }
  63. var pubkeyBytes [PubKeyEd25519Size]byte
  64. copy(pubkeyBytes[:], privKeyBytes[32:])
  65. return PubKeyEd25519(pubkeyBytes)
  66. }
  67. // Equals - you probably don't need to use this.
  68. // Runs in constant time based on length of the keys.
  69. func (privKey PrivKeyEd25519) Equals(other crypto.PrivKey) bool {
  70. if otherEd, ok := other.(PrivKeyEd25519); ok {
  71. return subtle.ConstantTimeCompare(privKey[:], otherEd[:]) == 1
  72. } else {
  73. return false
  74. }
  75. }
  76. // GenPrivKey generates a new ed25519 private key.
  77. // It uses OS randomness in conjunction with the current global random seed
  78. // in tendermint/libs/common to generate the private key.
  79. func GenPrivKey() PrivKeyEd25519 {
  80. return genPrivKey(crypto.CReader())
  81. }
  82. // genPrivKey generates a new ed25519 private key using the provided reader.
  83. func genPrivKey(rand io.Reader) PrivKeyEd25519 {
  84. seed := make([]byte, 32)
  85. _, err := io.ReadFull(rand, seed[:])
  86. if err != nil {
  87. panic(err)
  88. }
  89. privKey := ed25519.NewKeyFromSeed(seed)
  90. var privKeyEd PrivKeyEd25519
  91. copy(privKeyEd[:], privKey)
  92. return privKeyEd
  93. }
  94. // GenPrivKeyFromSecret hashes the secret with SHA2, and uses
  95. // that 32 byte output to create the private key.
  96. // NOTE: secret should be the output of a KDF like bcrypt,
  97. // if it's derived from user input.
  98. func GenPrivKeyFromSecret(secret []byte) PrivKeyEd25519 {
  99. seed := crypto.Sha256(secret) // Not Ripemd160 because we want 32 bytes.
  100. privKey := ed25519.NewKeyFromSeed(seed)
  101. var privKeyEd PrivKeyEd25519
  102. copy(privKeyEd[:], privKey)
  103. return privKeyEd
  104. }
  105. //-------------------------------------
  106. var _ crypto.PubKey = PubKeyEd25519{}
  107. // PubKeyEd25519Size is the number of bytes in an Ed25519 signature.
  108. const PubKeyEd25519Size = 32
  109. // PubKeyEd25519 implements crypto.PubKey for the Ed25519 signature scheme.
  110. type PubKeyEd25519 [PubKeyEd25519Size]byte
  111. // Address is the SHA256-20 of the raw pubkey bytes.
  112. func (pubKey PubKeyEd25519) Address() crypto.Address {
  113. return crypto.Address(tmhash.SumTruncated(pubKey[:]))
  114. }
  115. // Bytes marshals the PubKey using amino encoding.
  116. func (pubKey PubKeyEd25519) Bytes() []byte {
  117. bz, err := cdc.MarshalBinaryBare(pubKey)
  118. if err != nil {
  119. panic(err)
  120. }
  121. return bz
  122. }
  123. func (pubKey PubKeyEd25519) VerifyBytes(msg []byte, sig []byte) bool {
  124. // make sure we use the same algorithm to sign
  125. if len(sig) != SignatureSize {
  126. return false
  127. }
  128. return ed25519.Verify(pubKey[:], msg, sig)
  129. }
  130. func (pubKey PubKeyEd25519) String() string {
  131. return fmt.Sprintf("PubKeyEd25519{%X}", pubKey[:])
  132. }
  133. // nolint: golint
  134. func (pubKey PubKeyEd25519) Equals(other crypto.PubKey) bool {
  135. if otherEd, ok := other.(PubKeyEd25519); ok {
  136. return bytes.Equal(pubKey[:], otherEd[:])
  137. } else {
  138. return false
  139. }
  140. }