2022-12-07 14:19:04 -07:00
package passwd_test
import (
2022-12-10 08:58:08 -07:00
"bytes"
2022-12-07 14:19:04 -07:00
"crypto/subtle"
"fmt"
"testing"
"github.com/matryer/is"
"github.com/sour-is/go-passwd"
2022-12-10 08:58:08 -07:00
"github.com/sour-is/go-passwd/pkg/argon2"
2022-12-07 14:19:04 -07:00
"github.com/sour-is/go-passwd/pkg/unix"
)
type plainPasswd struct { }
2022-12-10 08:58:08 -07:00
func ( p * plainPasswd ) Passwd ( pass , check [ ] byte ) ( [ ] byte , error ) {
if check == nil {
var b bytes . Buffer
b . WriteString ( "$plain$" )
b . Write ( pass )
return b . Bytes ( ) , nil
2022-12-07 14:19:04 -07:00
}
2022-12-10 08:58:08 -07:00
if subtle . ConstantTimeCompare ( [ ] byte ( pass ) , [ ] byte ( bytes . TrimPrefix ( check , [ ] byte ( "$plain$" ) ) ) ) == 1 {
2022-12-07 14:19:04 -07:00
return check , nil
}
return check , passwd . ErrNoMatch
}
func ( p * plainPasswd ) ApplyPasswd ( passwd * passwd . Passwd ) {
passwd . Register ( "plain" , p )
passwd . SetFallthrough ( p )
}
2022-12-09 10:05:39 -07:00
// Example of upgrading password hash to a greater complexity.
//
// Note: This example uses very unsecure hash functions to allow for predictable output. Use of argon2.Argon2id or scrypt.Scrypt2 for greater hash security is recommended.
2022-12-07 14:19:04 -07:00
func Example ( ) {
2022-12-10 08:58:08 -07:00
pass := [ ] byte ( "my_pass" )
hash := [ ] byte ( "$1$81ed91e1131a3a5a50d8a68e8ef85fa0" )
2022-12-07 14:19:04 -07:00
pwd := passwd . New (
2022-12-10 08:58:08 -07:00
argon2 . Argon2id , // first is preferred type.
& unix . MD5 { } ,
2022-12-07 14:19:04 -07:00
)
_ , err := pwd . Passwd ( pass , hash )
if err != nil {
fmt . Println ( "fail: " , err )
2022-12-10 08:58:08 -07:00
return
2022-12-07 14:19:04 -07:00
}
// Check if we want to update.
if ! pwd . IsPreferred ( hash ) {
2022-12-10 08:58:08 -07:00
newHash , err := pwd . Passwd ( pass , nil )
2022-12-07 14:19:04 -07:00
if err != nil {
fmt . Println ( "fail: " , err )
2022-12-10 08:58:08 -07:00
return
2022-12-07 14:19:04 -07:00
}
2022-12-10 08:58:08 -07:00
fmt . Println ( "new hash:" , string ( newHash ) [ : 31 ] , "..." )
2022-12-07 14:19:04 -07:00
}
// Output:
2022-12-10 08:58:08 -07:00
// new hash: $argon2id$v=19,m=65536,t=1,p=4$ ...
2022-12-07 14:19:04 -07:00
}
func TestPasswdHash ( t * testing . T ) {
type testCase struct {
2022-12-10 08:58:08 -07:00
pass , hash [ ] byte
2022-12-07 14:19:04 -07:00
}
tests := [ ] testCase {
2022-12-10 08:58:08 -07:00
{ [ ] byte ( "passwd" ) , [ ] byte ( "passwd" ) } ,
{ [ ] byte ( "passwd" ) , [ ] byte ( "$plain$passwd" ) } ,
2022-12-07 14:19:04 -07:00
}
algos := [ ] passwd . Passwder { & plainPasswd { } }
is := is . New ( t )
// Generate additional test cases for each algo.
for _ , algo := range algos {
2022-12-10 08:58:08 -07:00
hash , err := algo . Passwd ( [ ] byte ( "passwd" ) , nil )
2022-12-07 14:19:04 -07:00
is . NoErr ( err )
2022-12-10 08:58:08 -07:00
tests = append ( tests , testCase { [ ] byte ( "passwd" ) , hash } )
2022-12-07 14:19:04 -07:00
}
pass := passwd . New ( algos ... )
for i , tt := range tests {
t . Run ( fmt . Sprint ( "Test-" , i ) , func ( t * testing . T ) {
is := is . New ( t )
hash , err := pass . Passwd ( tt . pass , tt . hash )
is . Equal ( hash , tt . hash )
is . NoErr ( err )
} )
}
}
func TestPasswdIsPreferred ( t * testing . T ) {
is := is . New ( t )
pass := passwd . New ( & plainPasswd { } )
2022-12-10 08:58:08 -07:00
ok := pass . IsPreferred ( [ ] byte ( "$plain$passwd" ) )
2022-12-07 14:19:04 -07:00
is . True ( ok )
2022-12-10 08:58:08 -07:00
ok = pass . IsPreferred ( [ ] byte ( "$foo$passwd" ) )
2022-12-07 14:19:04 -07:00
is . True ( ! ok )
}