blob: f08a6f5b22938194ddbdb9a5c56f6c1ceb2497c3 [file] [log] [blame]
Russ Cox470549d2012-01-25 15:31:12 -05001// Copyright 2011 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package bcrypt
6
7import (
8 "bytes"
Jeff Hodges2c5e2072012-10-31 12:35:46 -04009 "fmt"
Russ Cox470549d2012-01-25 15:31:12 -050010 "testing"
11)
12
13func TestBcryptingIsEasy(t *testing.T) {
14 pass := []byte("mypassword")
15 hp, err := GenerateFromPassword(pass, 0)
16 if err != nil {
17 t.Fatalf("GenerateFromPassword error: %s", err)
18 }
19
20 if CompareHashAndPassword(hp, pass) != nil {
21 t.Errorf("%v should hash %s correctly", hp, pass)
22 }
23
24 notPass := "notthepass"
25 err = CompareHashAndPassword(hp, []byte(notPass))
26 if err != ErrMismatchedHashAndPassword {
27 t.Errorf("%v and %s should be mismatched", hp, notPass)
28 }
29}
30
31func TestBcryptingIsCorrect(t *testing.T) {
32 pass := []byte("allmine")
33 salt := []byte("XajjQvNhvvRt5GSeFk1xFe")
34 expectedHash := []byte("$2a$10$XajjQvNhvvRt5GSeFk1xFeyqRrsxkhBkUiQeg0dt.wU1qD4aFDcga")
35
36 hash, err := bcrypt(pass, 10, salt)
37 if err != nil {
38 t.Fatalf("bcrypt blew up: %v", err)
39 }
40 if !bytes.HasSuffix(expectedHash, hash) {
41 t.Errorf("%v should be the suffix of %v", hash, expectedHash)
42 }
43
44 h, err := newFromHash(expectedHash)
45 if err != nil {
46 t.Errorf("Unable to parse %s: %v", string(expectedHash), err)
47 }
48
49 // This is not the safe way to compare these hashes. We do this only for
50 // testing clarity. Use bcrypt.CompareHashAndPassword()
51 if err == nil && !bytes.Equal(expectedHash, h.Hash()) {
52 t.Errorf("Parsed hash %v should equal %v", h.Hash(), expectedHash)
53 }
54}
55
Adam Langley8f45c682014-03-31 13:41:02 -040056func TestVeryShortPasswords(t *testing.T) {
57 key := []byte("k")
58 salt := []byte("XajjQvNhvvRt5GSeFk1xFe")
59 _, err := bcrypt(key, 10, salt)
60 if err != nil {
61 t.Errorf("One byte key resulted in error: %s", err)
62 }
63}
64
Russ Cox470549d2012-01-25 15:31:12 -050065func TestTooLongPasswordsWork(t *testing.T) {
66 salt := []byte("XajjQvNhvvRt5GSeFk1xFe")
67 // One byte over the usual 56 byte limit that blowfish has
68 tooLongPass := []byte("012345678901234567890123456789012345678901234567890123456")
69 tooLongExpected := []byte("$2a$10$XajjQvNhvvRt5GSeFk1xFe5l47dONXg781AmZtd869sO8zfsHuw7C")
70 hash, err := bcrypt(tooLongPass, 10, salt)
71 if err != nil {
72 t.Fatalf("bcrypt blew up on long password: %v", err)
73 }
74 if !bytes.HasSuffix(tooLongExpected, hash) {
75 t.Errorf("%v should be the suffix of %v", hash, tooLongExpected)
76 }
77}
78
79type InvalidHashTest struct {
80 err error
81 hash []byte
82}
83
84var invalidTests = []InvalidHashTest{
85 {ErrHashTooShort, []byte("$2a$10$fooo")},
86 {ErrHashTooShort, []byte("$2a")},
87 {HashVersionTooNewError('3'), []byte("$3a$10$sssssssssssssssssssssshhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh")},
88 {InvalidHashPrefixError('%'), []byte("%2a$10$sssssssssssssssssssssshhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh")},
89 {InvalidCostError(32), []byte("$2a$32$sssssssssssssssssssssshhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh")},
90}
91
92func TestInvalidHashErrors(t *testing.T) {
93 check := func(name string, expected, err error) {
94 if err == nil {
95 t.Errorf("%s: Should have returned an error", name)
96 }
97 if err != nil && err != expected {
98 t.Errorf("%s gave err %v but should have given %v", name, err, expected)
99 }
100 }
101 for _, iht := range invalidTests {
102 _, err := newFromHash(iht.hash)
103 check("newFromHash", iht.err, err)
104 err = CompareHashAndPassword(iht.hash, []byte("anything"))
105 check("CompareHashAndPassword", iht.err, err)
106 }
107}
108
109func TestUnpaddedBase64Encoding(t *testing.T) {
110 original := []byte{101, 201, 101, 75, 19, 227, 199, 20, 239, 236, 133, 32, 30, 109, 243, 30}
111 encodedOriginal := []byte("XajjQvNhvvRt5GSeFk1xFe")
112
113 encoded := base64Encode(original)
114
115 if !bytes.Equal(encodedOriginal, encoded) {
116 t.Errorf("Encoded %v should have equaled %v", encoded, encodedOriginal)
117 }
118
119 decoded, err := base64Decode(encodedOriginal)
120 if err != nil {
121 t.Fatalf("base64Decode blew up: %s", err)
122 }
123
124 if !bytes.Equal(decoded, original) {
125 t.Errorf("Decoded %v should have equaled %v", decoded, original)
126 }
127}
128
129func TestCost(t *testing.T) {
Jeff Hodges2c5e2072012-10-31 12:35:46 -0400130 suffix := "XajjQvNhvvRt5GSeFk1xFe5l47dONXg781AmZtd869sO8zfsHuw7C"
131 for _, vers := range []string{"2a", "2"} {
132 for _, cost := range []int{4, 10} {
133 s := fmt.Sprintf("$%s$%02d$%s", vers, cost, suffix)
134 h := []byte(s)
135 actual, err := Cost(h)
136 if err != nil {
137 t.Errorf("Cost, error: %s", err)
138 continue
139 }
140 if actual != cost {
141 t.Errorf("Cost, expected: %d, actual: %d", cost, actual)
142 }
143 }
144 }
145 _, err := Cost([]byte("$a$a$" + suffix))
146 if err == nil {
147 t.Errorf("Cost, malformed but no error returned")
148 }
149}
150
151func TestCostValidationInHash(t *testing.T) {
Russ Cox470549d2012-01-25 15:31:12 -0500152 if testing.Short() {
153 return
154 }
155
156 pass := []byte("mypassword")
157
158 for c := 0; c < MinCost; c++ {
159 p, _ := newFromPassword(pass, c)
Jeff Hodges2c5e2072012-10-31 12:35:46 -0400160 if p.cost != DefaultCost {
Russ Cox470549d2012-01-25 15:31:12 -0500161 t.Errorf("newFromPassword should default costs below %d to %d, but was %d", MinCost, DefaultCost, p.cost)
162 }
163 }
164
165 p, _ := newFromPassword(pass, 14)
166 if p.cost != 14 {
167 t.Errorf("newFromPassword should default cost to 14, but was %d", p.cost)
168 }
169
170 hp, _ := newFromHash(p.Hash())
171 if p.cost != hp.cost {
172 t.Errorf("newFromHash should maintain the cost at %d, but was %d", p.cost, hp.cost)
173 }
174
175 _, err := newFromPassword(pass, 32)
176 if err == nil {
177 t.Fatalf("newFromPassword: should return a cost error")
178 }
179 if err != InvalidCostError(32) {
180 t.Errorf("newFromPassword: should return cost error, got %#v", err)
181 }
182}
183
184func TestCostReturnsWithLeadingZeroes(t *testing.T) {
185 hp, _ := newFromPassword([]byte("abcdefgh"), 7)
186 cost := hp.Hash()[4:7]
187 expected := []byte("07$")
188
189 if !bytes.Equal(expected, cost) {
190 t.Errorf("single digit costs in hash should have leading zeros: was %v instead of %v", cost, expected)
191 }
192}
193
194func TestMinorNotRequired(t *testing.T) {
195 noMinorHash := []byte("$2$10$XajjQvNhvvRt5GSeFk1xFeyqRrsxkhBkUiQeg0dt.wU1qD4aFDcga")
196 h, err := newFromHash(noMinorHash)
197 if err != nil {
198 t.Fatalf("No minor hash blew up: %s", err)
199 }
200 if h.minor != 0 {
201 t.Errorf("Should leave minor version at 0, but was %d", h.minor)
202 }
203
204 if !bytes.Equal(noMinorHash, h.Hash()) {
205 t.Errorf("Should generate hash %v, but created %v", noMinorHash, h.Hash())
206 }
207}
208
209func BenchmarkEqual(b *testing.B) {
210 b.StopTimer()
211 passwd := []byte("somepasswordyoulike")
212 hash, _ := GenerateFromPassword(passwd, 10)
213 b.StartTimer()
214 for i := 0; i < b.N; i++ {
215 CompareHashAndPassword(hash, passwd)
216 }
217}
218
219func BenchmarkGeneration(b *testing.B) {
220 b.StopTimer()
221 passwd := []byte("mylongpassword1234")
222 b.StartTimer()
223 for i := 0; i < b.N; i++ {
224 GenerateFromPassword(passwd, 10)
225 }
226}