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.

378 lines
9.8 KiB

  1. package cryptostore_test
  2. import (
  3. "bytes"
  4. "fmt"
  5. "os"
  6. "testing"
  7. "github.com/stretchr/testify/assert"
  8. "github.com/stretchr/testify/require"
  9. cmn "github.com/tendermint/tmlibs/common"
  10. crypto "github.com/tendermint/go-crypto"
  11. "github.com/tendermint/go-crypto/keys"
  12. "github.com/tendermint/go-crypto/keys/cryptostore"
  13. "github.com/tendermint/go-crypto/keys/storage/memstorage"
  14. "github.com/tendermint/go-crypto/nano"
  15. )
  16. // TestKeyManagement makes sure we can manipulate these keys well
  17. func TestKeyManagement(t *testing.T) {
  18. assert, require := assert.New(t), require.New(t)
  19. // make the storage with reasonable defaults
  20. cstore := cryptostore.New(
  21. cryptostore.SecretBox,
  22. memstorage.New(),
  23. keys.MustLoadCodec("english"),
  24. )
  25. algo := crypto.NameEd25519
  26. n1, n2, n3 := "personal", "business", "other"
  27. p1, p2 := "1234", "really-secure!@#$"
  28. // Check empty state
  29. l, err := cstore.List()
  30. require.Nil(err)
  31. assert.Empty(l)
  32. // create some keys
  33. _, err = cstore.Get(n1)
  34. assert.NotNil(err)
  35. i, _, err := cstore.Create(n1, p1, algo)
  36. require.Equal(n1, i.Name)
  37. require.Nil(err)
  38. _, _, err = cstore.Create(n2, p2, algo)
  39. require.Nil(err)
  40. // we can get these keys
  41. i2, err := cstore.Get(n2)
  42. assert.Nil(err)
  43. _, err = cstore.Get(n3)
  44. assert.NotNil(err)
  45. // list shows them in order
  46. keyS, err := cstore.List()
  47. require.Nil(err)
  48. require.Equal(2, len(keyS))
  49. // note these are in alphabetical order
  50. assert.Equal(n2, keyS[0].Name)
  51. assert.Equal(n1, keyS[1].Name)
  52. assert.Equal(i2.PubKey, keyS[0].PubKey)
  53. // deleting a key removes it
  54. err = cstore.Delete("bad name", "foo")
  55. require.NotNil(err)
  56. err = cstore.Delete(n1, p1)
  57. require.Nil(err)
  58. keyS, err = cstore.List()
  59. require.Nil(err)
  60. assert.Equal(1, len(keyS))
  61. _, err = cstore.Get(n1)
  62. assert.NotNil(err)
  63. // make sure that it only signs with the right password
  64. // tx := mock.NewSig([]byte("mytransactiondata"))
  65. // err = cstore.Sign(n2, p1, tx)
  66. // assert.NotNil(err)
  67. // err = cstore.Sign(n2, p2, tx)
  68. // assert.Nil(err, "%+v", err)
  69. // sigs, err := tx.Signers()
  70. // assert.Nil(err, "%+v", err)
  71. // if assert.Equal(1, len(sigs)) {
  72. // assert.Equal(i2.PubKey, sigs[0])
  73. // }
  74. }
  75. // TestSignVerify does some detailed checks on how we sign and validate
  76. // signatures
  77. func TestSignVerify(t *testing.T) {
  78. assert, require := assert.New(t), require.New(t)
  79. // make the storage with reasonable defaults
  80. cstore := cryptostore.New(
  81. cryptostore.SecretBox,
  82. memstorage.New(),
  83. keys.MustLoadCodec("english"),
  84. )
  85. algo := crypto.NameSecp256k1
  86. n1, n2 := "some dude", "a dudette"
  87. p1, p2 := "1234", "foobar"
  88. // create two users and get their info
  89. i1, _, err := cstore.Create(n1, p1, algo)
  90. require.Nil(err)
  91. i2, _, err := cstore.Create(n2, p2, algo)
  92. require.Nil(err)
  93. // let's try to sign some messages
  94. d1 := []byte("my first message")
  95. d2 := []byte("some other important info!")
  96. // try signing both data with both keys...
  97. s11 := keys.NewMockSignable(d1)
  98. err = cstore.Sign(n1, p1, s11)
  99. require.Nil(err)
  100. s12 := keys.NewMockSignable(d2)
  101. err = cstore.Sign(n1, p1, s12)
  102. require.Nil(err)
  103. s21 := keys.NewMockSignable(d1)
  104. err = cstore.Sign(n2, p2, s21)
  105. require.Nil(err)
  106. s22 := keys.NewMockSignable(d2)
  107. err = cstore.Sign(n2, p2, s22)
  108. require.Nil(err)
  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.Signature, true},
  118. // change data, pubkey, or signature leads to fail
  119. {i1.PubKey, d2, s11.Signature, false},
  120. {i2.PubKey, d1, s11.Signature, false},
  121. {i1.PubKey, d1, s21.Signature, false},
  122. // make sure other successes
  123. {i1.PubKey, d2, s12.Signature, true},
  124. {i2.PubKey, d1, s21.Signature, true},
  125. {i2.PubKey, d2, s22.Signature, true},
  126. }
  127. for i, tc := range cases {
  128. valid := tc.key.VerifyBytes(tc.data, tc.sig)
  129. assert.Equal(tc.valid, valid, "%d", i)
  130. }
  131. }
  132. // TestSignWithLedger makes sure we have ledger compatibility with
  133. // the crypto store.
  134. //
  135. // This test will only succeed with a ledger attached to the computer
  136. // and the cosmos app open
  137. func TestSignWithLedger(t *testing.T) {
  138. assert, require := assert.New(t), require.New(t)
  139. if os.Getenv("WITH_LEDGER") == "" {
  140. t.Skip("Set WITH_LEDGER to run code on real ledger")
  141. }
  142. // make the storage with reasonable defaults
  143. cstore := cryptostore.New(
  144. cryptostore.SecretBox,
  145. memstorage.New(),
  146. keys.MustLoadCodec("english"),
  147. )
  148. n := "nano-s"
  149. p := "hard2hack"
  150. // create a nano user
  151. c, _, err := cstore.Create(n, p, nano.NameLedgerEd25519)
  152. require.Nil(err, "%+v", err)
  153. assert.Equal(c.Name, n)
  154. _, ok := c.PubKey.Unwrap().(nano.PubKeyLedgerEd25519)
  155. require.True(ok)
  156. // make sure we can get it back
  157. info, err := cstore.Get(n)
  158. require.Nil(err, "%+v", err)
  159. assert.Equal(info.Name, n)
  160. key := info.PubKey
  161. require.False(key.Empty())
  162. require.True(key.Equals(c.PubKey))
  163. // let's try to sign some messages
  164. d1 := []byte("welcome to cosmos")
  165. d2 := []byte("please turn on the app")
  166. // try signing both data with the ledger...
  167. s1 := keys.NewMockSignable(d1)
  168. err = cstore.Sign(n, p, s1)
  169. require.Nil(err)
  170. s2 := keys.NewMockSignable(d2)
  171. err = cstore.Sign(n, p, s2)
  172. require.Nil(err)
  173. // now, let's check those signatures work
  174. assert.True(key.VerifyBytes(d1, s1.Signature))
  175. assert.True(key.VerifyBytes(d2, s2.Signature))
  176. // and mismatched signatures don't
  177. assert.False(key.VerifyBytes(d1, s2.Signature))
  178. }
  179. func assertPassword(assert *assert.Assertions, cstore cryptostore.Manager, name, pass, badpass string) {
  180. err := cstore.Update(name, badpass, pass)
  181. assert.NotNil(err)
  182. err = cstore.Update(name, pass, pass)
  183. assert.Nil(err, "%+v", err)
  184. }
  185. // TestImportUnencrypted tests accepting raw priv keys bytes as input
  186. func TestImportUnencrypted(t *testing.T) {
  187. require := require.New(t)
  188. // make the storage with reasonable defaults
  189. cstore := cryptostore.New(
  190. cryptostore.SecretBox,
  191. memstorage.New(),
  192. keys.MustLoadCodec("english"),
  193. )
  194. key, err := cryptostore.GenEd25519.Generate(cmn.RandBytes(16))
  195. require.NoError(err)
  196. addr := key.PubKey().Address()
  197. name := "john"
  198. pass := "top-secret"
  199. // import raw bytes
  200. err = cstore.Import(name, pass, "", key.Bytes())
  201. require.Nil(err, "%+v", err)
  202. // make sure the address matches
  203. info, err := cstore.Get(name)
  204. require.Nil(err, "%+v", err)
  205. require.EqualValues(addr, info.Address)
  206. }
  207. // TestAdvancedKeyManagement verifies update, import, export functionality
  208. func TestAdvancedKeyManagement(t *testing.T) {
  209. assert, require := assert.New(t), require.New(t)
  210. // make the storage with reasonable defaults
  211. cstore := cryptostore.New(
  212. cryptostore.SecretBox,
  213. memstorage.New(),
  214. keys.MustLoadCodec("english"),
  215. )
  216. algo := crypto.NameSecp256k1
  217. n1, n2 := "old-name", "new name"
  218. p1, p2, p3, pt := "1234", "foobar", "ding booms!", "really-secure!@#$"
  219. // make sure key works with initial password
  220. _, _, err := cstore.Create(n1, p1, algo)
  221. require.Nil(err, "%+v", err)
  222. assertPassword(assert, cstore, n1, p1, p2)
  223. // update password requires the existing password
  224. err = cstore.Update(n1, "jkkgkg", p2)
  225. assert.NotNil(err)
  226. assertPassword(assert, cstore, n1, p1, p2)
  227. // then it changes the password when correct
  228. err = cstore.Update(n1, p1, p2)
  229. assert.Nil(err)
  230. // p2 is now the proper one!
  231. assertPassword(assert, cstore, n1, p2, p1)
  232. // exporting requires the proper name and passphrase
  233. _, err = cstore.Export(n2, p2, pt)
  234. assert.NotNil(err)
  235. _, err = cstore.Export(n1, p1, pt)
  236. assert.NotNil(err)
  237. exported, err := cstore.Export(n1, p2, pt)
  238. require.Nil(err, "%+v", err)
  239. // import fails on bad transfer pass
  240. err = cstore.Import(n2, p3, p2, exported)
  241. assert.NotNil(err)
  242. }
  243. // TestSeedPhrase verifies restoring from a seed phrase
  244. func TestSeedPhrase(t *testing.T) {
  245. assert, require := assert.New(t), require.New(t)
  246. // make the storage with reasonable defaults
  247. cstore := cryptostore.New(
  248. cryptostore.SecretBox,
  249. memstorage.New(),
  250. keys.MustLoadCodec("english"),
  251. )
  252. algo := crypto.NameEd25519
  253. n1, n2 := "lost-key", "found-again"
  254. p1, p2 := "1234", "foobar"
  255. // make sure key works with initial password
  256. info, seed, err := cstore.Create(n1, p1, algo)
  257. require.Nil(err, "%+v", err)
  258. assert.Equal(n1, info.Name)
  259. assert.NotEmpty(seed)
  260. // now, let us delete this key
  261. err = cstore.Delete(n1, p1)
  262. require.Nil(err, "%+v", err)
  263. _, err = cstore.Get(n1)
  264. require.NotNil(err)
  265. // let us re-create it from the seed-phrase
  266. newInfo, err := cstore.Recover(n2, p2, seed)
  267. require.Nil(err, "%+v", err)
  268. assert.Equal(n2, newInfo.Name)
  269. assert.Equal(info.Address, newInfo.Address)
  270. assert.Equal(info.PubKey, newInfo.PubKey)
  271. }
  272. func ExampleNew() {
  273. // Select the encryption and storage for your cryptostore
  274. cstore := cryptostore.New(
  275. cryptostore.SecretBox,
  276. // Note: use filestorage.New(dir) for real data
  277. memstorage.New(),
  278. keys.MustLoadCodec("english"),
  279. )
  280. ed := crypto.NameEd25519
  281. sec := crypto.NameSecp256k1
  282. // Add keys and see they return in alphabetical order
  283. bob, _, err := cstore.Create("Bob", "friend", ed)
  284. if err != nil {
  285. // this should never happen
  286. fmt.Println(err)
  287. } else {
  288. // return info here just like in List
  289. fmt.Println(bob.Name)
  290. }
  291. cstore.Create("Alice", "secret", sec)
  292. cstore.Create("Carl", "mitm", ed)
  293. info, _ := cstore.List()
  294. for _, i := range info {
  295. fmt.Println(i.Name)
  296. }
  297. // We need to use passphrase to generate a signature
  298. tx := keys.NewMockSignable([]byte("deadbeef"))
  299. err = cstore.Sign("Bob", "friend", tx)
  300. if err != nil {
  301. fmt.Println("don't accept real passphrase")
  302. }
  303. // and we can validate the signature with publically available info
  304. binfo, _ := cstore.Get("Bob")
  305. if !binfo.PubKey.Equals(bob.PubKey) {
  306. fmt.Println("Get and Create return different keys")
  307. }
  308. sigs, err := tx.Signers()
  309. if err != nil {
  310. fmt.Println("badly signed")
  311. } else if bytes.Equal(sigs[0].Bytes(), binfo.PubKey.Bytes()) {
  312. fmt.Println("signed by Bob")
  313. } else {
  314. fmt.Println("signed by someone else")
  315. }
  316. // Output:
  317. // Bob
  318. // Alice
  319. // Bob
  320. // Carl
  321. // signed by Bob
  322. }