1package argon2
2
3import (
4	"bytes"
5	"encoding/hex"
6	"testing"
7)
8
9func TestHash(t *testing.T) {
10	vectors := []struct {
11		ctx      *Context
12		password []byte
13		salt     []byte
14		hash     string
15	}{
16
17		{
18			&Context{
19				Iterations:     3,
20				Memory:         1 << 5,
21				Parallelism:    4,
22				Secret:         bytes.Repeat([]byte{3}, 8),
23				AssociatedData: bytes.Repeat([]byte{4}, 12),
24				HashLen:        32,
25				Mode:           ModeArgon2i,
26				Version:        Version10,
27			},
28			bytes.Repeat([]byte{1}, 32),
29			bytes.Repeat([]byte{2}, 16),
30			"87aeedd6517ab830cd9765cd8231abb2e647a5dee08f7c05e02fcb763335d0fd",
31		},
32		{
33			&Context{
34				Iterations:     3,
35				Memory:         1 << 5,
36				Parallelism:    4,
37				Secret:         bytes.Repeat([]byte{3}, 8),
38				AssociatedData: bytes.Repeat([]byte{4}, 12),
39				HashLen:        32,
40				Mode:           ModeArgon2i,
41				Version:        Version13,
42			},
43			bytes.Repeat([]byte{1}, 32),
44			bytes.Repeat([]byte{2}, 16),
45			"c814d9d1dc7f37aa13f0d77f2494bda1c8de6b016dd388d29952a4c4672b6ce8",
46		},
47		{
48			&Context{
49				Iterations:     3,
50				Memory:         1 << 5,
51				Parallelism:    4,
52				Secret:         bytes.Repeat([]byte{3}, 8),
53				AssociatedData: bytes.Repeat([]byte{4}, 12),
54				HashLen:        32,
55				Mode:           ModeArgon2d,
56				Version:        Version10,
57			},
58			bytes.Repeat([]byte{1}, 32),
59			bytes.Repeat([]byte{2}, 16),
60			"96a9d4e5a1734092c85e29f410a45914a5dd1f5cbf08b2670da68a0285abf32b",
61		},
62		{
63			&Context{
64				Iterations:     3,
65				Memory:         1 << 5,
66				Parallelism:    4,
67				Secret:         bytes.Repeat([]byte{3}, 8),
68				AssociatedData: bytes.Repeat([]byte{4}, 12),
69				HashLen:        32,
70				Mode:           ModeArgon2d,
71				Version:        Version13,
72			},
73			bytes.Repeat([]byte{1}, 32),
74			bytes.Repeat([]byte{2}, 16),
75			"512b391b6f1162975371d30919734294f868e3be3984f3c1a13a4db9fabe4acb",
76		},
77		{
78			&Context{
79				Iterations:     3,
80				Memory:         1 << 5,
81				Parallelism:    4,
82				Secret:         bytes.Repeat([]byte{3}, 8),
83				AssociatedData: bytes.Repeat([]byte{4}, 12),
84				HashLen:        32,
85				Mode:           ModeArgon2id,
86				Version:        Version10,
87			},
88			bytes.Repeat([]byte{1}, 32),
89			bytes.Repeat([]byte{2}, 16),
90			"b64615f07789b66b645b67ee9ed3b377ae350b6bfcbb0fc95141ea8f322613c0",
91		},
92		{
93			&Context{
94				Iterations:     3,
95				Memory:         1 << 5,
96				Parallelism:    4,
97				Secret:         bytes.Repeat([]byte{3}, 8),
98				AssociatedData: bytes.Repeat([]byte{4}, 12),
99				HashLen:        32,
100				Mode:           ModeArgon2id,
101				Version:        Version13,
102			},
103			bytes.Repeat([]byte{1}, 32),
104			bytes.Repeat([]byte{2}, 16),
105			"0d640df58d78766c08c037a34a8b53c9d01ef0452d75b65eb52520e96b01e659",
106		},
107	}
108
109	for i, v := range vectors {
110		expected, _ := hex.DecodeString(v.hash)
111		hash, err := Hash(v.ctx, v.password, v.salt)
112		if err != nil {
113			t.Errorf("received error: %s (%d)", err, i)
114		}
115		if !bytes.Equal(hash, expected) {
116			t.Errorf("%d:      got: %x", i, hash)
117			t.Errorf("%d: expected: %x", i, expected)
118		}
119	}
120
121}
122
123func TestHashEncoded(t *testing.T) {
124	ctx := NewContext(ModeArgon2d)
125
126	password = []byte("somepassword")
127	salt := []byte("somesalt")
128
129	expected := "$argon2d$v=19$m=4096,t=3,p=1$c29tZXNhbHQ$THaZx86KeqT+xuygENqvxaYIk3zu4wH0UmqzBL/wrdQ"
130
131	s, err := HashEncoded(ctx, password, salt)
132	if err != nil {
133		t.Fatal(err)
134	}
135	if s != expected {
136		t.Fatalf("HashEncoded: got %q  want %q", s, expected)
137	}
138
139	ctx.Version = Version10
140	expected = "$argon2d$v=16$m=4096,t=3,p=1$c29tZXNhbHQ$9zHzndOtdbtKI3zBlrpnnpjNj9FnrkeiK43kb8NuuMc"
141
142	s, err = HashEncoded(ctx, password, salt)
143	if err != nil {
144		t.Fatal(err)
145	}
146	if s != expected {
147		t.Fatalf("HashEncoded: got %q  want %q", s, expected)
148	}
149}
150
151func TestHash_Error(t *testing.T) {
152	ctx := NewContext()
153	_, err := Hash(ctx, []byte("password"), []byte("s"))
154	if err != ErrSaltTooShort {
155		t.Errorf("got %q  want %q", err, ErrSaltTooShort)
156	}
157
158	ctx = NewContext()
159	ctx.Mode = 99
160	_, err = Hash(ctx, []byte("password"), []byte("somesalt"))
161	if err != ErrIncorrectType {
162		t.Errorf("got %q  want %q", err, ErrIncorrectType)
163	}
164
165	ctx = NewContext()
166	ctx.Memory = 4
167	_, err = Hash(ctx, []byte("password"), []byte("somesalt"))
168	if err != ErrMemoryTooLittle {
169		t.Errorf("got %q  want %q", err, ErrMemoryTooLittle)
170	}
171}
172
173func TestVerify(t *testing.T) {
174	ctx := NewContext(ModeArgon2d)
175	testVerify(t, ctx)
176
177	ctx.Mode = ModeArgon2i
178	testVerify(t, ctx)
179}
180
181func TestVerifyEncoded(t *testing.T) {
182	ctx := NewContext(ModeArgon2d)
183	testVerifyEncoded(t, ctx)
184
185	ctx.Mode = ModeArgon2i
186	testVerifyEncoded(t, ctx)
187}
188
189func TestFlagClearPassword(t *testing.T) {
190	ctx := NewContext()
191	ctx.Flags = FlagDefault
192	password := []byte("somepassword")
193	salt := []byte("somesalt")
194
195	Hash(ctx, password, salt)
196	if !bytes.Equal([]byte("somepassword"), password) {
197		t.Fatalf("password slice is modified")
198	}
199
200	ctx.Flags = FlagClearPassword
201	Hash(ctx, password, salt)
202	if !bytes.Equal(make([]byte, len(password)), password) {
203		t.Fatalf("password slice is not cleared")
204	}
205}
206
207func TestFlagClearSecret(t *testing.T) {
208	ctx := NewContext()
209	ctx.Flags = FlagDefault
210	ctx.Secret = []byte("somesecret")
211	password := []byte("somepassword")
212	salt := []byte("somesalt")
213
214	Hash(ctx, password, salt)
215	if !bytes.Equal([]byte("somesecret"), ctx.Secret) {
216		t.Fatalf("secret slice is modified")
217	}
218
219	ctx.Flags = FlagClearSecret
220	Hash(ctx, password, salt)
221	if !bytes.Equal(make([]byte, len(ctx.Secret)), ctx.Secret) {
222		t.Fatalf("secret slice is not cleared")
223	}
224}
225
226func testVerifyEncoded(t *testing.T, ctx *Context) {
227	s, err := HashEncoded(ctx, []byte("somepassword"), []byte("somesalt"))
228	if err != nil {
229		t.Fatal(err)
230	}
231
232	pw := []byte("somepassword")
233	ok, err := VerifyEncoded(s, pw)
234	if err != nil {
235		t.Fatal(err)
236	}
237	if !ok {
238		t.Errorf("VerifyEncoded(s, []byte(%q)) = false  want true", pw)
239	}
240
241	pw = []byte("someotherpassword")
242	ok, err = VerifyEncoded(s, pw)
243	if err != nil {
244		t.Fatal(err)
245	}
246	if ok {
247		t.Errorf("VerifyEncoded(s, []byte(%q)) = true  want false", pw)
248	}
249}
250
251func testVerify(t *testing.T, ctx *Context) {
252	password := []byte("hunter2")
253	salt := []byte("somesalt")
254	hash, err := Hash(ctx, password, salt)
255	if err != nil {
256		t.Fatal(err)
257	}
258
259	// Test correct password
260	ok, err := Verify(ctx, hash, password, salt)
261	if err != nil {
262		t.Fatal(err)
263	}
264	if !ok {
265		t.Errorf("Verify(..) = false  want true (%v)", ctx)
266	}
267
268	// Test incorrect password
269	ok, err = Verify(ctx, hash, []byte("hunter3"), salt)
270	if err != nil {
271		t.Fatal(err)
272	}
273	if ok {
274		t.Errorf("Verify(badpw) = true  want false (%v)", ctx)
275	}
276
277	// Test incorrect salt
278	ok, err = Verify(ctx, hash, password, []byte("somepepper"))
279	if err != nil {
280		t.Fatal(err)
281	}
282	if ok {
283		t.Errorf("Verify(badsalt) = true  want false (%v)", ctx)
284	}
285}
286