diff --git a/api/mnemonic.go b/api/mnemonic.go index f7cd2b8cf0081089434d084b83548725825a901c..fd3ce7259be248692591a48bc86f745fd0089e7c 100644 --- a/api/mnemonic.go +++ b/api/mnemonic.go @@ -14,9 +14,11 @@ import ( xxMnemonic "gitlab.com/xx_network/crypto/mnemonic" "gitlab.com/xx_network/primitives/utils" "golang.org/x/crypto/chacha20poly1305" + "path/filepath" + "strings" ) -const mnemonicFile = "/.recovery" +const mnemonicFile = ".recovery" // StoreSecretWithMnemonic creates a mnemonic and uses it to encrypt the secret. // This encrypted data saved in storage. @@ -24,12 +26,18 @@ func StoreSecretWithMnemonic(secret []byte, path string) (string, error) { // Use fastRNG for RNG ops (AES fortuna based RNG using system RNG) rng := fastRNG.NewStreamGenerator(12, 3, csprng.NewSystemRNG).GetStream() + // Ensure path is appended by filepath separator "/" + if !strings.HasSuffix(path, string(filepath.Separator)) { + path = path + string(filepath.Separator) + } + // Create a mnemonic mnemonic, err := xxMnemonic.GenerateMnemonic(rng, 32) if err != nil { return "", errors.Errorf("Failed to generate mnemonic: %v", err) } + // Decode mnemonic decodedMnemonic, err := xxMnemonic.DecodeMnemonic(mnemonic) if err != nil { return "", errors.Errorf("Failed to decode mnemonic: %v", err) @@ -42,7 +50,8 @@ func StoreSecretWithMnemonic(secret []byte, path string) (string, error) { } // Save encrypted secret to file - err = utils.WriteFileDef(path+mnemonicFile, ciphertext) + recoveryFile := path + mnemonicFile + err = utils.WriteFileDef(recoveryFile, ciphertext) if err != nil { return "", errors.Errorf("Failed to save mnemonic information to file") } @@ -53,16 +62,31 @@ func StoreSecretWithMnemonic(secret []byte, path string) (string, error) { // LoadSecretWithMnemonic loads the encrypted secret from storage and decrypts // the secret using the given mnemonic. func LoadSecretWithMnemonic(mnemonic, path string) (secret []byte, err error) { - data, err := utils.ReadFile(path + mnemonicFile) + // Ensure path is appended by filepath separator "/" + if !strings.HasSuffix(path, string(filepath.Separator)) { + path = path + string(filepath.Separator) + } + + // Ensure that the recovery file exists + recoveryFile := path + mnemonicFile + if !utils.Exists(recoveryFile) { + return nil, errors.Errorf("Recovery file does not exist. " + + "Did you properly set up recovery or provide an incorrect filepath?") + } + + // Read file from storage + data, err := utils.ReadFile(recoveryFile) if err != nil { return nil, errors.Errorf("Failed to load mnemonic information: %v", err) } + // Decode mnemonic decodedMnemonic, err := xxMnemonic.DecodeMnemonic(mnemonic) if err != nil { return nil, errors.Errorf("Failed to decode mnemonic: %v", err) } + // Decrypt the stored secret secret, err = decryptWithMnemonic(data, decodedMnemonic) if err != nil { return nil, errors.Errorf("Failed to decrypt secret: %v", err) diff --git a/api/mnemonic_test.go b/api/mnemonic_test.go index b9f7715be1f4421575347147cc32720a4a9cc5cd..66b2f13dfc8e3f297be051f1f58206c94d22c399 100644 --- a/api/mnemonic_test.go +++ b/api/mnemonic_test.go @@ -19,7 +19,7 @@ import ( func TestStoreSecretWithMnemonic(t *testing.T) { secret := []byte("test123") - storageDir := "ignore.1" + storageDir := "ignore.1/" mnemonic, err := StoreSecretWithMnemonic(secret, storageDir) if err != nil { t.Errorf("StoreSecretWithMnemonic error; %v", err) @@ -90,6 +90,12 @@ func TestLoadSecretWithMnemonic(t *testing.T) { t.Fatalf("Loaded secret does not match original data."+ "\n\tExpected: %v\n\tReceived: %v", secret, received) } + + _, err = LoadSecretWithMnemonic(mnemonic, "badDirectory") + if err == nil { + t.Fatalf("LoadSecretWithMnemonic should error when provided a path " + + "where a recovery file does not exist.") + } } // Prng is a PRNG that satisfies the csprng.Source interface. diff --git a/bindings/mnemonic.go b/bindings/mnemonic.go new file mode 100644 index 0000000000000000000000000000000000000000..95f91744f2800674303ebd12a6bf9a7284ca3c0a --- /dev/null +++ b/bindings/mnemonic.go @@ -0,0 +1 @@ +package bindings