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.

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