LEFT | RIGHT |
1 // Copyright 2011 The Go Authors. All rights reserved. | 1 // Copyright 2011 The Go Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style | 2 // Use of this source code is governed by a BSD-style |
3 // license that can be found in the LICENSE file. | 3 // license that can be found in the LICENSE file. |
4 | 4 |
5 package bcrypt | 5 package bcrypt |
6 | 6 |
7 import ( | 7 import ( |
8 "bytes" | 8 "bytes" |
9 "os" | 9 "os" |
10 "testing" | 10 "testing" |
11 ) | 11 ) |
12 | 12 |
13 func TestBcryptingIsEasy(t *testing.T) { | 13 func TestBcryptingIsEasy(t *testing.T) { |
14 pass := []byte("mypassword") | 14 pass := []byte("mypassword") |
15 hp, err := GenerateFromPassword(pass, 0) | 15 hp, err := GenerateFromPassword(pass, 0) |
16 if err != nil { | 16 if err != nil { |
17 » » t.Fatalf("NewFromPassword error: %s", err) | 17 » » t.Fatalf("GenerateFromPassword error: %s", err) |
18 } | 18 } |
19 | 19 |
20 if CompareHashAndPassword(hp, pass) != nil { | 20 if CompareHashAndPassword(hp, pass) != nil { |
21 t.Errorf("%v should hash %s correctly", hp, pass) | 21 t.Errorf("%v should hash %s correctly", hp, pass) |
| 22 } |
| 23 |
| 24 notPass := "notthepass" |
| 25 err = CompareHashAndPassword(hp, []byte(notPass)) |
| 26 if err != MismatchedHashAndPasswordError { |
| 27 t.Errorf("%v and %s should be mismatched", hp, notPass) |
22 } | 28 } |
23 } | 29 } |
24 | 30 |
25 func TestBcryptingIsCorrect(t *testing.T) { | 31 func TestBcryptingIsCorrect(t *testing.T) { |
26 pass := []byte("allmine") | 32 pass := []byte("allmine") |
27 salt := []byte("XajjQvNhvvRt5GSeFk1xFe") | 33 salt := []byte("XajjQvNhvvRt5GSeFk1xFe") |
28 expectedHash := []byte("$2a$10$XajjQvNhvvRt5GSeFk1xFeyqRrsxkhBkUiQeg0dt.
wU1qD4aFDcga") | 34 expectedHash := []byte("$2a$10$XajjQvNhvvRt5GSeFk1xFeyqRrsxkhBkUiQeg0dt.
wU1qD4aFDcga") |
29 | 35 |
30 hash, err := bcrypt(pass, 10, salt) | 36 hash, err := bcrypt(pass, 10, salt) |
31 if err != nil { | 37 if err != nil { |
32 » » t.Fatalf("bcrypt blew up: %s", err) | 38 » » t.Fatalf("bcrypt blew up: %v", err) |
33 } | 39 } |
34 if !bytes.HasSuffix(expectedHash, hash) { | 40 if !bytes.HasSuffix(expectedHash, hash) { |
35 » » t.Errorf("%s should be the suffix of %s", string(hash), string(e
xpectedHash)) | 41 » » t.Errorf("%v should be the suffix of %v", hash, expectedHash) |
36 } | 42 } |
37 | 43 |
38 h, err := newFromHash(expectedHash) | 44 h, err := newFromHash(expectedHash) |
39 if err != nil { | 45 if err != nil { |
40 t.Errorf("Unable to parse %s: %v", string(expectedHash), err) | 46 t.Errorf("Unable to parse %s: %v", string(expectedHash), err) |
41 } | 47 } |
42 | 48 |
43 // This is not the safe way to compare these hashes. We do this only for | 49 // This is not the safe way to compare these hashes. We do this only for |
44 // testing clarity. Use bcrypt.CompareHashAndPassword() | 50 // testing clarity. Use bcrypt.CompareHashAndPassword() |
45 if err == nil && !bytes.Equal(expectedHash, h.Hash()) { | 51 if err == nil && !bytes.Equal(expectedHash, h.Hash()) { |
46 t.Errorf("Parsed hash %v should equal %v", h.Hash(), expectedHas
h) | 52 t.Errorf("Parsed hash %v should equal %v", h.Hash(), expectedHas
h) |
47 } | 53 } |
48 } | 54 } |
49 | 55 |
| 56 func TestTooLongPasswordsWork(t *testing.T) { |
| 57 salt := []byte("XajjQvNhvvRt5GSeFk1xFe") |
| 58 // One byte over the usual 56 byte limit that blowfish has |
| 59 tooLongPass := []byte("0123456789012345678901234567890123456789012345678
90123456") |
| 60 tooLongExpected := []byte("$2a$10$XajjQvNhvvRt5GSeFk1xFe5l47dONXg781AmZt
d869sO8zfsHuw7C") |
| 61 hash, err := bcrypt(tooLongPass, 10, salt) |
| 62 if err != nil { |
| 63 t.Fatalf("bcrypt blew up on long password: %v", err) |
| 64 } |
| 65 if !bytes.HasSuffix(tooLongExpected, hash) { |
| 66 t.Errorf("%v should be the suffix of %v", hash, tooLongExpected) |
| 67 } |
| 68 } |
| 69 |
| 70 type InvalidHashTest struct { |
| 71 err os.Error |
| 72 hash []byte |
| 73 } |
| 74 |
| 75 var invalidTests = []InvalidHashTest{ |
| 76 {HashTooShortError, []byte("$2a$10$fooo")}, |
| 77 {HashTooShortError, []byte("$2a")}, |
| 78 {HashVersionTooNewError('3'), []byte("$3a$10$sssssssssssssssssssssshhhhh
hhhhhhhhhhhhhhhhhhhhhhhhhh")}, |
| 79 {InvalidHashPrefixError('%'), []byte("%2a$10$sssssssssssssssssssssshhhhh
hhhhhhhhhhhhhhhhhhhhhhhhhh")}, |
| 80 {InvalidCostError(32), []byte("$2a$32$sssssssssssssssssssssshhhhhhhhhhhh
hhhhhhhhhhhhhhhhhhh")}, |
| 81 } |
| 82 |
50 func TestInvalidHashErrors(t *testing.T) { | 83 func TestInvalidHashErrors(t *testing.T) { |
51 » tooShort := []byte("$2a$10$fooo") | 84 » check := func(name string, expected, err os.Error) { |
52 » tooShortMsg := "hashedSecret too short to be a bcrypted password" | 85 » » if err == nil { |
53 » tooNew := []byte("$3a$10$sssssssssssssssssssssshhhhhhhhhhhhhhhhhhhhhhhhh
hhhhhh") | 86 » » » t.Errorf("%s: Should have returned an error", name) |
54 » tooNewMsg := "bcrypt version '3' requested is newer than current version
'2': " + string(tooNew) | 87 » » } |
55 | 88 » » if err != nil && err != expected { |
56 » _, hashErr := newFromHash(tooShort) | 89 » » » t.Errorf("%s gave err %v but should have given %v", name
, err.String(), expected.String()) |
57 » hashErrorsCheck(t, "newFromHash", tooShort, tooShortMsg, hashErr) | 90 » » } |
58 » equalErr := CompareHashAndPassword(tooShort, []byte("anything")) | 91 » } |
59 » hashErrorsCheck(t, "EqualVerbose", tooShort, tooShortMsg, equalErr) | 92 » for _, iht := range invalidTests { |
60 | 93 » » _, err := newFromHash(iht.hash) |
61 » _, hashErr = newFromHash(tooNew) | 94 » » check("newFromHash", iht.err, err) |
62 » hashErrorsCheck(t, "newFromHash", tooNew, tooNewMsg, hashErr) | 95 » » err = CompareHashAndPassword(iht.hash, []byte("anything")) |
63 » equalErr = CompareHashAndPassword(tooNew, []byte("anything")) | 96 » » check("CompareHashAndPassword", iht.err, err) |
64 » hashErrorsCheck(t, "EqualVerbose", tooNew, tooNewMsg, equalErr) | 97 » } |
65 } | 98 } |
66 | 99 |
67 func TestUnpaddedBase64EncodingWorks(t *testing.T) { | 100 func TestUnpaddedBase64EncodingWorks(t *testing.T) { |
68 original := []byte{101, 201, 101, 75, 19, 227, 199, 20, 239, 236, 133, 3
2, 30, 109, 243, 30} | 101 original := []byte{101, 201, 101, 75, 19, 227, 199, 20, 239, 236, 133, 3
2, 30, 109, 243, 30} |
69 encodedOriginal := []byte("XajjQvNhvvRt5GSeFk1xFe") | 102 encodedOriginal := []byte("XajjQvNhvvRt5GSeFk1xFe") |
70 | 103 |
71 encoded := base64Encode(original) | 104 encoded := base64Encode(original) |
72 | 105 |
73 if !bytes.Equal(encodedOriginal, encoded) { | 106 if !bytes.Equal(encodedOriginal, encoded) { |
74 t.Errorf("Encoded %v should have equaled %v", encoded, encodedOr
iginal) | 107 t.Errorf("Encoded %v should have equaled %v", encoded, encodedOr
iginal) |
75 } | 108 } |
76 | 109 |
77 decoded, err := base64Decode(encodedOriginal) | 110 decoded, err := base64Decode(encodedOriginal) |
78 if err != nil { | 111 if err != nil { |
79 t.Fatalf("base64Decode blew up: %s", err) | 112 t.Fatalf("base64Decode blew up: %s", err) |
80 } | 113 } |
81 | 114 |
82 if !bytes.Equal(decoded, original) { | 115 if !bytes.Equal(decoded, original) { |
83 t.Errorf("Decoded %v should have equaled %v", decoded, original) | 116 t.Errorf("Decoded %v should have equaled %v", decoded, original) |
84 } | 117 } |
85 } | 118 } |
86 | 119 |
87 func TestCost(t *testing.T) { | 120 func TestCost(t *testing.T) { |
88 pass := []byte("mypassword") | 121 pass := []byte("mypassword") |
89 | 122 |
90 for c := 0; c < MinCost; c++ { | 123 for c := 0; c < MinCost; c++ { |
91 p, _ := newFromPassword(pass, c) | 124 p, _ := newFromPassword(pass, c) |
92 if p.cost != uint32(DefaultCost) { | 125 if p.cost != uint32(DefaultCost) { |
93 » » » t.Errorf("NewFromPassword should default costs below %d
to %d, but was %d", MinCost, DefaultCost, p.cost) | 126 » » » t.Errorf("newFromPassword should default costs below %d
to %d, but was %d", MinCost, DefaultCost, p.cost) |
94 } | 127 } |
95 } | 128 } |
96 | 129 |
97 p, _ := newFromPassword(pass, 14) | 130 p, _ := newFromPassword(pass, 14) |
98 if p.cost != 14 { | 131 if p.cost != 14 { |
99 » » t.Errorf("NewFromPassword should default cost to 14, but was %d"
, p.cost) | 132 » » t.Errorf("newFromPassword should default cost to 14, but was %d"
, p.cost) |
100 } | 133 } |
101 | 134 |
102 hp, _ := newFromHash(p.Hash()) | 135 hp, _ := newFromHash(p.Hash()) |
103 if p.cost != hp.cost { | 136 if p.cost != hp.cost { |
104 t.Errorf("newFromHash should maintain the cost at %d, but was %d
", p.cost, hp.cost) | 137 t.Errorf("newFromHash should maintain the cost at %d, but was %d
", p.cost, hp.cost) |
105 } | 138 } |
106 | 139 |
107 _, err := newFromPassword(pass, 32) | 140 _, err := newFromPassword(pass, 32) |
108 if err == nil { | 141 if err == nil { |
109 » » t.Fatalf("NewFromPassword: should return a cost error") | 142 » » t.Fatalf("newFromPassword: should return a cost error") |
110 } | 143 } |
111 » if err.String() != "cost in salt is invalid: 32" { | 144 » if err != InvalidCostError(32) { |
112 » » t.Fatalf("NewFromPassword: should return cost error, got %s", er
r) | 145 » » t.Errorf("newFromPassword: should return cost error, got %#v", e
rr) |
113 } | 146 } |
114 } | 147 } |
115 | 148 |
116 func TestCostReturnsWithLeadingZeroes(t *testing.T) { | 149 func TestCostReturnsWithLeadingZeroes(t *testing.T) { |
117 hp, _ := newFromPassword([]byte("abcdefgh"), 7) | 150 hp, _ := newFromPassword([]byte("abcdefgh"), 7) |
118 cost := hp.Hash()[4:7] | 151 cost := hp.Hash()[4:7] |
119 expected := []byte("07$") | 152 expected := []byte("07$") |
120 | 153 |
121 if !bytes.Equal(expected, cost) { | 154 if !bytes.Equal(expected, cost) { |
122 t.Errorf("single digit costs in hash should have leading zeros:
was %v instead of %v", cost, expected) | 155 t.Errorf("single digit costs in hash should have leading zeros:
was %v instead of %v", cost, expected) |
123 } | 156 } |
124 } | 157 } |
125 | 158 |
126 func TestMinorNotRequired(t *testing.T) { | 159 func TestMinorNotRequired(t *testing.T) { |
127 noMinorHash := []byte("$2$10$XajjQvNhvvRt5GSeFk1xFeyqRrsxkhBkUiQeg0dt.wU
1qD4aFDcga") | 160 noMinorHash := []byte("$2$10$XajjQvNhvvRt5GSeFk1xFeyqRrsxkhBkUiQeg0dt.wU
1qD4aFDcga") |
128 h, err := newFromHash(noMinorHash) | 161 h, err := newFromHash(noMinorHash) |
129 if err != nil { | 162 if err != nil { |
130 t.Fatalf("No minor hash blew up: %s", err) | 163 t.Fatalf("No minor hash blew up: %s", err) |
131 } | 164 } |
132 if h.minor != 0 { | 165 if h.minor != 0 { |
133 t.Errorf("Should leave minor version at 0, but was %d", h.minor) | 166 t.Errorf("Should leave minor version at 0, but was %d", h.minor) |
134 } | 167 } |
135 | 168 |
136 if !bytes.Equal(noMinorHash, h.Hash()) { | 169 if !bytes.Equal(noMinorHash, h.Hash()) { |
137 t.Logf("wut %v\n", noMinorHash) | |
138 t.Logf("wut %v\n", h.Hash()) | |
139 t.Errorf("Should generate hash %v, but created %v", noMinorHash,
h.Hash()) | 170 t.Errorf("Should generate hash %v, but created %v", noMinorHash,
h.Hash()) |
140 } | |
141 } | |
142 | |
143 func hashErrorsCheck(t *testing.T, name string, hash []byte, expectedStr string,
err os.Error) { | |
144 if err == nil { | |
145 t.Errorf("%s: Should have returned an error", name) | |
146 } | |
147 if err != nil && err.String() != expectedStr { | |
148 t.Errorf("%s gave err %v but should have given %v", name, err.St
ring(), expectedStr) | |
149 } | 171 } |
150 } | 172 } |
151 | 173 |
152 func BenchmarkEqual(b *testing.B) { | 174 func BenchmarkEqual(b *testing.B) { |
153 b.StopTimer() | 175 b.StopTimer() |
154 passwd := []byte("somepasswordyoulike") | 176 passwd := []byte("somepasswordyoulike") |
155 hash, _ := GenerateFromPassword(passwd, 10) | 177 hash, _ := GenerateFromPassword(passwd, 10) |
156 b.StartTimer() | 178 b.StartTimer() |
157 for i := 0; i < b.N; i++ { | 179 for i := 0; i < b.N; i++ { |
158 CompareHashAndPassword(hash, passwd) | 180 CompareHashAndPassword(hash, passwd) |
159 } | 181 } |
160 } | 182 } |
161 | 183 |
162 func BenchmarkGeneration(b *testing.B) { | 184 func BenchmarkGeneration(b *testing.B) { |
163 b.StopTimer() | 185 b.StopTimer() |
164 passwd := []byte("mylongpassword1234") | 186 passwd := []byte("mylongpassword1234") |
165 b.StartTimer() | 187 b.StartTimer() |
166 for i := 0; i < b.N; i++ { | 188 for i := 0; i < b.N; i++ { |
167 GenerateFromPassword(passwd, 10) | 189 GenerateFromPassword(passwd, 10) |
168 } | 190 } |
169 } | 191 } |
LEFT | RIGHT |