1 package cryptostore_test
9 "github.com/stretchr/testify/assert"
10 "github.com/stretchr/testify/require"
12 cmn "github.com/tendermint/tmlibs/common"
14 crypto "github.com/tendermint/go-crypto"
15 "github.com/tendermint/go-crypto/keys"
16 "github.com/tendermint/go-crypto/keys/cryptostore"
17 "github.com/tendermint/go-crypto/keys/storage/memstorage"
18 "github.com/tendermint/go-crypto/nano"
21 // TestKeyManagement makes sure we can manipulate these keys well
22 func TestKeyManagement(t *testing.T) {
23 assert, require := assert.New(t), require.New(t)
25 // make the storage with reasonable defaults
26 cstore := cryptostore.New(
27 cryptostore.SecretBox,
29 keys.MustLoadCodec("english"),
32 algo := crypto.NameEd25519
33 n1, n2, n3 := "personal", "business", "other"
34 p1, p2 := "1234", "really-secure!@#$"
37 l, err := cstore.List()
42 _, err = cstore.Get(n1)
44 i, _, err := cstore.Create(n1, p1, algo)
45 require.Equal(n1, i.Name)
47 _, _, err = cstore.Create(n2, p2, algo)
50 // we can get these keys
51 i2, err := cstore.Get(n2)
53 _, err = cstore.Get(n3)
56 // list shows them in order
57 keyS, err := cstore.List()
59 require.Equal(2, len(keyS))
60 // note these are in alphabetical order
61 assert.Equal(n2, keyS[0].Name)
62 assert.Equal(n1, keyS[1].Name)
63 assert.Equal(i2.PubKey, keyS[0].PubKey)
65 // deleting a key removes it
66 err = cstore.Delete("bad name", "foo")
68 err = cstore.Delete(n1, p1)
70 keyS, err = cstore.List()
72 assert.Equal(1, len(keyS))
73 _, err = cstore.Get(n1)
76 // make sure that it only signs with the right password
77 // tx := mock.NewSig([]byte("mytransactiondata"))
78 // err = cstore.Sign(n2, p1, tx)
80 // err = cstore.Sign(n2, p2, tx)
81 // assert.Nil(err, "%+v", err)
82 // sigs, err := tx.Signers()
83 // assert.Nil(err, "%+v", err)
84 // if assert.Equal(1, len(sigs)) {
85 // assert.Equal(i2.PubKey, sigs[0])
89 // TestSignVerify does some detailed checks on how we sign and validate
91 func TestSignVerify(t *testing.T) {
92 assert, require := assert.New(t), require.New(t)
94 // make the storage with reasonable defaults
95 cstore := cryptostore.New(
96 cryptostore.SecretBox,
98 keys.MustLoadCodec("english"),
100 algo := crypto.NameSecp256k1
102 n1, n2 := "some dude", "a dudette"
103 p1, p2 := "1234", "foobar"
105 // create two users and get their info
106 i1, _, err := cstore.Create(n1, p1, algo)
109 i2, _, err := cstore.Create(n2, p2, algo)
112 // let's try to sign some messages
113 d1 := []byte("my first message")
114 d2 := []byte("some other important info!")
116 // try signing both data with both keys...
117 s11 := keys.NewMockSignable(d1)
118 err = cstore.Sign(n1, p1, s11)
120 s12 := keys.NewMockSignable(d2)
121 err = cstore.Sign(n1, p1, s12)
123 s21 := keys.NewMockSignable(d1)
124 err = cstore.Sign(n2, p2, s21)
126 s22 := keys.NewMockSignable(d2)
127 err = cstore.Sign(n2, p2, s22)
130 // let's try to validate and make sure it only works when everything is proper
138 {i1.PubKey, d1, s11.Signature, true},
139 // change data, pubkey, or signature leads to fail
140 {i1.PubKey, d2, s11.Signature, false},
141 {i2.PubKey, d1, s11.Signature, false},
142 {i1.PubKey, d1, s21.Signature, false},
143 // make sure other successes
144 {i1.PubKey, d2, s12.Signature, true},
145 {i2.PubKey, d1, s21.Signature, true},
146 {i2.PubKey, d2, s22.Signature, true},
149 for i, tc := range cases {
150 valid := tc.key.VerifyBytes(tc.data, tc.sig)
151 assert.Equal(tc.valid, valid, "%d", i)
155 // TestSignWithLedger makes sure we have ledger compatibility with
158 // This test will only succeed with a ledger attached to the computer
159 // and the cosmos app open
160 func TestSignWithLedger(t *testing.T) {
161 assert, require := assert.New(t), require.New(t)
162 if os.Getenv("WITH_LEDGER") == "" {
163 t.Skip("Set WITH_LEDGER to run code on real ledger")
166 // make the storage with reasonable defaults
167 cstore := cryptostore.New(
168 cryptostore.SecretBox,
170 keys.MustLoadCodec("english"),
175 // create a nano user
176 c, _, err := cstore.Create(n, p, nano.NameLedgerEd25519)
177 require.Nil(err, "%+v", err)
178 assert.Equal(c.Name, n)
179 _, ok := c.PubKey.Unwrap().(nano.PubKeyLedgerEd25519)
182 // make sure we can get it back
183 info, err := cstore.Get(n)
184 require.Nil(err, "%+v", err)
185 assert.Equal(info.Name, n)
187 require.False(key.Empty())
188 require.True(key.Equals(c.PubKey))
190 // let's try to sign some messages
191 d1 := []byte("welcome to cosmos")
192 d2 := []byte("please turn on the app")
194 // try signing both data with the ledger...
195 s1 := keys.NewMockSignable(d1)
196 err = cstore.Sign(n, p, s1)
198 s2 := keys.NewMockSignable(d2)
199 err = cstore.Sign(n, p, s2)
202 // now, let's check those signatures work
203 assert.True(key.VerifyBytes(d1, s1.Signature))
204 assert.True(key.VerifyBytes(d2, s2.Signature))
205 // and mismatched signatures don't
206 assert.False(key.VerifyBytes(d1, s2.Signature))
209 func assertPassword(assert *assert.Assertions, cstore cryptostore.Manager, name, pass, badpass string) {
210 err := cstore.Update(name, badpass, pass)
212 err = cstore.Update(name, pass, pass)
213 assert.Nil(err, "%+v", err)
216 // TestImportUnencrypted tests accepting raw priv keys bytes as input
217 func TestImportUnencrypted(t *testing.T) {
218 require := require.New(t)
220 // make the storage with reasonable defaults
221 cstore := cryptostore.New(
222 cryptostore.SecretBox,
224 keys.MustLoadCodec("english"),
227 key, err := cryptostore.GenEd25519.Generate(cmn.RandBytes(16))
230 addr := key.PubKey().Address()
235 err = cstore.Import(name, pass, "", key.Bytes())
236 require.Nil(err, "%+v", err)
238 // make sure the address matches
239 info, err := cstore.Get(name)
240 require.Nil(err, "%+v", err)
241 require.EqualValues(addr, info.Address)
244 // TestAdvancedKeyManagement verifies update, import, export functionality
245 func TestAdvancedKeyManagement(t *testing.T) {
246 assert, require := assert.New(t), require.New(t)
248 // make the storage with reasonable defaults
249 cstore := cryptostore.New(
250 cryptostore.SecretBox,
252 keys.MustLoadCodec("english"),
255 algo := crypto.NameSecp256k1
256 n1, n2 := "old-name", "new name"
257 p1, p2, p3, pt := "1234", "foobar", "ding booms!", "really-secure!@#$"
259 // make sure key works with initial password
260 _, _, err := cstore.Create(n1, p1, algo)
261 require.Nil(err, "%+v", err)
262 assertPassword(assert, cstore, n1, p1, p2)
264 // update password requires the existing password
265 err = cstore.Update(n1, "jkkgkg", p2)
267 assertPassword(assert, cstore, n1, p1, p2)
269 // then it changes the password when correct
270 err = cstore.Update(n1, p1, p2)
272 // p2 is now the proper one!
273 assertPassword(assert, cstore, n1, p2, p1)
275 // exporting requires the proper name and passphrase
276 _, err = cstore.Export(n2, p2, pt)
278 _, err = cstore.Export(n1, p1, pt)
280 exported, err := cstore.Export(n1, p2, pt)
281 require.Nil(err, "%+v", err)
283 // import fails on bad transfer pass
284 err = cstore.Import(n2, p3, p2, exported)
288 // TestSeedPhrase verifies restoring from a seed phrase
289 func TestSeedPhrase(t *testing.T) {
290 assert, require := assert.New(t), require.New(t)
292 // make the storage with reasonable defaults
293 cstore := cryptostore.New(
294 cryptostore.SecretBox,
296 keys.MustLoadCodec("english"),
299 algo := crypto.NameEd25519
300 n1, n2 := "lost-key", "found-again"
301 p1, p2 := "1234", "foobar"
303 // make sure key works with initial password
304 info, seed, err := cstore.Create(n1, p1, algo)
305 require.Nil(err, "%+v", err)
306 assert.Equal(n1, info.Name)
307 assert.NotEmpty(seed)
309 // now, let us delete this key
310 err = cstore.Delete(n1, p1)
311 require.Nil(err, "%+v", err)
312 _, err = cstore.Get(n1)
315 // let us re-create it from the seed-phrase
316 newInfo, err := cstore.Recover(n2, p2, seed)
317 require.Nil(err, "%+v", err)
318 assert.Equal(n2, newInfo.Name)
319 assert.Equal(info.Address, newInfo.Address)
320 assert.Equal(info.PubKey, newInfo.PubKey)
324 // Select the encryption and storage for your cryptostore
325 cstore := cryptostore.New(
326 cryptostore.SecretBox,
327 // Note: use filestorage.New(dir) for real data
329 keys.MustLoadCodec("english"),
331 ed := crypto.NameEd25519
332 sec := crypto.NameSecp256k1
334 // Add keys and see they return in alphabetical order
335 bob, _, err := cstore.Create("Bob", "friend", ed)
337 // this should never happen
340 // return info here just like in List
341 fmt.Println(bob.Name)
343 cstore.Create("Alice", "secret", sec)
344 cstore.Create("Carl", "mitm", ed)
345 info, _ := cstore.List()
346 for _, i := range info {
350 // We need to use passphrase to generate a signature
351 tx := keys.NewMockSignable([]byte("deadbeef"))
352 err = cstore.Sign("Bob", "friend", tx)
354 fmt.Println("don't accept real passphrase")
357 // and we can validate the signature with publically available info
358 binfo, _ := cstore.Get("Bob")
359 if !binfo.PubKey.Equals(bob.PubKey) {
360 fmt.Println("Get and Create return different keys")
363 sigs, err := tx.Signers()
365 fmt.Println("badly signed")
366 } else if bytes.Equal(sigs[0].Bytes(), binfo.PubKey.Bytes()) {
367 fmt.Println("signed by Bob")
369 fmt.Println("signed by someone else")