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.
611 lines
19 KiB
611 lines
19 KiB
package aezeed |
|
|
|
import ( |
|
"bytes" |
|
"math/rand" |
|
"testing" |
|
"testing/quick" |
|
"time" |
|
) |
|
|
|
// TestVector defines the values that are used to create a fully initialized |
|
// aezeed mnemonic seed and the expected values that should be calculated. |
|
type TestVector struct { |
|
version uint8 |
|
time time.Time |
|
entropy [EntropySize]byte |
|
salt [saltSize]byte |
|
password []byte |
|
expectedMnemonic [NumMnemonicWords]string |
|
expectedBirthday uint16 |
|
} |
|
|
|
var ( |
|
testEntropy = [EntropySize]byte{ |
|
0x81, 0xb6, 0x37, 0xd8, |
|
0x63, 0x59, 0xe6, 0x96, |
|
0x0d, 0xe7, 0x95, 0xe4, |
|
0x1e, 0x0b, 0x4c, 0xfd, |
|
} |
|
testSalt = [saltSize]byte{ |
|
0x73, 0x61, 0x6c, 0x74, 0x31, // equal to "salt1" |
|
} |
|
version0TestVectors = []TestVector{ |
|
{ |
|
version: 0, |
|
time: BitcoinGenesisDate, |
|
entropy: testEntropy, |
|
salt: testSalt, |
|
password: []byte{}, |
|
expectedMnemonic: [NumMnemonicWords]string{ |
|
"ability", "liquid", "travel", "stem", "barely", "drastic", |
|
"pact", "cupboard", "apple", "thrive", "morning", "oak", |
|
"feature", "tissue", "couch", "old", "math", "inform", |
|
"success", "suggest", "drink", "motion", "know", "royal", |
|
}, |
|
expectedBirthday: 0, |
|
}, |
|
{ |
|
version: 0, |
|
time: time.Unix(1521799345, 0), // 03/23/2018 @ 10:02am (UTC) |
|
entropy: testEntropy, |
|
salt: testSalt, |
|
password: []byte("!very_safe_55345_password*"), |
|
expectedMnemonic: [NumMnemonicWords]string{ |
|
"able", "tree", "stool", "crush", "transfer", "cloud", |
|
"cross", "three", "profit", "outside", "hen", "citizen", |
|
"plate", "ride", "require", "leg", "siren", "drum", |
|
"success", "suggest", "drink", "require", "fiscal", "upgrade", |
|
}, |
|
expectedBirthday: 3365, |
|
}, |
|
} |
|
) |
|
|
|
func assertCipherSeedEqual(t *testing.T, cipherSeed *CipherSeed, |
|
cipherSeed2 *CipherSeed) { |
|
|
|
if cipherSeed.InternalVersion != cipherSeed2.InternalVersion { |
|
t.Fatalf("mismatched versions: expected %v, got %v", |
|
cipherSeed.InternalVersion, cipherSeed2.InternalVersion) |
|
} |
|
if cipherSeed.Birthday != cipherSeed2.Birthday { |
|
t.Fatalf("mismatched birthday: expected %v, got %v", |
|
cipherSeed.Birthday, cipherSeed2.Birthday) |
|
} |
|
if cipherSeed.Entropy != cipherSeed2.Entropy { |
|
t.Fatalf("mismatched versions: expected %x, got %x", |
|
cipherSeed.Entropy[:], cipherSeed2.Entropy[:]) |
|
} |
|
} |
|
|
|
// TestAezeedVersion0TestVectors tests some fixed test vector values against |
|
// the expected mnemonic words. |
|
func TestAezeedVersion0TestVectors(t *testing.T) { |
|
t.Parallel() |
|
|
|
// To minimize the number of tests that need to be run, |
|
// go through all test vectors in the same test and also check |
|
// the birthday calculation while we're at it. |
|
for _, v := range version0TestVectors { |
|
// First, we create new cipher seed with the given values |
|
// from the test vector. |
|
cipherSeed, err := New(v.version, &v.entropy, v.time) |
|
if err != nil { |
|
t.Fatalf("unable to create seed: %v", err) |
|
} |
|
|
|
// Then we need to set the salt to the pre-defined value, otherwise |
|
// we'll end up with randomness in our mnemonics. |
|
cipherSeed.salt = testSalt |
|
|
|
// Now that the seed has been created, we'll attempt to convert it to a |
|
// valid mnemonic. |
|
mnemonic, err := cipherSeed.ToMnemonic(v.password) |
|
if err != nil { |
|
t.Fatalf("unable to create mnemonic: %v", err) |
|
} |
|
|
|
// Finally we compare the generated mnemonic and birthday to the |
|
// expected value. |
|
if mnemonic != v.expectedMnemonic { |
|
t.Fatalf("mismatched mnemonic: expected %s, got %s", |
|
v.expectedMnemonic, mnemonic) |
|
} |
|
if cipherSeed.Birthday != v.expectedBirthday { |
|
t.Fatalf("mismatched birthday: expected %v, got %v", |
|
v.expectedBirthday, cipherSeed.Birthday) |
|
} |
|
} |
|
} |
|
|
|
// TestEmptyPassphraseDerivation tests that the aezeed scheme is able to derive |
|
// a proper mnemonic, and decipher that mnemonic when the user uses an empty |
|
// passphrase. |
|
func TestEmptyPassphraseDerivation(t *testing.T) { |
|
t.Parallel() |
|
|
|
// Our empty passphrase... |
|
pass := []byte{} |
|
|
|
// We'll now create a new cipher seed with an internal version of zero |
|
// to simulate a wallet that just adopted the scheme. |
|
cipherSeed, err := New(0, &testEntropy, time.Now()) |
|
if err != nil { |
|
t.Fatalf("unable to create seed: %v", err) |
|
} |
|
|
|
// Now that the seed has been created, we'll attempt to convert it to a |
|
// valid mnemonic. |
|
mnemonic, err := cipherSeed.ToMnemonic(pass) |
|
if err != nil { |
|
t.Fatalf("unable to create mnemonic: %v", err) |
|
} |
|
|
|
// Next, we'll try to decrypt the mnemonic with the passphrase that we |
|
// used. |
|
cipherSeed2, err := mnemonic.ToCipherSeed(pass) |
|
if err != nil { |
|
t.Fatalf("unable to decrypt mnemonic: %v", err) |
|
} |
|
|
|
// Finally, we'll ensure that the uncovered cipher seed matches |
|
// precisely. |
|
assertCipherSeedEqual(t, cipherSeed, cipherSeed2) |
|
} |
|
|
|
// TestManualEntropyGeneration tests that if the user doesn't provide a source |
|
// of entropy, then we do so ourselves. |
|
func TestManualEntropyGeneration(t *testing.T) { |
|
t.Parallel() |
|
|
|
// Our empty passphrase... |
|
pass := []byte{} |
|
|
|
// We'll now create a new cipher seed with an internal version of zero |
|
// to simulate a wallet that just adopted the scheme. |
|
cipherSeed, err := New(0, nil, time.Now()) |
|
if err != nil { |
|
t.Fatalf("unable to create seed: %v", err) |
|
} |
|
|
|
// Now that the seed has been created, we'll attempt to convert it to a |
|
// valid mnemonic. |
|
mnemonic, err := cipherSeed.ToMnemonic(pass) |
|
if err != nil { |
|
t.Fatalf("unable to create mnemonic: %v", err) |
|
} |
|
|
|
// Next, we'll try to decrypt the mnemonic with the passphrase that we |
|
// used. |
|
cipherSeed2, err := mnemonic.ToCipherSeed(pass) |
|
if err != nil { |
|
t.Fatalf("unable to decrypt mnemonic: %v", err) |
|
} |
|
|
|
// Finally, we'll ensure that the uncovered cipher seed matches |
|
// precisely. |
|
assertCipherSeedEqual(t, cipherSeed, cipherSeed2) |
|
} |
|
|
|
// TestInvalidPassphraseRejection tests if a caller attempts to use the |
|
// incorrect passprhase for an enciphered seed, then the proper error is |
|
// returned. |
|
func TestInvalidPassphraseRejection(t *testing.T) { |
|
t.Parallel() |
|
|
|
// First, we'll generate a new cipher seed with a test passphrase. |
|
pass := []byte("test") |
|
cipherSeed, err := New(0, &testEntropy, time.Now()) |
|
if err != nil { |
|
t.Fatalf("unable to create seed: %v", err) |
|
} |
|
|
|
// Now that we have our cipher seed, we'll encipher it and request a |
|
// mnemonic that we can use to recover later. |
|
mnemonic, err := cipherSeed.ToMnemonic(pass) |
|
if err != nil { |
|
t.Fatalf("unable to create mnemonic: %v", err) |
|
} |
|
|
|
// If we try to decipher with the wrong passphrase, we should get the |
|
// proper error. |
|
wrongPass := []byte("kek") |
|
if _, err := mnemonic.ToCipherSeed(wrongPass); err != ErrInvalidPass { |
|
t.Fatalf("expected ErrInvalidPass, instead got %v", err) |
|
} |
|
} |
|
|
|
// TestRawEncipherDecipher tests that callers are able to use the raw methods |
|
// to map between ciphertext and the raw plaintext deciphered seed. |
|
func TestRawEncipherDecipher(t *testing.T) { |
|
t.Parallel() |
|
|
|
// First, we'll generate a new cipher seed with a test passphrase. |
|
pass := []byte("test") |
|
cipherSeed, err := New(0, &testEntropy, time.Now()) |
|
if err != nil { |
|
t.Fatalf("unable to create seed: %v", err) |
|
} |
|
|
|
// With the cipherseed obtained, we'll now use the raw encipher method |
|
// to obtain our final cipher text. |
|
cipherText, err := cipherSeed.Encipher(pass) |
|
if err != nil { |
|
t.Fatalf("unable to encipher seed: %v", err) |
|
} |
|
|
|
mnemonic, err := cipherTextToMnemonic(cipherText) |
|
if err != nil { |
|
t.Fatalf("unable to create mnemonic: %v", err) |
|
} |
|
|
|
// Now that we have the ciphertext (mapped to the mnemonic), we'll |
|
// attempt to decipher it raw using the user's passphrase. |
|
plainSeedBytes, err := mnemonic.Decipher(pass) |
|
if err != nil { |
|
t.Fatalf("unable to decipher: %v", err) |
|
} |
|
|
|
// If we deserialize the plaintext seed bytes, it should exactly match |
|
// the original cipher seed. |
|
var newSeed CipherSeed |
|
err = newSeed.decode(bytes.NewReader(plainSeedBytes[:])) |
|
if err != nil { |
|
t.Fatalf("unable to decode cipher seed: %v", err) |
|
} |
|
|
|
assertCipherSeedEqual(t, cipherSeed, &newSeed) |
|
} |
|
|
|
// TestInvalidExternalVersion tests that if we present a ciphertext with the |
|
// incorrect version to decipherCipherSeed, then it fails with the expected |
|
// error. |
|
func TestInvalidExternalVersion(t *testing.T) { |
|
t.Parallel() |
|
|
|
// First, we'll generate a new cipher seed. |
|
cipherSeed, err := New(0, &testEntropy, time.Now()) |
|
if err != nil { |
|
t.Fatalf("unable to create seed: %v", err) |
|
} |
|
|
|
// With the cipherseed obtained, we'll now use the raw encipher method |
|
// to obtain our final cipher text. |
|
pass := []byte("newpasswhodis") |
|
cipherText, err := cipherSeed.Encipher(pass) |
|
if err != nil { |
|
t.Fatalf("unable to encipher seed: %v", err) |
|
} |
|
|
|
// Now that we have the cipher text, we'll modify the first byte to be |
|
// an invalid version. |
|
cipherText[0] = 44 |
|
|
|
// With the version swapped, if we try to decipher it, (no matter the |
|
// passphrase), it should fail. |
|
_, err = decipherCipherSeed(cipherText, []byte("kek")) |
|
if err != ErrIncorrectVersion { |
|
t.Fatalf("wrong error: expected ErrIncorrectVersion, "+ |
|
"got %v", err) |
|
} |
|
} |
|
|
|
// TestChangePassphrase tests that we're able to generate a cipher seed, then |
|
// change the password. If we attempt to decipher the new enciphered seed, then |
|
// we should get the exact same seed back. |
|
func TestChangePassphrase(t *testing.T) { |
|
t.Parallel() |
|
|
|
// First, we'll generate a new cipher seed with a test passphrase. |
|
pass := []byte("test") |
|
cipherSeed, err := New(0, &testEntropy, time.Now()) |
|
if err != nil { |
|
t.Fatalf("unable to create seed: %v", err) |
|
} |
|
|
|
// Now that we have our cipher seed, we'll encipher it and request a |
|
// mnemonic that we can use to recover later. |
|
mnemonic, err := cipherSeed.ToMnemonic(pass) |
|
if err != nil { |
|
t.Fatalf("unable to create mnemonic: %v", err) |
|
} |
|
|
|
// Now that have the mnemonic, we'll attempt to re-encipher the |
|
// passphrase in order to get a brand new mnemonic. |
|
newPass := []byte("strongerpassyeh!") |
|
newmnemonic, err := mnemonic.ChangePass(pass, newPass) |
|
if err != nil { |
|
t.Fatalf("unable to change passphrase: %v", err) |
|
} |
|
|
|
// We'll now attempt to decipher the new mnemonic using the new |
|
// passphrase to arrive at (what should be) the original cipher seed. |
|
newCipherSeed, err := newmnemonic.ToCipherSeed(newPass) |
|
if err != nil { |
|
t.Fatalf("unable to decipher cipher seed: %v", err) |
|
} |
|
|
|
// Now that we have the cipher seed, we'll verify that the plaintext |
|
// seed matches *identically*. |
|
assertCipherSeedEqual(t, cipherSeed, newCipherSeed) |
|
} |
|
|
|
// TestChangePassphraseWrongPass tests that if we have a valid enciphered |
|
// cipherseed, but then try to change the password with the *wrong* password, |
|
// then we get an error. |
|
func TestChangePassphraseWrongPass(t *testing.T) { |
|
t.Parallel() |
|
|
|
// First, we'll generate a new cipher seed with a test passphrase. |
|
pass := []byte("test") |
|
cipherSeed, err := New(0, &testEntropy, time.Now()) |
|
if err != nil { |
|
t.Fatalf("unable to create seed: %v", err) |
|
} |
|
|
|
// Now that we have our cipher seed, we'll encipher it and request a |
|
// mnemonic that we can use to recover later. |
|
mnemonic, err := cipherSeed.ToMnemonic(pass) |
|
if err != nil { |
|
t.Fatalf("unable to create mnemonic: %v", err) |
|
} |
|
|
|
// Now that have the mnemonic, we'll attempt to re-encipher the |
|
// passphrase in order to get a brand new mnemonic. However, we'll be |
|
// using the *wrong* passphrase. This should result in an |
|
// ErrInvalidPass error. |
|
wrongPass := []byte("kek") |
|
newPass := []byte("strongerpassyeh!") |
|
_, err = mnemonic.ChangePass(wrongPass, newPass) |
|
if err != ErrInvalidPass { |
|
t.Fatalf("expected ErrInvalidPass, instead got %v", err) |
|
} |
|
} |
|
|
|
// TestMnemonicEncoding uses quickcheck like property based testing to ensure |
|
// that we're always able to fully recover the original byte stream encoded |
|
// into the mnemonic phrase. |
|
func TestMnemonicEncoding(t *testing.T) { |
|
t.Parallel() |
|
|
|
// mainScenario is the main driver of our property based test. We'll |
|
// ensure that given a random byte string of length 33 bytes, if we |
|
// convert that to the mnemonic, then we should be able to reverse the |
|
// conversion. |
|
mainScenario := func(cipherSeedBytes [EncipheredCipherSeedSize]byte) bool { |
|
mnemonic, err := cipherTextToMnemonic(cipherSeedBytes) |
|
if err != nil { |
|
t.Fatalf("unable to map cipher text: %v", err) |
|
return false |
|
} |
|
|
|
newCipher := mnemonicToCipherText(&mnemonic) |
|
|
|
if newCipher != cipherSeedBytes { |
|
t.Fatalf("cipherseed doesn't match: expected %v, got %v", |
|
cipherSeedBytes, newCipher) |
|
return false |
|
} |
|
|
|
return true |
|
} |
|
|
|
if err := quick.Check(mainScenario, nil); err != nil { |
|
t.Fatalf("fuzz check failed: %v", err) |
|
} |
|
} |
|
|
|
// TestEncipherDecipher is a property-based test that ensures that given a |
|
// version, entropy, and birthday, then we're able to map that to a cipherseed |
|
// mnemonic, then back to the original plaintext cipher seed. |
|
func TestEncipherDecipher(t *testing.T) { |
|
t.Parallel() |
|
|
|
// mainScenario is the main driver of our property based test. We'll |
|
// ensure that given a random seed tuple (internal version, entropy, |
|
// and birthday) we're able to convert that to a valid cipher seed. |
|
// Additionally, we should be able to decipher the final mnemonic, and |
|
// recover the original cipherseed. |
|
mainScenario := func(version uint8, entropy [EntropySize]byte, |
|
nowInt int64, pass [20]byte) bool { |
|
|
|
now := time.Unix(nowInt, 0) |
|
|
|
cipherSeed, err := New(version, &entropy, now) |
|
if err != nil { |
|
t.Fatalf("unable to map cipher text: %v", err) |
|
return false |
|
} |
|
|
|
mnemonic, err := cipherSeed.ToMnemonic(pass[:]) |
|
if err != nil { |
|
t.Fatalf("unable to generate mnemonic: %v", err) |
|
return false |
|
} |
|
|
|
cipherSeed2, err := mnemonic.ToCipherSeed(pass[:]) |
|
if err != nil { |
|
t.Fatalf("unable to decrypt cipher seed: %v", err) |
|
return false |
|
} |
|
|
|
if cipherSeed.InternalVersion != cipherSeed2.InternalVersion { |
|
t.Fatalf("mismatched versions: expected %v, got %v", |
|
cipherSeed.InternalVersion, cipherSeed2.InternalVersion) |
|
return false |
|
} |
|
if cipherSeed.Birthday != cipherSeed2.Birthday { |
|
t.Fatalf("mismatched birthday: expected %v, got %v", |
|
cipherSeed.Birthday, cipherSeed2.Birthday) |
|
return false |
|
} |
|
if cipherSeed.Entropy != cipherSeed2.Entropy { |
|
t.Fatalf("mismatched versions: expected %x, got %x", |
|
cipherSeed.Entropy[:], cipherSeed2.Entropy[:]) |
|
return false |
|
} |
|
|
|
return true |
|
} |
|
|
|
if err := quick.Check(mainScenario, nil); err != nil { |
|
t.Fatalf("fuzz check failed: %v", err) |
|
} |
|
} |
|
|
|
// TestSeedEncodeDecode tests that we're able to reverse the encoding of an |
|
// arbitrary raw seed. |
|
func TestSeedEncodeDecode(t *testing.T) { |
|
// mainScenario is the primary driver of our property-based test. We'll |
|
// ensure that given a random cipher seed, we can encode it an decode |
|
// it precisely. |
|
mainScenario := func(version uint8, nowInt int64, |
|
entropy [EntropySize]byte) bool { |
|
|
|
now := time.Unix(nowInt, 0) |
|
seed := CipherSeed{ |
|
InternalVersion: version, |
|
Birthday: uint16(now.Sub(BitcoinGenesisDate) / (time.Hour * 24)), |
|
Entropy: entropy, |
|
} |
|
|
|
var b bytes.Buffer |
|
if err := seed.encode(&b); err != nil { |
|
t.Fatalf("unable to encode: %v", err) |
|
return false |
|
} |
|
|
|
var newSeed CipherSeed |
|
if err := newSeed.decode(&b); err != nil { |
|
t.Fatalf("unable to decode: %v", err) |
|
return false |
|
} |
|
|
|
if seed.InternalVersion != newSeed.InternalVersion { |
|
t.Fatalf("mismatched versions: expected %v, got %v", |
|
seed.InternalVersion, newSeed.InternalVersion) |
|
return false |
|
} |
|
if seed.Birthday != newSeed.Birthday { |
|
t.Fatalf("mismatched birthday: expected %v, got %v", |
|
seed.Birthday, newSeed.Birthday) |
|
return false |
|
} |
|
if seed.Entropy != newSeed.Entropy { |
|
t.Fatalf("mismatched versions: expected %x, got %x", |
|
seed.Entropy[:], newSeed.Entropy[:]) |
|
return false |
|
} |
|
|
|
return true |
|
} |
|
|
|
if err := quick.Check(mainScenario, nil); err != nil { |
|
t.Fatalf("fuzz check failed: %v", err) |
|
} |
|
} |
|
|
|
// TestDecipherUnknownMnenomicWord tests that if we obtain a mnemonic, the |
|
// modify one of the words to not be within the word list, then it's detected |
|
// when we attempt to map it back to the original cipher seed. |
|
func TestDecipherUnknownMnenomicWord(t *testing.T) { |
|
t.Parallel() |
|
|
|
// First, we'll create a new cipher seed with "test" ass a password. |
|
pass := []byte("test") |
|
cipherSeed, err := New(0, &testEntropy, time.Now()) |
|
if err != nil { |
|
t.Fatalf("unable to create seed: %v", err) |
|
} |
|
|
|
// Now that we have our cipher seed, we'll encipher it and request a |
|
// mnemonic that we can use to recover later. |
|
mnemonic, err := cipherSeed.ToMnemonic(pass) |
|
if err != nil { |
|
t.Fatalf("unable to create mnemonic: %v", err) |
|
} |
|
|
|
// Before we attempt to decrypt the cipher seed, we'll mutate one of |
|
// the word so it isn't actually in our final word list. |
|
randIndex := rand.Int31n(int32(len(mnemonic))) |
|
mnemonic[randIndex] = "kek" |
|
|
|
// If we attempt to map back to the original cipher seed now, then we |
|
// should get ErrUnknownMnenomicWord. |
|
_, err = mnemonic.ToCipherSeed(pass) |
|
if err == nil { |
|
t.Fatalf("expected ErrUnknownMnenomicWord error") |
|
} |
|
|
|
wordErr, ok := err.(ErrUnknownMnenomicWord) |
|
if !ok { |
|
t.Fatalf("expected ErrUnknownMnenomicWord instead got %T", err) |
|
} |
|
|
|
if wordErr.Word != "kek" { |
|
t.Fatalf("word mismatch: expected %v, got %v", "kek", wordErr.Word) |
|
} |
|
if int32(wordErr.Index) != randIndex { |
|
t.Fatalf("wrong index detected: expected %v, got %v", |
|
randIndex, wordErr.Index) |
|
} |
|
|
|
// If the mnemonic includes a word that is not in the englishList |
|
// it fails, even when it is a substring of a valid word |
|
// Example: `heart` is in the list, `hear` is not |
|
mnemonic[randIndex] = "hear" |
|
|
|
// If we attempt to map back to the original cipher seed now, then we |
|
// should get ErrUnknownMnenomicWord. |
|
_, err = mnemonic.ToCipherSeed(pass) |
|
if err == nil { |
|
t.Fatalf("expected ErrUnknownMnenomicWord error") |
|
} |
|
_, ok = err.(ErrUnknownMnenomicWord) |
|
if !ok { |
|
t.Fatalf("expected ErrUnknownMnenomicWord instead got %T", err) |
|
} |
|
} |
|
|
|
// TestDecipherIncorrectMnemonic tests that if we obtain a cipherseed, but then |
|
// swap out words, then checksum fails. |
|
func TestDecipherIncorrectMnemonic(t *testing.T) { |
|
// First, we'll create a new cipher seed with "test" ass a password. |
|
pass := []byte("test") |
|
cipherSeed, err := New(0, &testEntropy, time.Now()) |
|
if err != nil { |
|
t.Fatalf("unable to create seed: %v", err) |
|
} |
|
|
|
// Now that we have our cipher seed, we'll encipher it and request a |
|
// mnemonic that we can use to recover later. |
|
mnemonic, err := cipherSeed.ToMnemonic(pass) |
|
if err != nil { |
|
t.Fatalf("unable to create mnemonic: %v", err) |
|
} |
|
|
|
// We'll now swap out two words from the mnemonic, which should trigger |
|
// a checksum failure. |
|
swapIndex1 := 9 |
|
swapIndex2 := 13 |
|
mnemonic[swapIndex1], mnemonic[swapIndex2] = mnemonic[swapIndex2], mnemonic[swapIndex1] |
|
|
|
// If we attempt to decrypt now, we should get a checksum failure. |
|
// If we attempt to map back to the original cipher seed now, then we |
|
// should get ErrUnknownMnenomicWord. |
|
_, err = mnemonic.ToCipherSeed(pass) |
|
if err != ErrIncorrectMnemonic { |
|
t.Fatalf("expected ErrIncorrectMnemonic error") |
|
} |
|
} |
|
|
|
// TODO(roasbeef): add test failure checksum fail is modified, new error |
|
|
|
func init() { |
|
// For the purposes of our test, we'll crank down the scrypt params a |
|
// bit. |
|
scryptN = 16 |
|
scryptR = 8 |
|
scryptP = 1 |
|
}
|
|
|