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.8 KiB

  1. package cryptostore
  2. import (
  3. "strings"
  4. crypto "github.com/tendermint/go-crypto"
  5. keys "github.com/tendermint/go-crypto/keys"
  6. )
  7. // Manager combines encyption and storage implementation to provide
  8. // a full-featured key manager
  9. type Manager struct {
  10. es encryptedStorage
  11. codec keys.Codec
  12. }
  13. func New(coder Encoder, store keys.Storage, codec keys.Codec) Manager {
  14. return Manager{
  15. es: encryptedStorage{
  16. coder: coder,
  17. store: store,
  18. },
  19. codec: codec,
  20. }
  21. }
  22. // assert Manager satisfies keys.Signer and keys.Manager interfaces
  23. var _ keys.Signer = Manager{}
  24. var _ keys.Manager = Manager{}
  25. // Create adds a new key to the storage engine, returning error if
  26. // another key already stored under this name
  27. //
  28. // algo must be a supported go-crypto algorithm: ed25519, secp256k1
  29. func (s Manager) Create(name, passphrase, algo string) (keys.Info, string, error) {
  30. gen, err := getGenerator(algo)
  31. if err != nil {
  32. return keys.Info{}, "", err
  33. }
  34. // 128-bits are the all the randomness we can make use of
  35. secret := crypto.CRandBytes(16)
  36. key := gen.Generate(secret)
  37. err = s.es.Put(name, passphrase, key)
  38. if err != nil {
  39. return keys.Info{}, "", err
  40. }
  41. // we append the type byte to the serialized secret to help with recovery
  42. // ie [secret] = [secret] + [type]
  43. typ := key.Bytes()[0]
  44. secret = append(secret, typ)
  45. seed, err := s.codec.BytesToWords(secret)
  46. phrase := strings.Join(seed, " ")
  47. return info(name, key), phrase, err
  48. }
  49. // Recover takes a seed phrase and tries to recover the private key.
  50. //
  51. // If the seed phrase is valid, it will create the private key and store
  52. // it under name, protected by passphrase.
  53. //
  54. // Result similar to New(), except it doesn't return the seed again...
  55. func (s Manager) Recover(name, passphrase, seedphrase string) (keys.Info, error) {
  56. words := strings.Split(strings.TrimSpace(seedphrase), " ")
  57. secret, err := s.codec.WordsToBytes(words)
  58. if err != nil {
  59. return keys.Info{}, err
  60. }
  61. // secret is comprised of the actual secret with the type appended
  62. // ie [secret] = [secret] + [type]
  63. l := len(secret)
  64. secret, typ := secret[:l-1], secret[l-1]
  65. gen, err := getGeneratorByType(typ)
  66. if err != nil {
  67. return keys.Info{}, err
  68. }
  69. key := gen.Generate(secret)
  70. // d00d, it worked! create the bugger....
  71. err = s.es.Put(name, passphrase, key)
  72. return info(name, key), err
  73. }
  74. // List loads the keys from the storage and enforces alphabetical order
  75. func (s Manager) List() (keys.Infos, error) {
  76. res, err := s.es.List()
  77. res.Sort()
  78. return res, err
  79. }
  80. // Get returns the public information about one key
  81. func (s Manager) Get(name string) (keys.Info, error) {
  82. _, _, info, err := s.es.store.Get(name)
  83. return info, err
  84. }
  85. // Sign will modify the Signable in order to attach a valid signature with
  86. // this public key
  87. //
  88. // If no key for this name, or the passphrase doesn't match, returns an error
  89. func (s Manager) Sign(name, passphrase string, tx keys.Signable) error {
  90. key, _, err := s.es.Get(name, passphrase)
  91. if err != nil {
  92. return err
  93. }
  94. sig := key.Sign(tx.SignBytes())
  95. pubkey := key.PubKey()
  96. return tx.Sign(pubkey, sig)
  97. }
  98. // Export decodes the private key with the current password, encodes
  99. // it with a secure one-time password and generates a sequence that can be
  100. // Imported by another Manager
  101. //
  102. // This is designed to copy from one device to another, or provide backups
  103. // during version updates.
  104. // TODO: How to handle Export with salt?
  105. func (s Manager) Export(name, oldpass, transferpass string) (salt, data []byte, err error) {
  106. key, _, err := s.es.Get(name, oldpass)
  107. if err != nil {
  108. return nil, nil, err
  109. }
  110. salt, data, err = s.es.coder.Encrypt(key, transferpass)
  111. return salt, data, err
  112. }
  113. // Import accepts bytes generated by Export along with the same transferpass
  114. // If they are valid, it stores the key under the given name with the
  115. // new passphrase.
  116. // TODO: How to handle Import with salt?
  117. func (s Manager) Import(name, newpass, transferpass string, salt, data []byte) error {
  118. key, err := s.es.coder.Decrypt(salt, data, transferpass)
  119. if err != nil {
  120. return err
  121. }
  122. return s.es.Put(name, newpass, key)
  123. }
  124. // Delete removes key forever, but we must present the
  125. // proper passphrase before deleting it (for security)
  126. func (s Manager) Delete(name, passphrase string) error {
  127. // verify we have the proper password before deleting
  128. _, _, err := s.es.Get(name, passphrase)
  129. if err != nil {
  130. return err
  131. }
  132. return s.es.Delete(name)
  133. }
  134. // Update changes the passphrase with which a already stored key is encoded.
  135. //
  136. // oldpass must be the current passphrase used for encoding, newpass will be
  137. // the only valid passphrase from this time forward
  138. func (s Manager) Update(name, oldpass, newpass string) error {
  139. key, _, err := s.es.Get(name, oldpass)
  140. if err != nil {
  141. return err
  142. }
  143. // we must delete first, as Putting over an existing name returns an error
  144. s.Delete(name, oldpass)
  145. return s.es.Put(name, newpass, key)
  146. }