package domain import ( "errors" "strings" "testing" ) // testRounds is the work factor the vault tests use: the cost of PBKDF2 is not the property // under test, and one test asserts the shipped factor still round-trips. const testRounds = 1000 // phrase is a valid twelve-word phrase with a correct checksum. const phrase = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about" // TestSealAndOpen: the phrase survives the round trip, and the record is not the phrase. func TestSealAndOpen(t *testing.T) { sealed, err := seal("correct horse", phrase, testRounds) if err != nil { t.Fatal(err) } if strings.Contains(string(sealed), "abandon") { t.Fatal("the record contains the phrase") } opened, err := open("correct horse", sealed, testRounds) if err != nil { t.Fatal(err) } if opened != phrase { t.Fatalf("opened %q", opened) } } // TestTheShippedWorkFactorRoundTrips: Seal and Open agree on the shipped factor. func TestTheShippedWorkFactorRoundTrips(t *testing.T) { sealed, err := Seal("correct horse", phrase) if err != nil { t.Fatal(err) } opened, err := Open("correct horse", sealed) if err != nil { t.Fatal(err) } if opened != phrase { t.Fatalf("opened %q", opened) } } // TestWrongPassphraseIsRefused: a wallet says the passphrase is wrong rather than opening a // different seed. func TestWrongPassphraseIsRefused(t *testing.T) { sealed, err := seal("one", phrase, testRounds) if err != nil { t.Fatal(err) } if _, err := open("two", sealed, testRounds); !errors.Is(err, ErrVault) { t.Fatalf("wrong passphrase opened the record: %v", err) } } // TestTamperedRecordIsRefused: GCM authenticates, so an edited byte is an error. func TestTamperedRecordIsRefused(t *testing.T) { sealed, err := seal("pass", phrase, testRounds) if err != nil { t.Fatal(err) } for _, index := range []int{0, 1, vaultHeaderLen, len(sealed) - 1} { edited := append([]byte{}, sealed...) edited[index] ^= 0xff if _, err := open("pass", edited, testRounds); err == nil { t.Fatalf("a record edited at byte %d opened", index) } } } // TestShortRecordIsRefused: a truncated record is a damaged record, not an empty wallet. func TestShortRecordIsRefused(t *testing.T) { if _, err := open("pass", []byte{1, 2, 3}, testRounds); err == nil { t.Fatal("a three-byte record opened") } if _, err := open("pass", nil, testRounds); err == nil { t.Fatal("an empty record opened") } } // TestUnknownVersionIsRefused: a record this build does not understand is refused, not guessed // at. func TestUnknownVersionIsRefused(t *testing.T) { sealed, err := seal("pass", phrase, testRounds) if err != nil { t.Fatal(err) } sealed[0] = 99 if _, err := open("pass", sealed, testRounds); err == nil { t.Fatal("a record with an unknown version opened") } } // TestAnEmptyPassphraseIsRefused: sealing under nothing would be sealing under a constant. func TestAnEmptyPassphraseIsRefused(t *testing.T) { if _, err := Seal("", phrase); !errors.Is(err, ErrNoPassphrase) { t.Fatalf("sealed under an empty passphrase: %v", err) } if _, err := Open("", []byte{1, 2, 3}); !errors.Is(err, ErrNoPassphrase) { t.Fatalf("opened under an empty passphrase: %v", err) } } // TestSealingIsNotDeterministic: a fresh salt and nonce per record, so two wallets sealed from // one phrase do not look alike. func TestSealingIsNotDeterministic(t *testing.T) { first, err := seal("pass", phrase, testRounds) if err != nil { t.Fatal(err) } second, err := seal("pass", phrase, testRounds) if err != nil { t.Fatal(err) } if string(first) == string(second) { t.Fatal("two seals of one phrase are identical") } } // TestAnEmptyPhraseIsRefused: sealing nothing produces a wallet that cannot restore anything. func TestAnEmptyPhraseIsRefused(t *testing.T) { if _, err := Seal("pass", ""); err == nil { t.Fatal("an empty phrase was sealed") } }