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.

246 lines
6.8 KiB

7 years ago
  1. package keys
  2. import (
  3. "fmt"
  4. "strings"
  5. "github.com/pkg/errors"
  6. crypto "github.com/tendermint/go-crypto"
  7. dbm "github.com/tendermint/tmlibs/db"
  8. "github.com/tendermint/go-crypto/nano"
  9. )
  10. // XXX Lets use go-crypto/bcrypt and ascii encoding directly in here without
  11. // further wrappers around a store or DB.
  12. // Copy functions from: https://github.com/tendermint/mintkey/blob/master/cmd/mintkey/common.go
  13. //
  14. // dbKeybase combines encyption and storage implementation to provide
  15. // a full-featured key manager
  16. type dbKeybase struct {
  17. db dbm.DB
  18. codec Codec
  19. }
  20. func New(db dbm.DB, codec Codec) dbKeybase {
  21. return dbKeybase{
  22. db: db,
  23. codec: codec,
  24. }
  25. }
  26. var _ Keybase = dbKeybase{}
  27. // Create adds a new key to the storage engine, returning error if
  28. // another key already stored under this name
  29. //
  30. // algo must be a supported go-crypto algorithm: ed25519, secp256k1
  31. func (kb dbKeybase) Create(name, passphrase, algo string) (Info, string, error) {
  32. // 128-bits are the all the randomness we can make use of
  33. secret := crypto.CRandBytes(16)
  34. key, err := generate(algo, secret)
  35. if err != nil {
  36. return Info{}, "", err
  37. }
  38. public := kb.writeKey(key, name, passphrase)
  39. // we append the type byte to the serialized secret to help with recovery
  40. // ie [secret] = [secret] + [type]
  41. typ := key.Bytes()[0]
  42. secret = append(secret, typ)
  43. seed, err := kb.codec.BytesToWords(secret)
  44. phrase := strings.Join(seed, " ")
  45. return public, phrase, err
  46. }
  47. // Recover takes a seed phrase and tries to recover the private key.
  48. //
  49. // If the seed phrase is valid, it will create the private key and store
  50. // it under name, protected by passphrase.
  51. //
  52. // Result similar to New(), except it doesn't return the seed again...
  53. func (kb dbKeybase) Recover(name, passphrase, seedphrase string) (Info, error) {
  54. words := strings.Split(strings.TrimSpace(seedphrase), " ")
  55. secret, err := kb.codec.WordsToBytes(words)
  56. if err != nil {
  57. return Info{}, err
  58. }
  59. // secret is comprised of the actual secret with the type appended
  60. // ie [secret] = [secret] + [type]
  61. l := len(secret)
  62. secret, typ := secret[:l-1], secret[l-1]
  63. key, err := generateByType(typ, secret)
  64. if err != nil {
  65. return Info{}, err
  66. }
  67. // d00d, it worked! create the bugger....
  68. public := kb.writeKey(key, name, passphrase)
  69. return public, err
  70. }
  71. // List loads the keys from the storage and enforces alphabetical order
  72. func (kb dbKeybase) List() ([]Info, error) {
  73. var res []Info
  74. for iter := kb.db.Iterator(); iter.Valid(); iter.Next() {
  75. key := iter.Key()
  76. if isPub(key) {
  77. info, err := readInfo(iter.Value())
  78. if err != nil {
  79. return nil, err
  80. }
  81. res = append(res, info)
  82. }
  83. }
  84. return res, nil
  85. }
  86. // Get returns the public information about one key
  87. func (kb dbKeybase) Get(name string) (Info, error) {
  88. bs := kb.db.Get(pubName(name))
  89. return readInfo(bs)
  90. }
  91. // Sign will modify the Signable in order to attach a valid signature with
  92. // this public key
  93. //
  94. // If no key for this name, or the passphrase doesn't match, returns an error
  95. func (kb dbKeybase) Sign(name, passphrase string, msg []byte) (sig crypto.Signature, pk crypto.PubKey, err error) {
  96. var key crypto.PrivKey
  97. bs := kb.db.Get(privName(name))
  98. key, err = unarmorDecryptPrivKey(string(bs), passphrase)
  99. if err != nil {
  100. return
  101. }
  102. sig = key.Sign(msg)
  103. pk = key.PubKey()
  104. return
  105. }
  106. // Export decodes the private key with the current password, encodes
  107. // it with a secure one-time password and generates a sequence that can be
  108. // Imported by another dbKeybase
  109. //
  110. // This is designed to copy from one device to another, or provide backups
  111. // during version updates.
  112. func (kb dbKeybase) Export(name, oldpass, transferpass string) ([]byte, error) {
  113. bs := kb.db.Get(privName(name))
  114. key, err := unarmorDecryptPrivKey(string(bs), oldpass)
  115. if err != nil {
  116. return nil, err
  117. }
  118. if transferpass == "" {
  119. return key.Bytes(), nil
  120. }
  121. res := encryptArmorPrivKey(key, transferpass)
  122. return []byte(res), nil
  123. }
  124. // Import accepts bytes generated by Export along with the same transferpass
  125. // If they are valid, it stores the password under the given name with the
  126. // new passphrase.
  127. func (kb dbKeybase) Import(name, newpass, transferpass string, data []byte) (err error) {
  128. var key crypto.PrivKey
  129. if transferpass == "" {
  130. key, err = crypto.PrivKeyFromBytes(data)
  131. } else {
  132. key, err = unarmorDecryptPrivKey(string(data), transferpass)
  133. }
  134. if err != nil {
  135. return err
  136. }
  137. kb.writeKey(key, name, newpass)
  138. return nil
  139. }
  140. // Delete removes key forever, but we must present the
  141. // proper passphrase before deleting it (for security)
  142. func (kb dbKeybase) Delete(name, passphrase string) error {
  143. // verify we have the proper password before deleting
  144. bs := kb.db.Get(privName(name))
  145. _, err := unarmorDecryptPrivKey(string(bs), passphrase)
  146. if err != nil {
  147. return err
  148. }
  149. kb.db.DeleteSync(pubName(name))
  150. kb.db.DeleteSync(privName(name))
  151. return nil
  152. }
  153. // Update changes the passphrase with which a already stored key is encoded.
  154. //
  155. // oldpass must be the current passphrase used for encoding, newpass will be
  156. // the only valid passphrase from this time forward
  157. func (kb dbKeybase) Update(name, oldpass, newpass string) error {
  158. bs := kb.db.Get(privName(name))
  159. key, err := unarmorDecryptPrivKey(string(bs), oldpass)
  160. if err != nil {
  161. return err
  162. }
  163. // we must delete first, as Putting over an existing name returns an error
  164. kb.db.DeleteSync(pubName(name))
  165. kb.db.DeleteSync(privName(name))
  166. kb.writeKey(key, name, newpass)
  167. return nil
  168. }
  169. func (kb dbKeybase) writeKey(priv crypto.PrivKey, name, passphrase string) Info {
  170. // generate the public bytes
  171. public := info(name, priv)
  172. // generate the encrypted privkey
  173. private := encryptArmorPrivKey(priv, passphrase)
  174. // write them both
  175. kb.db.SetSync(pubName(name), public.bytes())
  176. kb.db.SetSync(privName(name), []byte(private))
  177. return public
  178. }
  179. func generate(algo string, secret []byte) (crypto.PrivKey, error) {
  180. switch algo {
  181. case crypto.NameEd25519:
  182. return crypto.GenPrivKeyEd25519FromSecret(secret).Wrap(), nil
  183. case crypto.NameSecp256k1:
  184. return crypto.GenPrivKeySecp256k1FromSecret(secret).Wrap(), nil
  185. case nano.NameLedgerEd25519:
  186. return nano.NewPrivKeyLedgerEd25519Ed25519()
  187. default:
  188. err := errors.Errorf("Cannot generate keys for algorithm: %s", algo)
  189. return crypto.PrivKey{}, err
  190. }
  191. }
  192. func generateByType(typ byte, secret []byte) (crypto.PrivKey, error) {
  193. switch typ {
  194. case crypto.TypeEd25519:
  195. return generate(crypto.NameEd25519, secret)
  196. case crypto.TypeSecp256k1:
  197. return generate(crypto.NameSecp256k1, secret)
  198. case nano.TypeLedgerEd25519:
  199. return generate(nano.NameLedgerEd25519, secret)
  200. default:
  201. err := errors.Errorf("Cannot generate keys for algorithm: %X", typ)
  202. return crypto.PrivKey{}, err
  203. }
  204. }
  205. func pubName(name string) []byte {
  206. return []byte(fmt.Sprintf("%s.pub", name))
  207. }
  208. func privName(name string) []byte {
  209. return []byte(fmt.Sprintf("%s.priv", name))
  210. }
  211. func isPub(name []byte) bool {
  212. return strings.HasSuffix(string(name), ".pub")
  213. }