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