github.com/decred/dcrlnd@v0.7.6/aezeed/cipherseed_test.go (about)

     1  package aezeed
     2  
     3  import (
     4  	"bytes"
     5  	"math/rand"
     6  	"testing"
     7  	"testing/quick"
     8  	"time"
     9  )
    10  
    11  // TestVector defines the values that are used to create a fully initialized
    12  // aezeed mnemonic seed and the expected values that should be calculated.
    13  type TestVector struct {
    14  	version          uint8
    15  	time             time.Time
    16  	entropy          [EntropySize]byte
    17  	salt             [saltSize]byte
    18  	password         []byte
    19  	expectedMnemonic [NumMnemonicWords]string
    20  	expectedBirthday uint16
    21  }
    22  
    23  var (
    24  	testEntropy = [EntropySize]byte{
    25  		0x81, 0xb6, 0x37, 0xd8,
    26  		0x63, 0x59, 0xe6, 0x96,
    27  		0x0d, 0xe7, 0x95, 0xe4,
    28  		0x1e, 0x0b, 0x4c, 0xfd,
    29  	}
    30  	testSalt = [saltSize]byte{
    31  		0x73, 0x61, 0x6c, 0x74, 0x31, // equal to "salt1"
    32  	}
    33  	version0TestVectors = []TestVector{
    34  		{
    35  			version:  0,
    36  			time:     DecredGenesisDate,
    37  			entropy:  testEntropy,
    38  			salt:     testSalt,
    39  			password: []byte{},
    40  			expectedMnemonic: [NumMnemonicWords]string{
    41  				"ability", "liquid", "travel", "stem", "barely", "drastic",
    42  				"pact", "cupboard", "apple", "thrive", "morning", "oak",
    43  				"feature", "tissue", "couch", "old", "math", "inform",
    44  				"success", "suggest", "drink", "motion", "know", "royal",
    45  			},
    46  			expectedBirthday: 0,
    47  		},
    48  		{
    49  			version:  0,
    50  			time:     time.Unix(1521799345, 0), // 03/23/2018 @ 10:02am (UTC)
    51  			entropy:  testEntropy,
    52  			salt:     testSalt,
    53  			password: []byte("!very_safe_55345_password*"),
    54  			expectedMnemonic: [NumMnemonicWords]string{
    55  				"absorb", "life", "portion", "oxygen", "stock", "pottery",
    56  				"soup", "build", "ugly", "jeans", "small", "uniform",
    57  				"letter", "voyage", "spirit", "age", "fury", "entire",
    58  				"success", "suggest", "drink", "office", "view", "dizzy",
    59  			},
    60  			expectedBirthday: 773,
    61  		},
    62  	}
    63  )
    64  
    65  func assertCipherSeedEqual(t *testing.T, cipherSeed *CipherSeed,
    66  	cipherSeed2 *CipherSeed) {
    67  
    68  	if cipherSeed.InternalVersion != cipherSeed2.InternalVersion {
    69  		t.Fatalf("mismatched versions: expected %v, got %v",
    70  			cipherSeed.InternalVersion, cipherSeed2.InternalVersion)
    71  	}
    72  	if cipherSeed.Birthday != cipherSeed2.Birthday {
    73  		t.Fatalf("mismatched birthday: expected %v, got %v",
    74  			cipherSeed.Birthday, cipherSeed2.Birthday)
    75  	}
    76  	if cipherSeed.Entropy != cipherSeed2.Entropy {
    77  		t.Fatalf("mismatched versions: expected %x, got %x",
    78  			cipherSeed.Entropy[:], cipherSeed2.Entropy[:])
    79  	}
    80  }
    81  
    82  // TestAezeedVersion0TestVectors tests some fixed test vector values against
    83  // the expected mnemonic words.
    84  func TestAezeedVersion0TestVectors(t *testing.T) {
    85  	t.Parallel()
    86  
    87  	// To minimize the number of tests that need to be run,
    88  	// go through all test vectors in the same test and also check
    89  	// the birthday calculation while we're at it.
    90  	for _, v := range version0TestVectors {
    91  		// First, we create new cipher seed with the given values
    92  		// from the test vector.
    93  		cipherSeed, err := New(v.version, &v.entropy, v.time)
    94  		if err != nil {
    95  			t.Fatalf("unable to create seed: %v", err)
    96  		}
    97  
    98  		// Then we need to set the salt to the pre-defined value, otherwise
    99  		// we'll end up with randomness in our mnemonics.
   100  		cipherSeed.salt = testSalt
   101  
   102  		// Now that the seed has been created, we'll attempt to convert it to a
   103  		// valid mnemonic.
   104  		mnemonic, err := cipherSeed.ToMnemonic(v.password)
   105  		if err != nil {
   106  			t.Fatalf("unable to create mnemonic: %v", err)
   107  		}
   108  
   109  		// Finally we compare the generated mnemonic and birthday to the
   110  		// expected value.
   111  		if mnemonic != v.expectedMnemonic {
   112  			t.Fatalf("mismatched mnemonic: expected %s, got %s",
   113  				v.expectedMnemonic, mnemonic)
   114  		}
   115  		if cipherSeed.Birthday != v.expectedBirthday {
   116  			t.Fatalf("mismatched birthday: expected %v, got %v",
   117  				v.expectedBirthday, cipherSeed.Birthday)
   118  		}
   119  	}
   120  }
   121  
   122  // TestEmptyPassphraseDerivation tests that the aezeed scheme is able to derive
   123  // a proper mnemonic, and decipher that mnemonic when the user uses an empty
   124  // passphrase.
   125  func TestEmptyPassphraseDerivation(t *testing.T) {
   126  	t.Parallel()
   127  
   128  	// Our empty passphrase...
   129  	pass := []byte{}
   130  
   131  	// We'll now create a new cipher seed with an internal version of zero
   132  	// to simulate a wallet that just adopted the scheme.
   133  	cipherSeed, err := New(0, &testEntropy, time.Now())
   134  	if err != nil {
   135  		t.Fatalf("unable to create seed: %v", err)
   136  	}
   137  
   138  	// Now that the seed has been created, we'll attempt to convert it to a
   139  	// valid mnemonic.
   140  	mnemonic, err := cipherSeed.ToMnemonic(pass)
   141  	if err != nil {
   142  		t.Fatalf("unable to create mnemonic: %v", err)
   143  	}
   144  
   145  	// Next, we'll try to decrypt the mnemonic with the passphrase that we
   146  	// used.
   147  	cipherSeed2, err := mnemonic.ToCipherSeed(pass)
   148  	if err != nil {
   149  		t.Fatalf("unable to decrypt mnemonic: %v", err)
   150  	}
   151  
   152  	// Finally, we'll ensure that the uncovered cipher seed matches
   153  	// precisely.
   154  	assertCipherSeedEqual(t, cipherSeed, cipherSeed2)
   155  }
   156  
   157  // TestManualEntropyGeneration tests that if the user doesn't provide a source
   158  // of entropy, then we do so ourselves.
   159  func TestManualEntropyGeneration(t *testing.T) {
   160  	t.Parallel()
   161  
   162  	// Our empty passphrase...
   163  	pass := []byte{}
   164  
   165  	// We'll now create a new cipher seed with an internal version of zero
   166  	// to simulate a wallet that just adopted the scheme.
   167  	cipherSeed, err := New(0, nil, time.Now())
   168  	if err != nil {
   169  		t.Fatalf("unable to create seed: %v", err)
   170  	}
   171  
   172  	// Now that the seed has been created, we'll attempt to convert it to a
   173  	// valid mnemonic.
   174  	mnemonic, err := cipherSeed.ToMnemonic(pass)
   175  	if err != nil {
   176  		t.Fatalf("unable to create mnemonic: %v", err)
   177  	}
   178  
   179  	// Next, we'll try to decrypt the mnemonic with the passphrase that we
   180  	// used.
   181  	cipherSeed2, err := mnemonic.ToCipherSeed(pass)
   182  	if err != nil {
   183  		t.Fatalf("unable to decrypt mnemonic: %v", err)
   184  	}
   185  
   186  	// Finally, we'll ensure that the uncovered cipher seed matches
   187  	// precisely.
   188  	assertCipherSeedEqual(t, cipherSeed, cipherSeed2)
   189  }
   190  
   191  // TestInvalidPassphraseRejection tests if a caller attempts to use the
   192  // incorrect passprhase for an enciphered seed, then the proper error is
   193  // returned.
   194  func TestInvalidPassphraseRejection(t *testing.T) {
   195  	t.Parallel()
   196  
   197  	// First, we'll generate a new cipher seed with a test passphrase.
   198  	pass := []byte("test")
   199  	cipherSeed, err := New(0, &testEntropy, time.Now())
   200  	if err != nil {
   201  		t.Fatalf("unable to create seed: %v", err)
   202  	}
   203  
   204  	// Now that we have our cipher seed, we'll encipher it and request a
   205  	// mnemonic that we can use to recover later.
   206  	mnemonic, err := cipherSeed.ToMnemonic(pass)
   207  	if err != nil {
   208  		t.Fatalf("unable to create mnemonic: %v", err)
   209  	}
   210  
   211  	// If we try to decipher with the wrong passphrase, we should get the
   212  	// proper error.
   213  	wrongPass := []byte("kek")
   214  	if _, err := mnemonic.ToCipherSeed(wrongPass); err != ErrInvalidPass {
   215  		t.Fatalf("expected ErrInvalidPass, instead got %v", err)
   216  	}
   217  }
   218  
   219  // TestRawEncipherDecipher tests that callers are able to use the raw methods
   220  // to map between ciphertext and the raw plaintext deciphered seed.
   221  func TestRawEncipherDecipher(t *testing.T) {
   222  	t.Parallel()
   223  
   224  	// First, we'll generate a new cipher seed with a test passphrase.
   225  	pass := []byte("test")
   226  	cipherSeed, err := New(0, &testEntropy, time.Now())
   227  	if err != nil {
   228  		t.Fatalf("unable to create seed: %v", err)
   229  	}
   230  
   231  	// With the cipherseed obtained, we'll now use the raw encipher method
   232  	// to obtain our final cipher text.
   233  	cipherText, err := cipherSeed.Encipher(pass)
   234  	if err != nil {
   235  		t.Fatalf("unable to encipher seed: %v", err)
   236  	}
   237  
   238  	mnemonic, err := cipherTextToMnemonic(cipherText)
   239  	if err != nil {
   240  		t.Fatalf("unable to create mnemonic: %v", err)
   241  	}
   242  
   243  	// Now that we have the ciphertext (mapped to the mnemonic), we'll
   244  	// attempt to decipher it raw using the user's passphrase.
   245  	plainSeedBytes, err := mnemonic.Decipher(pass)
   246  	if err != nil {
   247  		t.Fatalf("unable to decipher: %v", err)
   248  	}
   249  
   250  	// If we deserialize the plaintext seed bytes, it should exactly match
   251  	// the original cipher seed.
   252  	var newSeed CipherSeed
   253  	err = newSeed.decode(bytes.NewReader(plainSeedBytes[:]))
   254  	if err != nil {
   255  		t.Fatalf("unable to decode cipher seed: %v", err)
   256  	}
   257  
   258  	assertCipherSeedEqual(t, cipherSeed, &newSeed)
   259  }
   260  
   261  // TestInvalidExternalVersion tests that if we present a ciphertext with the
   262  // incorrect version to decipherCipherSeed, then it fails with the expected
   263  // error.
   264  func TestInvalidExternalVersion(t *testing.T) {
   265  	t.Parallel()
   266  
   267  	// First, we'll generate a new cipher seed.
   268  	cipherSeed, err := New(0, &testEntropy, time.Now())
   269  	if err != nil {
   270  		t.Fatalf("unable to create seed: %v", err)
   271  	}
   272  
   273  	// With the cipherseed obtained, we'll now use the raw encipher method
   274  	// to obtain our final cipher text.
   275  	pass := []byte("newpasswhodis")
   276  	cipherText, err := cipherSeed.Encipher(pass)
   277  	if err != nil {
   278  		t.Fatalf("unable to encipher seed: %v", err)
   279  	}
   280  
   281  	// Now that we have the cipher text, we'll modify the first byte to be
   282  	// an invalid version.
   283  	cipherText[0] = 44
   284  
   285  	// With the version swapped, if we try to decipher it, (no matter the
   286  	// passphrase), it should fail.
   287  	_, err = decipherCipherSeed(cipherText, []byte("kek"))
   288  	if err != ErrIncorrectVersion {
   289  		t.Fatalf("wrong error: expected ErrIncorrectVersion, "+
   290  			"got %v", err)
   291  	}
   292  }
   293  
   294  // TestChangePassphrase tests that we're able to generate a cipher seed, then
   295  // change the password. If we attempt to decipher the new enciphered seed, then
   296  // we should get the exact same seed back.
   297  func TestChangePassphrase(t *testing.T) {
   298  	t.Parallel()
   299  
   300  	// First, we'll generate a new cipher seed with a test passphrase.
   301  	pass := []byte("test")
   302  	cipherSeed, err := New(0, &testEntropy, time.Now())
   303  	if err != nil {
   304  		t.Fatalf("unable to create seed: %v", err)
   305  	}
   306  
   307  	// Now that we have our cipher seed, we'll encipher it and request a
   308  	// mnemonic that we can use to recover later.
   309  	mnemonic, err := cipherSeed.ToMnemonic(pass)
   310  	if err != nil {
   311  		t.Fatalf("unable to create mnemonic: %v", err)
   312  	}
   313  
   314  	// Now that have the mnemonic, we'll attempt to re-encipher the
   315  	// passphrase in order to get a brand new mnemonic.
   316  	newPass := []byte("strongerpassyeh!")
   317  	newmnemonic, err := mnemonic.ChangePass(pass, newPass)
   318  	if err != nil {
   319  		t.Fatalf("unable to change passphrase: %v", err)
   320  	}
   321  
   322  	// We'll now attempt to decipher the new mnemonic using the new
   323  	// passphrase to arrive at (what should be) the original cipher seed.
   324  	newCipherSeed, err := newmnemonic.ToCipherSeed(newPass)
   325  	if err != nil {
   326  		t.Fatalf("unable to decipher cipher seed: %v", err)
   327  	}
   328  
   329  	// Now that we have the cipher seed, we'll verify that the plaintext
   330  	// seed matches *identically*.
   331  	assertCipherSeedEqual(t, cipherSeed, newCipherSeed)
   332  }
   333  
   334  // TestChangePassphraseWrongPass tests that if we have a valid enciphered
   335  // cipherseed, but then try to change the password with the *wrong* password,
   336  // then we get an error.
   337  func TestChangePassphraseWrongPass(t *testing.T) {
   338  	t.Parallel()
   339  
   340  	// First, we'll generate a new cipher seed with a test passphrase.
   341  	pass := []byte("test")
   342  	cipherSeed, err := New(0, &testEntropy, time.Now())
   343  	if err != nil {
   344  		t.Fatalf("unable to create seed: %v", err)
   345  	}
   346  
   347  	// Now that we have our cipher seed, we'll encipher it and request a
   348  	// mnemonic that we can use to recover later.
   349  	mnemonic, err := cipherSeed.ToMnemonic(pass)
   350  	if err != nil {
   351  		t.Fatalf("unable to create mnemonic: %v", err)
   352  	}
   353  
   354  	// Now that have the mnemonic, we'll attempt to re-encipher the
   355  	// passphrase in order to get a brand new mnemonic. However, we'll be
   356  	// using the *wrong* passphrase. This should result in an
   357  	// ErrInvalidPass error.
   358  	wrongPass := []byte("kek")
   359  	newPass := []byte("strongerpassyeh!")
   360  	_, err = mnemonic.ChangePass(wrongPass, newPass)
   361  	if err != ErrInvalidPass {
   362  		t.Fatalf("expected ErrInvalidPass, instead got %v", err)
   363  	}
   364  }
   365  
   366  // TestMnemonicEncoding uses quickcheck like property based testing to ensure
   367  // that we're always able to fully recover the original byte stream encoded
   368  // into the mnemonic phrase.
   369  func TestMnemonicEncoding(t *testing.T) {
   370  	t.Parallel()
   371  
   372  	// mainScenario is the main driver of our property based test. We'll
   373  	// ensure that given a random byte string of length 33 bytes, if we
   374  	// convert that to the mnemonic, then we should be able to reverse the
   375  	// conversion.
   376  	mainScenario := func(cipherSeedBytes [EncipheredCipherSeedSize]byte) bool {
   377  		mnemonic, err := cipherTextToMnemonic(cipherSeedBytes)
   378  		if err != nil {
   379  			t.Fatalf("unable to map cipher text: %v", err)
   380  			return false
   381  		}
   382  
   383  		newCipher := mnemonicToCipherText(&mnemonic)
   384  
   385  		if newCipher != cipherSeedBytes {
   386  			t.Fatalf("cipherseed doesn't match: expected %v, got %v",
   387  				cipherSeedBytes, newCipher)
   388  			return false
   389  		}
   390  
   391  		return true
   392  	}
   393  
   394  	if err := quick.Check(mainScenario, nil); err != nil {
   395  		t.Fatalf("fuzz check failed: %v", err)
   396  	}
   397  }
   398  
   399  // TestEncipherDecipher is a property-based test that ensures that given a
   400  // version, entropy, and birthday, then we're able to map that to a cipherseed
   401  // mnemonic, then back to the original plaintext cipher seed.
   402  func TestEncipherDecipher(t *testing.T) {
   403  	t.Parallel()
   404  
   405  	// mainScenario is the main driver of our property based test. We'll
   406  	// ensure that given a random seed tuple (internal version, entropy,
   407  	// and birthday) we're able to convert that to a valid cipher seed.
   408  	// Additionally, we should be able to decipher the final mnemonic, and
   409  	// recover the original cipherseed.
   410  	mainScenario := func(version uint8, entropy [EntropySize]byte,
   411  		nowInt int64, pass [20]byte) bool {
   412  
   413  		now := time.Unix(nowInt, 0)
   414  
   415  		cipherSeed, err := New(version, &entropy, now)
   416  		if err != nil {
   417  			t.Fatalf("unable to map cipher text: %v", err)
   418  			return false
   419  		}
   420  
   421  		mnemonic, err := cipherSeed.ToMnemonic(pass[:])
   422  		if err != nil {
   423  			t.Fatalf("unable to generate mnemonic: %v", err)
   424  			return false
   425  		}
   426  
   427  		cipherSeed2, err := mnemonic.ToCipherSeed(pass[:])
   428  		if err != nil {
   429  			t.Fatalf("unable to decrypt cipher seed: %v", err)
   430  			return false
   431  		}
   432  
   433  		if cipherSeed.InternalVersion != cipherSeed2.InternalVersion {
   434  			t.Fatalf("mismatched versions: expected %v, got %v",
   435  				cipherSeed.InternalVersion, cipherSeed2.InternalVersion)
   436  			return false
   437  		}
   438  		if cipherSeed.Birthday != cipherSeed2.Birthday {
   439  			t.Fatalf("mismatched birthday: expected %v, got %v",
   440  				cipherSeed.Birthday, cipherSeed2.Birthday)
   441  			return false
   442  		}
   443  		if cipherSeed.Entropy != cipherSeed2.Entropy {
   444  			t.Fatalf("mismatched versions: expected %x, got %x",
   445  				cipherSeed.Entropy[:], cipherSeed2.Entropy[:])
   446  			return false
   447  		}
   448  
   449  		return true
   450  	}
   451  
   452  	if err := quick.Check(mainScenario, nil); err != nil {
   453  		t.Fatalf("fuzz check failed: %v", err)
   454  	}
   455  }
   456  
   457  // TestSeedEncodeDecode tests that we're able to reverse the encoding of an
   458  // arbitrary raw seed.
   459  func TestSeedEncodeDecode(t *testing.T) {
   460  	// mainScenario is the primary driver of our property-based test. We'll
   461  	// ensure that given a random cipher seed, we can encode it an decode
   462  	// it precisely.
   463  	mainScenario := func(version uint8, nowInt int64,
   464  		entropy [EntropySize]byte) bool {
   465  
   466  		now := time.Unix(nowInt, 0)
   467  		seed := CipherSeed{
   468  			InternalVersion: version,
   469  			Birthday:        uint16(now.Sub(DecredGenesisDate) / (time.Hour * 24)),
   470  			Entropy:         entropy,
   471  		}
   472  
   473  		var b bytes.Buffer
   474  		if err := seed.encode(&b); err != nil {
   475  			t.Fatalf("unable to encode: %v", err)
   476  			return false
   477  		}
   478  
   479  		var newSeed CipherSeed
   480  		if err := newSeed.decode(&b); err != nil {
   481  			t.Fatalf("unable to decode: %v", err)
   482  			return false
   483  		}
   484  
   485  		if seed.InternalVersion != newSeed.InternalVersion {
   486  			t.Fatalf("mismatched versions: expected %v, got %v",
   487  				seed.InternalVersion, newSeed.InternalVersion)
   488  			return false
   489  		}
   490  		if seed.Birthday != newSeed.Birthday {
   491  			t.Fatalf("mismatched birthday: expected %v, got %v",
   492  				seed.Birthday, newSeed.Birthday)
   493  			return false
   494  		}
   495  		if seed.Entropy != newSeed.Entropy {
   496  			t.Fatalf("mismatched versions: expected %x, got %x",
   497  				seed.Entropy[:], newSeed.Entropy[:])
   498  			return false
   499  		}
   500  
   501  		return true
   502  	}
   503  
   504  	if err := quick.Check(mainScenario, nil); err != nil {
   505  		t.Fatalf("fuzz check failed: %v", err)
   506  	}
   507  }
   508  
   509  // TestDecipherUnknownMnenomicWord tests that if we obtain a mnemonic, the
   510  // modify one of the words to not be within the word list, then it's detected
   511  // when we attempt to map it back to the original cipher seed.
   512  func TestDecipherUnknownMnenomicWord(t *testing.T) {
   513  	t.Parallel()
   514  
   515  	// First, we'll create a new cipher seed with "test" ass a password.
   516  	pass := []byte("test")
   517  	cipherSeed, err := New(0, &testEntropy, time.Now())
   518  	if err != nil {
   519  		t.Fatalf("unable to create seed: %v", err)
   520  	}
   521  
   522  	// Now that we have our cipher seed, we'll encipher it and request a
   523  	// mnemonic that we can use to recover later.
   524  	mnemonic, err := cipherSeed.ToMnemonic(pass)
   525  	if err != nil {
   526  		t.Fatalf("unable to create mnemonic: %v", err)
   527  	}
   528  
   529  	// Before we attempt to decrypt the cipher seed, we'll mutate one of
   530  	// the word so it isn't actually in our final word list.
   531  	randIndex := rand.Int31n(int32(len(mnemonic)))
   532  	mnemonic[randIndex] = "kek"
   533  
   534  	// If we attempt to map back to the original cipher seed now, then we
   535  	// should get ErrUnknownMnenomicWord.
   536  	_, err = mnemonic.ToCipherSeed(pass)
   537  	if err == nil {
   538  		t.Fatalf("expected ErrUnknownMnenomicWord error")
   539  	}
   540  
   541  	wordErr, ok := err.(ErrUnknownMnenomicWord)
   542  	if !ok {
   543  		t.Fatalf("expected ErrUnknownMnenomicWord instead got %T", err)
   544  	}
   545  
   546  	if wordErr.Word != "kek" {
   547  		t.Fatalf("word mismatch: expected %v, got %v", "kek", wordErr.Word)
   548  	}
   549  	if int32(wordErr.Index) != randIndex {
   550  		t.Fatalf("wrong index detected: expected %v, got %v",
   551  			randIndex, wordErr.Index)
   552  	}
   553  
   554  	// If the mnemonic includes a word that is not in the englishList
   555  	// it fails, even when it is a substring of a valid word
   556  	// Example: `heart` is in the list, `hear` is not
   557  	mnemonic[randIndex] = "hear"
   558  
   559  	// If we attempt to map back to the original cipher seed now, then we
   560  	// should get ErrUnknownMnenomicWord.
   561  	_, err = mnemonic.ToCipherSeed(pass)
   562  	if err == nil {
   563  		t.Fatalf("expected ErrUnknownMnenomicWord error")
   564  	}
   565  	_, ok = err.(ErrUnknownMnenomicWord)
   566  	if !ok {
   567  		t.Fatalf("expected ErrUnknownMnenomicWord instead got %T", err)
   568  	}
   569  }
   570  
   571  // TestDecipherIncorrectMnemonic tests that if we obtain a cipherseed, but then
   572  // swap out words, then checksum fails.
   573  func TestDecipherIncorrectMnemonic(t *testing.T) {
   574  	// First, we'll create a new cipher seed with "test" ass a password.
   575  	pass := []byte("test")
   576  	cipherSeed, err := New(0, &testEntropy, time.Now())
   577  	if err != nil {
   578  		t.Fatalf("unable to create seed: %v", err)
   579  	}
   580  
   581  	// Now that we have our cipher seed, we'll encipher it and request a
   582  	// mnemonic that we can use to recover later.
   583  	mnemonic, err := cipherSeed.ToMnemonic(pass)
   584  	if err != nil {
   585  		t.Fatalf("unable to create mnemonic: %v", err)
   586  	}
   587  
   588  	// We'll now swap out two words from the mnemonic, which should trigger
   589  	// a checksum failure.
   590  	swapIndex1 := 9
   591  	swapIndex2 := 13
   592  	mnemonic[swapIndex1], mnemonic[swapIndex2] = mnemonic[swapIndex2], mnemonic[swapIndex1]
   593  
   594  	// If we attempt to decrypt now, we should get a checksum failure.
   595  	// If we attempt to map back to the original cipher seed now, then we
   596  	// should get ErrUnknownMnenomicWord.
   597  	_, err = mnemonic.ToCipherSeed(pass)
   598  	if err != ErrIncorrectMnemonic {
   599  		t.Fatalf("expected ErrIncorrectMnemonic error")
   600  	}
   601  }
   602  
   603  // TODO(roasbeef): add test failure checksum fail is modified, new error
   604  
   605  func init() {
   606  	// For the purposes of our test, we'll crank down the scrypt params a
   607  	// bit.
   608  	scryptN = 16
   609  	scryptR = 8
   610  	scryptP = 1
   611  }