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.

452 lines
12 KiB

7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
  1. package keys_test
  2. import (
  3. "fmt"
  4. "testing"
  5. "github.com/stretchr/testify/assert"
  6. "github.com/stretchr/testify/require"
  7. dbm "github.com/tendermint/tmlibs/db"
  8. "github.com/tendermint/go-crypto"
  9. "github.com/tendermint/go-crypto/keys"
  10. "github.com/tendermint/go-crypto/keys/words"
  11. )
  12. // TestKeyManagement makes sure we can manipulate these keys well
  13. func TestKeyManagement(t *testing.T) {
  14. // make the storage with reasonable defaults
  15. cstore := keys.New(
  16. dbm.NewMemDB(),
  17. words.MustLoadCodec("english"),
  18. )
  19. algo := keys.AlgoEd25519
  20. n1, n2, n3 := "personal", "business", "other"
  21. p1, p2 := "1234", "really-secure!@#$"
  22. // Check empty state
  23. l, err := cstore.List()
  24. require.Nil(t, err)
  25. assert.Empty(t, l)
  26. // create some keys
  27. _, err = cstore.Get(n1)
  28. assert.NotNil(t, err)
  29. i, _, err := cstore.CreateMnemonic(n1, p1, algo)
  30. require.Equal(t, n1, i.GetName())
  31. require.Nil(t, err)
  32. _, _, err = cstore.CreateMnemonic(n2, p2, algo)
  33. require.Nil(t, err)
  34. // we can get these keys
  35. i2, err := cstore.Get(n2)
  36. assert.NoError(t, err)
  37. _, err = cstore.Get(n3)
  38. assert.NotNil(t, err)
  39. // list shows them in order
  40. keyS, err := cstore.List()
  41. require.Nil(t, err)
  42. require.Equal(t, 2, len(keyS))
  43. // note these are in alphabetical order
  44. assert.Equal(t, n2, keyS[0].GetName())
  45. assert.Equal(t, n1, keyS[1].GetName())
  46. assert.Equal(t, i2.GetPubKey(), keyS[0].GetPubKey())
  47. // deleting a key removes it
  48. err = cstore.Delete("bad name", "foo")
  49. require.NotNil(t, err)
  50. err = cstore.Delete(n1, p1)
  51. require.Nil(t, err)
  52. keyS, err = cstore.List()
  53. require.Nil(t, err)
  54. assert.Equal(t, 1, len(keyS))
  55. _, err = cstore.Get(n1)
  56. assert.NotNil(t, err)
  57. // create an offline key
  58. o1 := "offline"
  59. priv1 := crypto.GenPrivKeyEd25519()
  60. pub1, err := priv1.PubKey()
  61. require.Nil(t, err)
  62. i, err = cstore.CreateOffline(o1, pub1)
  63. require.Nil(t, err)
  64. require.Equal(t, pub1, i.GetPubKey())
  65. require.Equal(t, o1, i.GetName())
  66. keyS, err = cstore.List()
  67. require.Equal(t, 2, len(keyS))
  68. // delete the offline key
  69. err = cstore.Delete(o1, "no")
  70. require.NotNil(t, err)
  71. err = cstore.Delete(o1, "yes")
  72. require.Nil(t, err)
  73. keyS, err = cstore.List()
  74. require.Equal(t, 1, len(keyS))
  75. // make sure that it only signs with the right password
  76. // tx := mock.NewSig([]byte("mytransactiondata"))
  77. // err = cstore.Sign(n2, p1, tx)
  78. // assert.NotNil(t, err)
  79. // err = cstore.Sign(n2, p2, tx)
  80. // assert.Nil(t, err, "%+v", err)
  81. // sigs, err := tx.Signers()
  82. // assert.Nil(t, err, "%+v", err)
  83. // if assert.Equal(t, 1, len(sigs)) {
  84. // assert.Equal(t, i2.PubKey, sigs[0])
  85. // }
  86. }
  87. // TestSignVerify does some detailed checks on how we sign and validate
  88. // signatures
  89. func TestSignVerify(t *testing.T) {
  90. // make the storage with reasonable defaults
  91. cstore := keys.New(
  92. dbm.NewMemDB(),
  93. words.MustLoadCodec("english"),
  94. )
  95. algo := keys.AlgoSecp256k1
  96. n1, n2, n3 := "some dude", "a dudette", "dude-ish"
  97. p1, p2, p3 := "1234", "foobar", "foobar"
  98. // create two users and get their info
  99. i1, _, err := cstore.CreateMnemonic(n1, p1, algo)
  100. require.Nil(t, err)
  101. i2, _, err := cstore.CreateMnemonic(n2, p2, algo)
  102. require.Nil(t, err)
  103. // Import a public key
  104. armor, err := cstore.ExportPubKey(n2)
  105. require.Nil(t, err)
  106. cstore.ImportPubKey(n3, armor)
  107. _, err = cstore.Get(n3)
  108. require.Nil(t, err)
  109. // let's try to sign some messages
  110. d1 := []byte("my first message")
  111. d2 := []byte("some other important info!")
  112. d3 := []byte("feels like I forgot something...")
  113. // try signing both data with both keys...
  114. s11, pub1, err := cstore.Sign(n1, p1, d1)
  115. require.Nil(t, err)
  116. require.Equal(t, i1.GetPubKey(), pub1)
  117. s12, pub1, err := cstore.Sign(n1, p1, d2)
  118. require.Nil(t, err)
  119. require.Equal(t, i1.GetPubKey(), pub1)
  120. s21, pub2, err := cstore.Sign(n2, p2, d1)
  121. require.Nil(t, err)
  122. require.Equal(t, i2.GetPubKey(), pub2)
  123. s22, pub2, err := cstore.Sign(n2, p2, d2)
  124. require.Nil(t, err)
  125. require.Equal(t, i2.GetPubKey(), pub2)
  126. // let's try to validate and make sure it only works when everything is proper
  127. cases := []struct {
  128. key crypto.PubKey
  129. data []byte
  130. sig crypto.Signature
  131. valid bool
  132. }{
  133. // proper matches
  134. {i1.GetPubKey(), d1, s11, true},
  135. // change data, pubkey, or signature leads to fail
  136. {i1.GetPubKey(), d2, s11, false},
  137. {i2.GetPubKey(), d1, s11, false},
  138. {i1.GetPubKey(), d1, s21, false},
  139. // make sure other successes
  140. {i1.GetPubKey(), d2, s12, true},
  141. {i2.GetPubKey(), d1, s21, true},
  142. {i2.GetPubKey(), d2, s22, true},
  143. }
  144. for i, tc := range cases {
  145. valid := tc.key.VerifyBytes(tc.data, tc.sig)
  146. assert.Equal(t, tc.valid, valid, "%d", i)
  147. }
  148. // Now try to sign data with a secret-less key
  149. _, _, err = cstore.Sign(n3, p3, d3)
  150. assert.NotNil(t, err)
  151. }
  152. /*
  153. // TestSignWithLedger makes sure we have ledger compatibility with
  154. // the crypto store.
  155. //
  156. // This test will only succeed with a ledger attached to the computer
  157. // and the cosmos app open
  158. func TestSignWithLedger(t *testing.T) {
  159. if os.Getenv("WITH_LEDGER") == "" {
  160. t.Skip("Set WITH_LEDGER to run code on real ledger")
  161. }
  162. // make the storage with reasonable defaults
  163. cstore := keys.New(
  164. dbm.NewMemDB(),
  165. words.MustLoadCodec("english"),
  166. )
  167. n := "nano-s"
  168. p := "hard2hack"
  169. // create a nano user
  170. c, _, err := cstore.Create(n, p, nano.KeyLedgerEd25519)
  171. require.Nil(t, err, "%+v", err)
  172. assert.Equal(t, c.Key, n)
  173. _, ok := c.PubKey.Unwrap().(nano.PubKeyLedgerEd25519)
  174. require.True(t, ok)
  175. // make sure we can get it back
  176. info, err := cstore.Get(n)
  177. require.Nil(t, err, "%+v", err)
  178. assert.Equal(t, info.Key, n)
  179. key := info.PubKey
  180. require.False(t ,key.Empty())
  181. require.True(t, key.Equals(c.PubKey))
  182. // let's try to sign some messages
  183. d1 := []byte("welcome to cosmos")
  184. d2 := []byte("please turn on the app")
  185. // try signing both data with the ledger...
  186. s1, pub, err := cstore.Sign(n, p, d1)
  187. require.Nil(t, err)
  188. require.Equal(t, info.PubKey, pub)
  189. s2, pub, err := cstore.Sign(n, p, d2)
  190. require.Nil(t, err)
  191. require.Equal(t, info.PubKey, pub)
  192. // now, let's check those signatures work
  193. assert.True(t, key.VerifyBytes(d1, s1))
  194. assert.True(t, key.VerifyBytes(d2, s2))
  195. // and mismatched signatures don't
  196. assert.False(t, key.VerifyBytes(d1, s2))
  197. }
  198. */
  199. func assertPassword(t *testing.T, cstore keys.Keybase, name, pass, badpass string) {
  200. err := cstore.Update(name, badpass, pass)
  201. assert.NotNil(t, err)
  202. err = cstore.Update(name, pass, pass)
  203. assert.Nil(t, err, "%+v", err)
  204. }
  205. // TestExportImport tests exporting and importing keys.
  206. func TestExportImport(t *testing.T) {
  207. // make the storage with reasonable defaults
  208. db := dbm.NewMemDB()
  209. cstore := keys.New(
  210. db,
  211. words.MustLoadCodec("english"),
  212. )
  213. info, _, err := cstore.CreateMnemonic("john", "passphrase", keys.AlgoEd25519)
  214. assert.NoError(t, err)
  215. assert.Equal(t, info.GetName(), "john")
  216. addr := info.GetPubKey().Address()
  217. john, err := cstore.Get("john")
  218. assert.NoError(t, err)
  219. assert.Equal(t, john.GetName(), "john")
  220. assert.Equal(t, john.GetPubKey().Address(), addr)
  221. armor, err := cstore.Export("john")
  222. assert.NoError(t, err)
  223. err = cstore.Import("john2", armor)
  224. assert.NoError(t, err)
  225. john2, err := cstore.Get("john2")
  226. assert.NoError(t, err)
  227. assert.Equal(t, john.GetPubKey().Address(), addr)
  228. assert.Equal(t, john.GetName(), "john")
  229. assert.Equal(t, john, john2)
  230. }
  231. func TestExportImportPubKey(t *testing.T) {
  232. // make the storage with reasonable defaults
  233. db := dbm.NewMemDB()
  234. cstore := keys.New(
  235. db,
  236. words.MustLoadCodec("english"),
  237. )
  238. // Create a private-public key pair and ensure consistency
  239. info, _, err := cstore.CreateMnemonic("john", "passphrase", keys.AlgoEd25519)
  240. assert.NoError(t, err)
  241. assert.Equal(t, info.GetName(), "john")
  242. addr := info.GetPubKey().Address()
  243. john, err := cstore.Get("john")
  244. assert.NoError(t, err)
  245. assert.Equal(t, john.GetName(), "john")
  246. assert.Equal(t, john.GetPubKey().Address(), addr)
  247. // Export the public key only
  248. armor, err := cstore.ExportPubKey("john")
  249. assert.NoError(t, err)
  250. // Import it under a different name
  251. err = cstore.ImportPubKey("john-pubkey-only", armor)
  252. assert.NoError(t, err)
  253. // Ensure consistency
  254. john2, err := cstore.Get("john-pubkey-only")
  255. assert.NoError(t, err)
  256. // Compare the public keys
  257. assert.True(t, john.GetPubKey().Equals(john2.GetPubKey()))
  258. // Ensure the original key hasn't changed
  259. john, err = cstore.Get("john")
  260. assert.NoError(t, err)
  261. assert.Equal(t, john.GetPubKey().Address(), addr)
  262. assert.Equal(t, john.GetName(), "john")
  263. // Ensure keys cannot be overwritten
  264. err = cstore.ImportPubKey("john-pubkey-only", armor)
  265. assert.NotNil(t, err)
  266. }
  267. // TestAdvancedKeyManagement verifies update, import, export functionality
  268. func TestAdvancedKeyManagement(t *testing.T) {
  269. // make the storage with reasonable defaults
  270. cstore := keys.New(
  271. dbm.NewMemDB(),
  272. words.MustLoadCodec("english"),
  273. )
  274. algo := keys.AlgoSecp256k1
  275. n1, n2 := "old-name", "new name"
  276. p1, p2 := "1234", "foobar"
  277. // make sure key works with initial password
  278. _, _, err := cstore.CreateMnemonic(n1, p1, algo)
  279. require.Nil(t, err, "%+v", err)
  280. assertPassword(t, cstore, n1, p1, p2)
  281. // update password requires the existing password
  282. err = cstore.Update(n1, "jkkgkg", p2)
  283. assert.NotNil(t, err)
  284. assertPassword(t, cstore, n1, p1, p2)
  285. // then it changes the password when correct
  286. err = cstore.Update(n1, p1, p2)
  287. assert.NoError(t, err)
  288. // p2 is now the proper one!
  289. assertPassword(t, cstore, n1, p2, p1)
  290. // exporting requires the proper name and passphrase
  291. _, err = cstore.Export(n1 + ".notreal")
  292. assert.NotNil(t, err)
  293. _, err = cstore.Export(" " + n1)
  294. assert.NotNil(t, err)
  295. _, err = cstore.Export(n1 + " ")
  296. assert.NotNil(t, err)
  297. _, err = cstore.Export("")
  298. assert.NotNil(t, err)
  299. exported, err := cstore.Export(n1)
  300. require.Nil(t, err, "%+v", err)
  301. // import succeeds
  302. err = cstore.Import(n2, exported)
  303. assert.NoError(t, err)
  304. // second import fails
  305. err = cstore.Import(n2, exported)
  306. assert.NotNil(t, err)
  307. }
  308. // TestSeedPhrase verifies restoring from a seed phrase
  309. func TestSeedPhrase(t *testing.T) {
  310. // make the storage with reasonable defaults
  311. cstore := keys.New(
  312. dbm.NewMemDB(),
  313. words.MustLoadCodec("english"),
  314. )
  315. algo := keys.AlgoEd25519
  316. n1, n2 := "lost-key", "found-again"
  317. p1, p2 := "1234", "foobar"
  318. // make sure key works with initial password
  319. info, seed, err := cstore.CreateMnemonic(n1, p1, algo)
  320. require.Nil(t, err, "%+v", err)
  321. assert.Equal(t, n1, info.GetName())
  322. assert.NotEmpty(t, seed)
  323. // now, let us delete this key
  324. err = cstore.Delete(n1, p1)
  325. require.Nil(t, err, "%+v", err)
  326. _, err = cstore.Get(n1)
  327. require.NotNil(t, err)
  328. // let us re-create it from the seed-phrase
  329. newInfo, err := cstore.Recover(n2, p2, seed)
  330. require.Nil(t, err, "%+v", err)
  331. assert.Equal(t, n2, newInfo.GetName())
  332. assert.Equal(t, info.GetPubKey().Address(), newInfo.GetPubKey().Address())
  333. assert.Equal(t, info.GetPubKey(), newInfo.GetPubKey())
  334. }
  335. func ExampleNew() {
  336. // Select the encryption and storage for your cryptostore
  337. cstore := keys.New(
  338. dbm.NewMemDB(),
  339. words.MustLoadCodec("english"),
  340. )
  341. ed := keys.AlgoEd25519
  342. sec := keys.AlgoSecp256k1
  343. // Add keys and see they return in alphabetical order
  344. bob, _, err := cstore.CreateMnemonic("Bob", "friend", ed)
  345. if err != nil {
  346. // this should never happen
  347. fmt.Println(err)
  348. } else {
  349. // return info here just like in List
  350. fmt.Println(bob.GetName())
  351. }
  352. cstore.CreateMnemonic("Alice", "secret", sec)
  353. cstore.CreateMnemonic("Carl", "mitm", ed)
  354. info, _ := cstore.List()
  355. for _, i := range info {
  356. fmt.Println(i.GetName())
  357. }
  358. // We need to use passphrase to generate a signature
  359. tx := []byte("deadbeef")
  360. sig, pub, err := cstore.Sign("Bob", "friend", tx)
  361. if err != nil {
  362. fmt.Println("don't accept real passphrase")
  363. }
  364. // and we can validate the signature with publically available info
  365. binfo, _ := cstore.Get("Bob")
  366. if !binfo.GetPubKey().Equals(bob.GetPubKey()) {
  367. fmt.Println("Get and Create return different keys")
  368. }
  369. if pub.Equals(binfo.GetPubKey()) {
  370. fmt.Println("signed by Bob")
  371. }
  372. if !pub.VerifyBytes(tx, sig) {
  373. fmt.Println("invalid signature")
  374. }
  375. // Output:
  376. // Bob
  377. // Alice
  378. // Bob
  379. // Carl
  380. // signed by Bob
  381. }