keys_test.go raw
1 package keys
2
3 import (
4 "strconv"
5 "strings"
6 "testing"
7
8 "github.com/sirupsen/logrus"
9
10 "github.com/getAlby/hub/config"
11 "github.com/getAlby/hub/logger"
12 "github.com/getAlby/hub/tests/db"
13
14 "github.com/getAlby/go-nostr"
15 "github.com/stretchr/testify/assert"
16 "github.com/stretchr/testify/require"
17 "github.com/tyler-smith/go-bip32"
18 "github.com/tyler-smith/go-bip39"
19 )
20
21 func TestUseExistingMnemonic(t *testing.T) {
22 logger.Init(strconv.Itoa(int(logrus.DebugLevel)))
23 gormDb, err := db.NewDB(t)
24 require.NoError(t, err)
25 defer db.CloseDB(gormDb)
26
27 mnemonic := "thought turkey ask pottery head say catalog desk pledge elbow naive mimic"
28 unlockPassword := "123"
29
30 config, err := config.NewConfig(&config.AppConfig{}, gormDb)
31 require.NoError(t, err)
32 config.SetUpdate("Mnemonic", mnemonic, unlockPassword)
33
34 keys := NewKeys()
35 err = keys.Init(config, unlockPassword)
36 require.NoError(t, err)
37
38 mnemonicFromConfig, err := config.Get("Mnemonic", unlockPassword)
39 require.NoError(t, err)
40 require.Equal(t, mnemonic, mnemonicFromConfig)
41
42 // ensure backup key uses correct derivation path
43 derivedKeyFromKeys, err := keys.DeriveKey([]uint32{bip32.FirstHardenedChild})
44 require.NoError(t, err)
45
46 masterKey, err := bip32.NewMasterKey(bip39.NewSeed(mnemonic, ""))
47 assert.NoError(t, err)
48
49 appKey, err := masterKey.NewChildKey(bip32.FirstHardenedChild + 128029 /* 🐝 */)
50 assert.NoError(t, err)
51
52 encryptedChannelsBackupKey, err := appKey.NewChildKey(bip32.FirstHardenedChild)
53 assert.NoError(t, err)
54
55 assert.Equal(t, encryptedChannelsBackupKey.String(), derivedKeyFromKeys.String())
56
57 // get a wallet key for app ID 2, expect it is derived correctly
58 appWalletPrivateKey, err := keys.GetAppWalletKey(2)
59 require.NoError(t, err)
60 appWalletPubkey, err := nostr.GetPublicKey(appWalletPrivateKey)
61 require.NoError(t, err)
62
63 assert.Equal(t, "dd9e304d24f29f3481d5cf18a76c85ca3e95931aee3c997a27f267e975e72976", appWalletPubkey)
64 }
65
66 func TestGenerateNewMnemonic(t *testing.T) {
67 logger.Init(strconv.Itoa(int(logrus.DebugLevel)))
68 gormDb, err := db.NewDB(t)
69 require.NoError(t, err)
70 defer db.CloseDB(gormDb)
71
72 unlockPassword := "123"
73
74 config, err := config.NewConfig(&config.AppConfig{}, gormDb)
75 require.NoError(t, err)
76
77 keys := NewKeys()
78 err = keys.Init(config, unlockPassword)
79 require.NoError(t, err)
80
81 mnemonicFromConfig, err := config.Get("Mnemonic", unlockPassword)
82 require.NoError(t, err)
83
84 // expect a new 12-word mnemonic to be saved
85 assert.Equal(t, 12, len(strings.Split(mnemonicFromConfig, " ")))
86
87 // re-create keys, ensure same mnemonic is used
88 keys = NewKeys()
89 err = keys.Init(config, unlockPassword)
90 require.NoError(t, err)
91
92 mnemonicFromConfig2, err := config.Get("Mnemonic", unlockPassword)
93 require.NoError(t, err)
94 assert.Equal(t, mnemonicFromConfig, mnemonicFromConfig2)
95
96 // check derivation
97
98 derivedKeyFromKeys, err := keys.DeriveKey([]uint32{bip32.FirstHardenedChild})
99 require.NoError(t, err)
100
101 masterKey, err := bip32.NewMasterKey(bip39.NewSeed(mnemonicFromConfig, ""))
102 assert.NoError(t, err)
103
104 appKey, err := masterKey.NewChildKey(bip32.FirstHardenedChild + 128029 /* 🐝 */)
105 assert.NoError(t, err)
106
107 encryptedChannelsBackupKey, err := appKey.NewChildKey(bip32.FirstHardenedChild)
108 assert.NoError(t, err)
109
110 assert.Equal(t, encryptedChannelsBackupKey.String(), derivedKeyFromKeys.String())
111 }
112
113 func TestInit_WrongPasswordDoesNotOverwriteNostrKey(t *testing.T) {
114 logger.Init(strconv.Itoa(int(logrus.DebugLevel)))
115 gormDb, err := db.NewDB(t)
116 require.NoError(t, err)
117 defer db.CloseDB(gormDb)
118
119 unlockPassword := "correct"
120
121 cfg, err := config.NewConfig(&config.AppConfig{}, gormDb)
122 require.NoError(t, err)
123
124 // initialise keys under the correct password, storing an encrypted NostrSecretKey
125 keys := NewKeys()
126 err = keys.Init(cfg, unlockPassword)
127 require.NoError(t, err)
128
129 originalSecret, err := cfg.Get("NostrSecretKey", unlockPassword)
130 require.NoError(t, err)
131 require.NotEmpty(t, originalSecret)
132
133 // a wrong password must abort instead of mistaking the failed decrypt for
134 // "no key yet" and overwriting the stored key with a freshly generated one
135 keys2 := NewKeys()
136 err = keys2.Init(cfg, "wrong")
137 require.Error(t, err)
138
139 // the stored key, decrypted with the correct password, must be unchanged
140 secretAfter, err := cfg.Get("NostrSecretKey", unlockPassword)
141 require.NoError(t, err)
142 assert.Equal(t, originalSecret, secretAfter)
143 }
144
145 func TestGenerateSwapMnemonic(t *testing.T) {
146 logger.Init(strconv.Itoa(int(logrus.DebugLevel)))
147 gormDb, err := db.NewDB(t)
148 require.NoError(t, err)
149 defer db.CloseDB(gormDb)
150
151 mnemonic := "thought turkey ask pottery head say catalog desk pledge elbow naive mimic"
152 unlockPassword := "123"
153
154 config, err := config.NewConfig(&config.AppConfig{}, gormDb)
155 require.NoError(t, err)
156 config.SetUpdate("Mnemonic", mnemonic, unlockPassword)
157
158 keys := NewKeys()
159 err = keys.Init(config, unlockPassword)
160 require.NoError(t, err)
161
162 mnemonicFromConfig, err := config.Get("Mnemonic", unlockPassword)
163 require.NoError(t, err)
164 require.Equal(t, mnemonic, mnemonicFromConfig)
165
166 seed := bip39.NewSeed(mnemonicFromConfig, "")
167 masterKey, err := bip32.NewMasterKey(seed)
168 require.NoError(t, err)
169
170 swapMnemonic, err := keys.GenerateSwapMnemonic(masterKey)
171 require.NoError(t, err)
172
173 // this matches https://iancoleman.io/bip39/ -> check "Show BIP85" and set BIP85 Index to 128260
174 expectedSwapMnemonic := "truth cargo pluck prefer mosquito symptom review kitchen exile fit corn vault"
175 assert.Equal(t, expectedSwapMnemonic, swapMnemonic)
176 }
177