mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 19:07:30 +00:00
feat(crypto): support for SHA2 and PHPass password hashes (#9809)
# Which Problems Are Solved - Allow users to use SHA-256 and SHA-512 hashing algorithms. These algorithms are used by Linux's crypt(3) function. - Allow users to import passwords using the PHPass algorithm. This algorithm is used by older PHP systems, WordPress in particular. # How the Problems Are Solved - Upgrade passwap to [v0.9.0](https://github.com/zitadel/passwap/releases/tag/v0.9.0) - Add sha2 and phpass as a new verifier option in defaults.yaml # Additional Changes - Updated docs to explain the two algorithms # Additional Context Implements the changes in the passwap library from https://github.com/zitadel/passwap/pull/59 and https://github.com/zitadel/passwap/pull/60
This commit is contained in:
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/zitadel/passwap/md5salted"
|
||||
"github.com/zitadel/passwap/pbkdf2"
|
||||
"github.com/zitadel/passwap/scrypt"
|
||||
"github.com/zitadel/passwap/sha2"
|
||||
)
|
||||
|
||||
func TestPasswordHasher_EncodingSupported(t *testing.T) {
|
||||
@@ -78,7 +79,9 @@ func TestPasswordHashConfig_PasswordHasher(t *testing.T) {
|
||||
HashNameBcrypt,
|
||||
HashNameMd5,
|
||||
HashNameMd5Salted,
|
||||
HashNamePHPass,
|
||||
HashNameScrypt,
|
||||
HashNameSha2,
|
||||
"foobar",
|
||||
},
|
||||
Hasher: HasherConfig{
|
||||
@@ -142,6 +145,15 @@ func TestPasswordHashConfig_PasswordHasher(t *testing.T) {
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid phpass",
|
||||
fields: fields{
|
||||
Hasher: HasherConfig{
|
||||
Algorithm: HashNamePHPass,
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid argon2",
|
||||
fields: fields{
|
||||
@@ -357,6 +369,59 @@ func TestPasswordHashConfig_PasswordHasher(t *testing.T) {
|
||||
},
|
||||
wantPrefixes: []string{pbkdf2.Prefix, argon2.Prefix, bcrypt.Prefix, md5.Prefix, md5salted.Prefix},
|
||||
},
|
||||
{
|
||||
name: "sha2, parse error",
|
||||
fields: fields{
|
||||
Hasher: HasherConfig{
|
||||
Algorithm: HashNameSha2,
|
||||
Params: map[string]any{
|
||||
"cost": "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "pbkdf2, hash mode error",
|
||||
fields: fields{
|
||||
Hasher: HasherConfig{
|
||||
Algorithm: HashNameSha2,
|
||||
Params: map[string]any{
|
||||
"Rounds": 12,
|
||||
"Hash": "foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "sha2, sha256",
|
||||
fields: fields{
|
||||
Hasher: HasherConfig{
|
||||
Algorithm: HashNameSha2,
|
||||
Params: map[string]any{
|
||||
"Rounds": 12,
|
||||
"Hash": HashModeSHA256,
|
||||
},
|
||||
},
|
||||
Verifiers: []HashName{HashNameArgon2, HashNameBcrypt, HashNameMd5, HashNameMd5Plain},
|
||||
},
|
||||
wantPrefixes: []string{sha2.Sha256Identifier, sha2.Sha512Identifier, argon2.Prefix, bcrypt.Prefix, md5.Prefix},
|
||||
},
|
||||
{
|
||||
name: "sha2, sha512",
|
||||
fields: fields{
|
||||
Hasher: HasherConfig{
|
||||
Algorithm: HashNameSha2,
|
||||
Params: map[string]any{
|
||||
"Rounds": 12,
|
||||
"Hash": HashModeSHA512,
|
||||
},
|
||||
},
|
||||
Verifiers: []HashName{HashNameArgon2, HashNameBcrypt, HashNameMd5, HashNameMd5Plain},
|
||||
},
|
||||
wantPrefixes: []string{sha2.Sha256Identifier, sha2.Sha512Identifier, argon2.Prefix, bcrypt.Prefix, md5.Prefix},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
@@ -723,3 +788,93 @@ func TestHasherConfig_pbkdf2Params(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHasherConfig_sha2Params(t *testing.T) {
|
||||
type fields struct {
|
||||
Params map[string]any
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
want512 bool
|
||||
wantRounds int
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "decode error",
|
||||
fields: fields{
|
||||
Params: map[string]any{
|
||||
"foo": "bar",
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "sha1",
|
||||
fields: fields{
|
||||
Params: map[string]any{
|
||||
"Rounds": 12,
|
||||
"Hash": "sha1",
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "sha224",
|
||||
fields: fields{
|
||||
Params: map[string]any{
|
||||
"Rounds": 12,
|
||||
"Hash": "sha224",
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "sha256",
|
||||
fields: fields{
|
||||
Params: map[string]any{
|
||||
"Rounds": 5000,
|
||||
"Hash": "sha256",
|
||||
},
|
||||
},
|
||||
want512: false,
|
||||
wantRounds: 5000,
|
||||
},
|
||||
{
|
||||
name: "sha384",
|
||||
fields: fields{
|
||||
Params: map[string]any{
|
||||
"Rounds": 12,
|
||||
"Hash": "sha384",
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "sha512",
|
||||
fields: fields{
|
||||
Params: map[string]any{
|
||||
"Rounds": 15000,
|
||||
"Hash": "sha512",
|
||||
},
|
||||
},
|
||||
want512: true,
|
||||
wantRounds: 15000,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &HasherConfig{
|
||||
Params: tt.fields.Params,
|
||||
}
|
||||
got512, gotRounds, err := c.sha2Params()
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tt.want512, got512)
|
||||
assert.Equal(t, tt.wantRounds, gotRounds)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user