44 _ "crypto/sha256"
55 _ "crypto/sha512"
66 "testing"
7- )
87
9- import (
8+ "golang.org/x/crypto/bcrypt"
9+
1010 "github.com/dhui/passhash"
1111)
1212
@@ -180,7 +180,7 @@ func TestMatchesPasswordMatchNoUpdate(t *testing.T) {
180180 if credential .Kdf != origKdf {
181181 t .Errorf ("Original credential Kdf changed. %v != %v" , credential .Kdf , origKdf )
182182 }
183- if credential .WorkFactor != origWorkFactor {
183+ if ! passhash . WorkFactorsEqual ( credential .WorkFactor , origWorkFactor ) {
184184 t .Errorf ("Original credential WorkFactor changed. %v != %v" , credential .WorkFactor , origWorkFactor )
185185 }
186186
@@ -195,7 +195,7 @@ func TestMatchesPasswordMatchNoUpdate(t *testing.T) {
195195 t .Errorf ("Credential Kdf unexpectedly updated from safe recommended Kdf. %v != %v" , credential .Kdf ,
196196 passhash .DefaultConfig .Kdf )
197197 }
198- if credential .WorkFactor != passhash .DefaultWorkFactor [passhash .DefaultConfig .Kdf ] {
198+ if ! passhash . WorkFactorsEqual ( credential .WorkFactor , passhash .DefaultWorkFactor [passhash .DefaultConfig .Kdf ]) {
199199 t .Errorf ("Credential WorkFactor unexpected updated from safe recommended Kdf WorkFactor. %v != %v" ,
200200 credential .WorkFactor , passhash .DefaultWorkFactor [passhash .DefaultConfig .Kdf ])
201201 }
@@ -223,7 +223,7 @@ func TestMatchesPasswordUpdateKdfAndWorkFactor(t *testing.T) {
223223 if credential .Kdf != origKdf {
224224 t .Errorf ("Original credential Kdf changed. %v != %v" , credential .Kdf , origKdf )
225225 }
226- if credential .WorkFactor != origWorkFactor {
226+ if ! passhash . WorkFactorsEqual ( credential .WorkFactor , origWorkFactor ) {
227227 t .Errorf ("Original credential WorkFactor changed. %v != %v" , credential .WorkFactor , origWorkFactor )
228228 }
229229
@@ -238,7 +238,7 @@ func TestMatchesPasswordUpdateKdfAndWorkFactor(t *testing.T) {
238238 t .Errorf ("Updated credential Kdf did not update to safe recommended Kdf. %v != %v" , credential .Kdf ,
239239 passhash .DefaultConfig .Kdf )
240240 }
241- if credential .WorkFactor != passhash .DefaultWorkFactor [passhash .DefaultConfig .Kdf ] {
241+ if ! passhash . WorkFactorsEqual ( credential .WorkFactor , passhash .DefaultWorkFactor [passhash .DefaultConfig .Kdf ]) {
242242 t .Errorf ("Updated credential WorkFactor did not update to safe recommended Kdf WorkFactor. %v != %v" ,
243243 credential .WorkFactor , passhash .DefaultWorkFactor [passhash .DefaultConfig .Kdf ])
244244 }
@@ -278,12 +278,81 @@ func TestMatchesPasswordUpdateWorkFactor(t *testing.T) {
278278 if credential .Kdf != kdf {
279279 t .Errorf ("Updated credential Kdf changed. %v != %v" , credential .Kdf , kdf )
280280 }
281- if credential .WorkFactor != passhash .DefaultWorkFactor [passhash .DefaultConfig .Kdf ] {
281+ if ! passhash . WorkFactorsEqual ( credential .WorkFactor , passhash .DefaultWorkFactor [passhash .DefaultConfig .Kdf ]) {
282282 t .Errorf ("Updated credential WorkFactor did not update to safe recommended Kdf WorkFactor. %v != %v" ,
283283 credential .WorkFactor , passhash .DefaultWorkFactor [passhash .DefaultConfig .Kdf ])
284284 }
285285}
286286
287+ func TestScryptWorkFactorMutationDoesNotBreakExistingCredential (t * testing.T ) {
288+ cfg := passhash.Config {
289+ Kdf : passhash .Scrypt ,
290+ WorkFactor : & passhash.ScryptWorkFactor {N : 1024 , R : 16 , P : 1 },
291+ SaltSize : 16 ,
292+ KeyLength : 32 ,
293+ AuditLogger : & passhash.DummyAuditLogger {},
294+ Store : passhash.DummyCredentialStore {},
295+ }
296+ userID := passhash .UserID (1 )
297+
298+ cred , err := cfg .NewCredential (userID , testPassword )
299+ if err != nil {
300+ t .Fatalf ("Unable to create new Credential: %v" , err )
301+ }
302+
303+ if matched , _ := cred .MatchesPasswordWithConfig (cfg , testPassword ); ! matched {
304+ t .Fatalf ("Password did not match before work factor change" )
305+ }
306+
307+ wf := cfg .WorkFactor .(* passhash.ScryptWorkFactor )
308+ wf .N *= 2
309+
310+ if matched , _ := cred .MatchesPasswordWithConfig (cfg , testPassword ); ! matched {
311+ t .Fatalf ("Password did not match after work factor change; credential should be stable" )
312+ }
313+ }
314+
315+ func TestBcryptWorkFactorMutationTriggersUpgrade (t * testing.T ) {
316+ cfg := passhash.Config {
317+ Kdf : passhash .Bcrypt ,
318+ WorkFactor : & passhash.BcryptWorkFactor {Cost : 10 },
319+ SaltSize : 16 ,
320+ KeyLength : 32 ,
321+ AuditLogger : & passhash.DummyAuditLogger {},
322+ Store : passhash.DummyCredentialStore {},
323+ }
324+ userID := passhash .UserID (2 )
325+
326+ cred , err := cfg .NewCredential (userID , testPassword )
327+ if err != nil {
328+ t .Fatalf ("Unable to create new Credential: %v" , err )
329+ }
330+
331+ beforeCost , err := bcrypt .Cost (cred .Hash )
332+ if err != nil {
333+ t .Fatalf ("Unable to read bcrypt cost: %v" , err )
334+ }
335+
336+ wf := cfg .WorkFactor .(* passhash.BcryptWorkFactor )
337+ wf .Cost = beforeCost + 2
338+
339+ matched , updated := cred .MatchesPasswordWithConfig (cfg , testPassword )
340+ if ! matched {
341+ t .Fatalf ("Password did not match after bcrypt work factor change" )
342+ }
343+ if ! updated {
344+ t .Fatalf ("Expected credential to be upgraded after bcrypt work factor change" )
345+ }
346+
347+ afterCost , err := bcrypt .Cost (cred .Hash )
348+ if err != nil {
349+ t .Fatalf ("Unable to read bcrypt cost after upgrade: %v" , err )
350+ }
351+ if afterCost <= beforeCost {
352+ t .Fatalf ("Expected bcrypt cost to increase, got before=%d after=%d" , beforeCost , afterCost )
353+ }
354+ }
355+
287356func TestChangePassword (t * testing.T ) {
288357 userID := passhash .UserID (0 )
289358 credential , err := passhash .NewCredential (userID , testPassword )
0 commit comments