vault_test.go raw
1 package domain
2
3 import (
4 "errors"
5 "strings"
6 "testing"
7 )
8
9 // testRounds is the work factor the vault tests use: the cost of PBKDF2 is not the property
10 // under test, and one test asserts the shipped factor still round-trips.
11 const testRounds = 1000
12
13 // phrase is a valid twelve-word phrase with a correct checksum.
14 const phrase = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
15
16 // TestSealAndOpen: the phrase survives the round trip, and the record is not the phrase.
17 func TestSealAndOpen(t *testing.T) {
18 sealed, err := seal("correct horse", phrase, testRounds)
19
20 if err != nil {
21 t.Fatal(err)
22 }
23
24 if strings.Contains(string(sealed), "abandon") {
25 t.Fatal("the record contains the phrase")
26 }
27
28 opened, err := open("correct horse", sealed, testRounds)
29
30 if err != nil {
31 t.Fatal(err)
32 }
33
34 if opened != phrase {
35 t.Fatalf("opened %q", opened)
36 }
37 }
38
39 // TestTheShippedWorkFactorRoundTrips: Seal and Open agree on the shipped factor.
40 func TestTheShippedWorkFactorRoundTrips(t *testing.T) {
41 sealed, err := Seal("correct horse", phrase)
42
43 if err != nil {
44 t.Fatal(err)
45 }
46
47 opened, err := Open("correct horse", sealed)
48
49 if err != nil {
50 t.Fatal(err)
51 }
52
53 if opened != phrase {
54 t.Fatalf("opened %q", opened)
55 }
56 }
57
58 // TestWrongPassphraseIsRefused: a wallet says the passphrase is wrong rather than opening a
59 // different seed.
60 func TestWrongPassphraseIsRefused(t *testing.T) {
61 sealed, err := seal("one", phrase, testRounds)
62
63 if err != nil {
64 t.Fatal(err)
65 }
66
67 if _, err := open("two", sealed, testRounds); !errors.Is(err, ErrVault) {
68 t.Fatalf("wrong passphrase opened the record: %v", err)
69 }
70 }
71
72 // TestTamperedRecordIsRefused: GCM authenticates, so an edited byte is an error.
73 func TestTamperedRecordIsRefused(t *testing.T) {
74 sealed, err := seal("pass", phrase, testRounds)
75
76 if err != nil {
77 t.Fatal(err)
78 }
79
80 for _, index := range []int{0, 1, vaultHeaderLen, len(sealed) - 1} {
81 edited := append([]byte{}, sealed...)
82 edited[index] ^= 0xff
83
84 if _, err := open("pass", edited, testRounds); err == nil {
85 t.Fatalf("a record edited at byte %d opened", index)
86 }
87 }
88 }
89
90 // TestShortRecordIsRefused: a truncated record is a damaged record, not an empty wallet.
91 func TestShortRecordIsRefused(t *testing.T) {
92 if _, err := open("pass", []byte{1, 2, 3}, testRounds); err == nil {
93 t.Fatal("a three-byte record opened")
94 }
95
96 if _, err := open("pass", nil, testRounds); err == nil {
97 t.Fatal("an empty record opened")
98 }
99 }
100
101 // TestUnknownVersionIsRefused: a record this build does not understand is refused, not guessed
102 // at.
103 func TestUnknownVersionIsRefused(t *testing.T) {
104 sealed, err := seal("pass", phrase, testRounds)
105
106 if err != nil {
107 t.Fatal(err)
108 }
109
110 sealed[0] = 99
111
112 if _, err := open("pass", sealed, testRounds); err == nil {
113 t.Fatal("a record with an unknown version opened")
114 }
115 }
116
117 // TestAnEmptyPassphraseIsRefused: sealing under nothing would be sealing under a constant.
118 func TestAnEmptyPassphraseIsRefused(t *testing.T) {
119 if _, err := Seal("", phrase); !errors.Is(err, ErrNoPassphrase) {
120 t.Fatalf("sealed under an empty passphrase: %v", err)
121 }
122
123 if _, err := Open("", []byte{1, 2, 3}); !errors.Is(err, ErrNoPassphrase) {
124 t.Fatalf("opened under an empty passphrase: %v", err)
125 }
126 }
127
128 // TestSealingIsNotDeterministic: a fresh salt and nonce per record, so two wallets sealed from
129 // one phrase do not look alike.
130 func TestSealingIsNotDeterministic(t *testing.T) {
131 first, err := seal("pass", phrase, testRounds)
132
133 if err != nil {
134 t.Fatal(err)
135 }
136
137 second, err := seal("pass", phrase, testRounds)
138
139 if err != nil {
140 t.Fatal(err)
141 }
142
143 if string(first) == string(second) {
144 t.Fatal("two seals of one phrase are identical")
145 }
146 }
147
148 // TestAnEmptyPhraseIsRefused: sealing nothing produces a wallet that cannot restore anything.
149 func TestAnEmptyPhraseIsRefused(t *testing.T) {
150 if _, err := Seal("pass", ""); err == nil {
151 t.Fatal("an empty phrase was sealed")
152 }
153 }
154