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.

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