mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 20:27:32 +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:
@@ -653,7 +653,7 @@ SystemDefaults:
|
|||||||
# or cost are automatically re-hashed using this config,
|
# or cost are automatically re-hashed using this config,
|
||||||
# upon password validation or update.
|
# upon password validation or update.
|
||||||
Hasher:
|
Hasher:
|
||||||
# Supported algorithms: "argon2i", "argon2id", "bcrypt", "scrypt", "pbkdf2"
|
# Supported algorithms: "argon2i", "argon2id", "bcrypt", "scrypt", "pbkdf2", "sha2"
|
||||||
# Depending on the algorithm, different configuration options take effect.
|
# Depending on the algorithm, different configuration options take effect.
|
||||||
Algorithm: bcrypt # ZITADEL_SYSTEMDEFAULTS_PASSWORDHASHER_HASHER_ALGORITHM
|
Algorithm: bcrypt # ZITADEL_SYSTEMDEFAULTS_PASSWORDHASHER_HASHER_ALGORITHM
|
||||||
# Cost takes effect for the algorithms bcrypt and scrypt
|
# Cost takes effect for the algorithms bcrypt and scrypt
|
||||||
@@ -664,10 +664,11 @@ SystemDefaults:
|
|||||||
Memory: 32768 # ZITADEL_SYSTEMDEFAULTS_PASSWORDHASHER_HASHER_MEMORY
|
Memory: 32768 # ZITADEL_SYSTEMDEFAULTS_PASSWORDHASHER_HASHER_MEMORY
|
||||||
# Threads takes effect for the algorithms argon2i and argon2id
|
# Threads takes effect for the algorithms argon2i and argon2id
|
||||||
Threads: 4 # ZITADEL_SYSTEMDEFAULTS_PASSWORDHASHER_HASHER_THREADS
|
Threads: 4 # ZITADEL_SYSTEMDEFAULTS_PASSWORDHASHER_HASHER_THREADS
|
||||||
# Rounds takes effect for the algorithm pbkdf2
|
# Rounds takes effect for the algorithm pbkdf2 and sha2
|
||||||
Rounds: 290000 # ZITADEL_SYSTEMDEFAULTS_PASSWORDHASHER_HASHER_ROUNDS
|
Rounds: 290000 # ZITADEL_SYSTEMDEFAULTS_PASSWORDHASHER_HASHER_ROUNDS
|
||||||
# Hash takes effect for the algorithm pbkdf2
|
# Hash takes effect for the algorithm pbkdf2 and sha2
|
||||||
# Can be "sha1", "sha224", "sha256", "sha384" or "sha512"
|
# Can be "sha1", "sha224", "sha256", "sha384" or "sha512" for pbkdf2
|
||||||
|
# Can be "sha256" or "sha512" for sha2
|
||||||
Hash: sha256 # ZITADEL_SYSTEMDEFAULTS_PASSWORDHASHER_HASHER_HASH
|
Hash: sha256 # ZITADEL_SYSTEMDEFAULTS_PASSWORDHASHER_HASHER_HASH
|
||||||
|
|
||||||
# Verifiers enable the possibility of verifying
|
# Verifiers enable the possibility of verifying
|
||||||
@@ -689,6 +690,8 @@ SystemDefaults:
|
|||||||
# - "md5" # md5Crypt with salt and password shuffling.
|
# - "md5" # md5Crypt with salt and password shuffling.
|
||||||
# - "md5plain" # md5 digest of a password without salt
|
# - "md5plain" # md5 digest of a password without salt
|
||||||
# - "md5salted" # md5 digest of a salted password
|
# - "md5salted" # md5 digest of a salted password
|
||||||
|
# - "phpass"
|
||||||
|
# - "sha2" # crypt(3) SHA-256 and SHA-512
|
||||||
# - "scrypt"
|
# - "scrypt"
|
||||||
# - "pbkdf2" # verifier for all pbkdf2 hash modes.
|
# - "pbkdf2" # verifier for all pbkdf2 hash modes.
|
||||||
SecretHasher:
|
SecretHasher:
|
||||||
|
@@ -71,6 +71,8 @@ The following hash algorithms are supported:
|
|||||||
- md5: implementation of md5Crypt with salt and password shuffling [^2]
|
- md5: implementation of md5Crypt with salt and password shuffling [^2]
|
||||||
- md5plain: md5 digest of a password without salt [^2]
|
- md5plain: md5 digest of a password without salt [^2]
|
||||||
- md5salted: md5 digest of a salted password [^2]
|
- md5salted: md5 digest of a salted password [^2]
|
||||||
|
- phpass: md5 digest with PHPass algorithm (used in WordPress) [^2]
|
||||||
|
- sha2: implementation of crypt(3) SHA-256 & SHA-512
|
||||||
- scrypt
|
- scrypt
|
||||||
- pbkdf2
|
- pbkdf2
|
||||||
|
|
||||||
|
10
go.mod
10
go.mod
@@ -73,7 +73,7 @@ require (
|
|||||||
github.com/zitadel/exifremove v0.1.0
|
github.com/zitadel/exifremove v0.1.0
|
||||||
github.com/zitadel/logging v0.6.2
|
github.com/zitadel/logging v0.6.2
|
||||||
github.com/zitadel/oidc/v3 v3.36.1
|
github.com/zitadel/oidc/v3 v3.36.1
|
||||||
github.com/zitadel/passwap v0.7.0
|
github.com/zitadel/passwap v0.9.0
|
||||||
github.com/zitadel/saml v0.3.5
|
github.com/zitadel/saml v0.3.5
|
||||||
github.com/zitadel/schema v1.3.1
|
github.com/zitadel/schema v1.3.1
|
||||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0
|
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0
|
||||||
@@ -87,12 +87,12 @@ require (
|
|||||||
go.opentelemetry.io/otel/sdk/metric v1.35.0
|
go.opentelemetry.io/otel/sdk/metric v1.35.0
|
||||||
go.opentelemetry.io/otel/trace v1.35.0
|
go.opentelemetry.io/otel/trace v1.35.0
|
||||||
go.uber.org/mock v0.5.0
|
go.uber.org/mock v0.5.0
|
||||||
golang.org/x/crypto v0.36.0
|
golang.org/x/crypto v0.37.0
|
||||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394
|
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394
|
||||||
golang.org/x/net v0.37.0
|
golang.org/x/net v0.37.0
|
||||||
golang.org/x/oauth2 v0.28.0
|
golang.org/x/oauth2 v0.28.0
|
||||||
golang.org/x/sync v0.12.0
|
golang.org/x/sync v0.13.0
|
||||||
golang.org/x/text v0.23.0
|
golang.org/x/text v0.24.0
|
||||||
google.golang.org/api v0.227.0
|
google.golang.org/api v0.227.0
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20250313205543-e70fdf4c4cb4
|
google.golang.org/genproto/googleapis/api v0.0.0-20250313205543-e70fdf4c4cb4
|
||||||
google.golang.org/grpc v1.71.0
|
google.golang.org/grpc v1.71.0
|
||||||
@@ -226,7 +226,7 @@ require (
|
|||||||
github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 // indirect
|
github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 // indirect
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect
|
||||||
go.opentelemetry.io/proto/otlp v1.5.0 // indirect
|
go.opentelemetry.io/proto/otlp v1.5.0 // indirect
|
||||||
golang.org/x/sys v0.31.0
|
golang.org/x/sys v0.32.0
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
nhooyr.io/websocket v1.8.11 // indirect
|
nhooyr.io/websocket v1.8.11 // indirect
|
||||||
|
20
go.sum
20
go.sum
@@ -807,8 +807,8 @@ github.com/zitadel/logging v0.6.2 h1:MW2kDDR0ieQynPZ0KIZPrh9ote2WkxfBif5QoARDQcU
|
|||||||
github.com/zitadel/logging v0.6.2/go.mod h1:z6VWLWUkJpnNVDSLzrPSQSQyttysKZ6bCRongw0ROK4=
|
github.com/zitadel/logging v0.6.2/go.mod h1:z6VWLWUkJpnNVDSLzrPSQSQyttysKZ6bCRongw0ROK4=
|
||||||
github.com/zitadel/oidc/v3 v3.36.1 h1:1AT1NqKKEqAwx4GmKJZ9fYkWH2WIn/VKMfQ46nBtRf0=
|
github.com/zitadel/oidc/v3 v3.36.1 h1:1AT1NqKKEqAwx4GmKJZ9fYkWH2WIn/VKMfQ46nBtRf0=
|
||||||
github.com/zitadel/oidc/v3 v3.36.1/go.mod h1:dApGZLvWZTHRuxmcbQlW5d2XVjVYR3vGOdq536igmTs=
|
github.com/zitadel/oidc/v3 v3.36.1/go.mod h1:dApGZLvWZTHRuxmcbQlW5d2XVjVYR3vGOdq536igmTs=
|
||||||
github.com/zitadel/passwap v0.7.0 h1:TQTr9TV75PLATGICor1g5hZDRNHRvB9t0Hn4XkiR7xQ=
|
github.com/zitadel/passwap v0.9.0 h1:QvDK8OHKdb73C0m+mwXvu87UJSBqix3oFwTVENHdv80=
|
||||||
github.com/zitadel/passwap v0.7.0/go.mod h1:/NakQNYahdU+YFEitVD6mlm8BLfkiIT+IM5wgClRoAY=
|
github.com/zitadel/passwap v0.9.0/go.mod h1:6QzwFjDkIr3FfudzSogTOx5Ydhq4046dRJtDM/kX+G8=
|
||||||
github.com/zitadel/saml v0.3.5 h1:L1RKWS5y66cGepVxUGjx/WSBOtrtSpRA/J3nn5BJLOY=
|
github.com/zitadel/saml v0.3.5 h1:L1RKWS5y66cGepVxUGjx/WSBOtrtSpRA/J3nn5BJLOY=
|
||||||
github.com/zitadel/saml v0.3.5/go.mod h1:ybs3e4tIWdYgSYBpuCsvf3T4FNDfbXYM+GPv5vIpHYk=
|
github.com/zitadel/saml v0.3.5/go.mod h1:ybs3e4tIWdYgSYBpuCsvf3T4FNDfbXYM+GPv5vIpHYk=
|
||||||
github.com/zitadel/schema v1.3.1 h1:QT3kwiRIRXXLVAs6gCK/u044WmUVh6IlbLXUsn6yRQU=
|
github.com/zitadel/schema v1.3.1 h1:QT3kwiRIRXXLVAs6gCK/u044WmUVh6IlbLXUsn6yRQU=
|
||||||
@@ -881,8 +881,8 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY
|
|||||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||||
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
||||||
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
|
golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
|
||||||
@@ -970,8 +970,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
|||||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
|
||||||
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
@@ -1016,8 +1016,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
@@ -1038,8 +1038,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
|||||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||||
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
||||||
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
||||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
||||||
|
@@ -14,7 +14,9 @@ import (
|
|||||||
"github.com/zitadel/passwap/md5plain"
|
"github.com/zitadel/passwap/md5plain"
|
||||||
"github.com/zitadel/passwap/md5salted"
|
"github.com/zitadel/passwap/md5salted"
|
||||||
"github.com/zitadel/passwap/pbkdf2"
|
"github.com/zitadel/passwap/pbkdf2"
|
||||||
|
"github.com/zitadel/passwap/phpass"
|
||||||
"github.com/zitadel/passwap/scrypt"
|
"github.com/zitadel/passwap/scrypt"
|
||||||
|
"github.com/zitadel/passwap/sha2"
|
||||||
"github.com/zitadel/passwap/verifier"
|
"github.com/zitadel/passwap/verifier"
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/zerrors"
|
"github.com/zitadel/zitadel/internal/zerrors"
|
||||||
@@ -51,6 +53,8 @@ const (
|
|||||||
HashNameMd5 HashName = "md5" // verify only, as hashing with md5 is insecure and deprecated
|
HashNameMd5 HashName = "md5" // verify only, as hashing with md5 is insecure and deprecated
|
||||||
HashNameMd5Plain HashName = "md5plain" // verify only, as hashing with md5 is insecure and deprecated
|
HashNameMd5Plain HashName = "md5plain" // verify only, as hashing with md5 is insecure and deprecated
|
||||||
HashNameMd5Salted HashName = "md5salted" // verify only, as hashing with md5 is insecure and deprecated
|
HashNameMd5Salted HashName = "md5salted" // verify only, as hashing with md5 is insecure and deprecated
|
||||||
|
HashNamePHPass HashName = "phpass" // verify only, as hashing with md5 is insecure and deprecated
|
||||||
|
HashNameSha2 HashName = "sha2" // hash and verify
|
||||||
HashNameScrypt HashName = "scrypt" // hash and verify
|
HashNameScrypt HashName = "scrypt" // hash and verify
|
||||||
HashNamePBKDF2 HashName = "pbkdf2" // hash and verify
|
HashNamePBKDF2 HashName = "pbkdf2" // hash and verify
|
||||||
)
|
)
|
||||||
@@ -125,6 +129,14 @@ var knowVerifiers = map[HashName]prefixVerifier{
|
|||||||
prefixes: []string{md5salted.Prefix},
|
prefixes: []string{md5salted.Prefix},
|
||||||
verifier: md5salted.Verifier,
|
verifier: md5salted.Verifier,
|
||||||
},
|
},
|
||||||
|
HashNameSha2: {
|
||||||
|
prefixes: []string{sha2.Sha256Identifier, sha2.Sha512Identifier},
|
||||||
|
verifier: sha2.Verifier,
|
||||||
|
},
|
||||||
|
HashNamePHPass: {
|
||||||
|
prefixes: []string{phpass.IdentifierP, phpass.IdentifierH},
|
||||||
|
verifier: phpass.Verifier,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *HashConfig) buildVerifiers() (verifiers []verifier.Verifier, prefixes []string, err error) {
|
func (c *HashConfig) buildVerifiers() (verifiers []verifier.Verifier, prefixes []string, err error) {
|
||||||
@@ -158,9 +170,11 @@ func (c *HasherConfig) buildHasher() (hasher passwap.Hasher, prefixes []string,
|
|||||||
return c.scrypt()
|
return c.scrypt()
|
||||||
case HashNamePBKDF2:
|
case HashNamePBKDF2:
|
||||||
return c.pbkdf2()
|
return c.pbkdf2()
|
||||||
|
case HashNameSha2:
|
||||||
|
return c.sha2()
|
||||||
case "":
|
case "":
|
||||||
return nil, nil, fmt.Errorf("missing hasher algorithm")
|
return nil, nil, fmt.Errorf("missing hasher algorithm")
|
||||||
case HashNameArgon2, HashNameMd5:
|
case HashNameArgon2, HashNameMd5, HashNameMd5Plain, HashNameMd5Salted, HashNamePHPass:
|
||||||
fallthrough
|
fallthrough
|
||||||
default:
|
default:
|
||||||
return nil, nil, fmt.Errorf("invalid algorithm %q", c.Algorithm)
|
return nil, nil, fmt.Errorf("invalid algorithm %q", c.Algorithm)
|
||||||
@@ -296,6 +310,44 @@ func (c *HasherConfig) pbkdf2() (passwap.Hasher, []string, error) {
|
|||||||
case HashModeSHA512:
|
case HashModeSHA512:
|
||||||
return pbkdf2.NewSHA512(p), prefix, nil
|
return pbkdf2.NewSHA512(p), prefix, nil
|
||||||
default:
|
default:
|
||||||
return nil, nil, fmt.Errorf("unsuppored pbkdf2 hash mode: %s", hash)
|
return nil, nil, fmt.Errorf("unsupported pbkdf2 hash mode: %s", hash)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *HasherConfig) sha2Params() (use512 bool, rounds int, err error) {
|
||||||
|
var dst = struct {
|
||||||
|
Rounds uint32 `mapstructure:"Rounds"`
|
||||||
|
Hash HashMode `mapstructure:"Hash"`
|
||||||
|
}{}
|
||||||
|
if err := c.decodeParams(&dst); err != nil {
|
||||||
|
return false, 0, fmt.Errorf("decode sha2 params: %w", err)
|
||||||
|
}
|
||||||
|
switch dst.Hash {
|
||||||
|
case HashModeSHA256:
|
||||||
|
use512 = false
|
||||||
|
case HashModeSHA512:
|
||||||
|
use512 = true
|
||||||
|
case HashModeSHA1, HashModeSHA224, HashModeSHA384:
|
||||||
|
fallthrough
|
||||||
|
default:
|
||||||
|
return false, 0, fmt.Errorf("cannot use %s with sha2", dst.Hash)
|
||||||
|
}
|
||||||
|
if dst.Rounds > sha2.RoundsMax {
|
||||||
|
return false, 0, fmt.Errorf("rounds with sha2 cannot be larger than %d", sha2.RoundsMax)
|
||||||
|
} else {
|
||||||
|
rounds = int(dst.Rounds)
|
||||||
|
}
|
||||||
|
return use512, rounds, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *HasherConfig) sha2() (passwap.Hasher, []string, error) {
|
||||||
|
use512, rounds, err := c.sha2Params()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if use512 {
|
||||||
|
return sha2.New512(rounds), []string{sha2.Sha256Identifier, sha2.Sha512Identifier}, nil
|
||||||
|
} else {
|
||||||
|
return sha2.New256(rounds), []string{sha2.Sha256Identifier, sha2.Sha512Identifier}, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -14,6 +14,7 @@ import (
|
|||||||
"github.com/zitadel/passwap/md5salted"
|
"github.com/zitadel/passwap/md5salted"
|
||||||
"github.com/zitadel/passwap/pbkdf2"
|
"github.com/zitadel/passwap/pbkdf2"
|
||||||
"github.com/zitadel/passwap/scrypt"
|
"github.com/zitadel/passwap/scrypt"
|
||||||
|
"github.com/zitadel/passwap/sha2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestPasswordHasher_EncodingSupported(t *testing.T) {
|
func TestPasswordHasher_EncodingSupported(t *testing.T) {
|
||||||
@@ -78,7 +79,9 @@ func TestPasswordHashConfig_PasswordHasher(t *testing.T) {
|
|||||||
HashNameBcrypt,
|
HashNameBcrypt,
|
||||||
HashNameMd5,
|
HashNameMd5,
|
||||||
HashNameMd5Salted,
|
HashNameMd5Salted,
|
||||||
|
HashNamePHPass,
|
||||||
HashNameScrypt,
|
HashNameScrypt,
|
||||||
|
HashNameSha2,
|
||||||
"foobar",
|
"foobar",
|
||||||
},
|
},
|
||||||
Hasher: HasherConfig{
|
Hasher: HasherConfig{
|
||||||
@@ -142,6 +145,15 @@ func TestPasswordHashConfig_PasswordHasher(t *testing.T) {
|
|||||||
},
|
},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "invalid phpass",
|
||||||
|
fields: fields{
|
||||||
|
Hasher: HasherConfig{
|
||||||
|
Algorithm: HashNamePHPass,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "invalid argon2",
|
name: "invalid argon2",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
@@ -357,6 +369,59 @@ func TestPasswordHashConfig_PasswordHasher(t *testing.T) {
|
|||||||
},
|
},
|
||||||
wantPrefixes: []string{pbkdf2.Prefix, argon2.Prefix, bcrypt.Prefix, md5.Prefix, md5salted.Prefix},
|
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 {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
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