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.

110 lines
2.8 KiB

  1. package sr25519
  2. import (
  3. "crypto/subtle"
  4. "fmt"
  5. "io"
  6. "github.com/tendermint/tendermint/crypto"
  7. schnorrkel "github.com/ChainSafe/go-schnorrkel"
  8. )
  9. // PrivKeySize is the number of bytes in an Sr25519 private key.
  10. const PrivKeySize = 32
  11. // PrivKeySr25519 implements crypto.PrivKey.
  12. type PrivKey []byte
  13. // Bytes returns the byte representation of the PrivKey.
  14. func (privKey PrivKey) Bytes() []byte {
  15. return []byte(privKey)
  16. }
  17. // Sign produces a signature on the provided message.
  18. func (privKey PrivKey) Sign(msg []byte) ([]byte, error) {
  19. var p [PrivKeySize]byte
  20. copy(p[:], privKey)
  21. miniSecretKey, err := schnorrkel.NewMiniSecretKeyFromRaw(p)
  22. if err != nil {
  23. return []byte{}, err
  24. }
  25. secretKey := miniSecretKey.ExpandEd25519()
  26. signingContext := schnorrkel.NewSigningContext([]byte{}, msg)
  27. sig, err := secretKey.Sign(signingContext)
  28. if err != nil {
  29. return []byte{}, err
  30. }
  31. sigBytes := sig.Encode()
  32. return sigBytes[:], nil
  33. }
  34. // PubKey gets the corresponding public key from the private key.
  35. func (privKey PrivKey) PubKey() crypto.PubKey {
  36. var p [PrivKeySize]byte
  37. copy(p[:], privKey)
  38. miniSecretKey, err := schnorrkel.NewMiniSecretKeyFromRaw(p)
  39. if err != nil {
  40. panic(fmt.Sprintf("Invalid private key: %v", err))
  41. }
  42. secretKey := miniSecretKey.ExpandEd25519()
  43. pubkey, err := secretKey.Public()
  44. if err != nil {
  45. panic(fmt.Sprintf("Could not generate public key: %v", err))
  46. }
  47. key := pubkey.Encode()
  48. return PubKey(key[:])
  49. }
  50. // Equals - you probably don't need to use this.
  51. // Runs in constant time based on length of the keys.
  52. func (privKey PrivKey) Equals(other crypto.PrivKey) bool {
  53. if otherEd, ok := other.(PrivKey); ok {
  54. return subtle.ConstantTimeCompare(privKey[:], otherEd[:]) == 1
  55. }
  56. return false
  57. }
  58. func (privKey PrivKey) Type() string {
  59. return KeyType
  60. }
  61. // GenPrivKey generates a new sr25519 private key.
  62. // It uses OS randomness in conjunction with the current global random seed
  63. // in tendermint/libs/common to generate the private key.
  64. func GenPrivKey() PrivKey {
  65. return genPrivKey(crypto.CReader())
  66. }
  67. // genPrivKey generates a new sr25519 private key using the provided reader.
  68. func genPrivKey(rand io.Reader) PrivKey {
  69. var seed [64]byte
  70. out := make([]byte, 64)
  71. _, err := io.ReadFull(rand, out)
  72. if err != nil {
  73. panic(err)
  74. }
  75. copy(seed[:], out)
  76. key := schnorrkel.NewMiniSecretKey(seed).ExpandEd25519().Encode()
  77. return key[:]
  78. }
  79. // GenPrivKeyFromSecret hashes the secret with SHA2, and uses
  80. // that 32 byte output to create the private key.
  81. // NOTE: secret should be the output of a KDF like bcrypt,
  82. // if it's derived from user input.
  83. func GenPrivKeyFromSecret(secret []byte) PrivKey {
  84. seed := crypto.Sha256(secret) // Not Ripemd160 because we want 32 bytes.
  85. var bz [PrivKeySize]byte
  86. copy(bz[:], seed)
  87. privKey, _ := schnorrkel.NewMiniSecretKeyFromRaw(bz)
  88. key := privKey.ExpandEd25519().Encode()
  89. return key[:]
  90. }