[BUGFIX] Password hashing schema map mismatch with docs (#852)
* add a nolint for gosec 'possibly hardcoded password' that was incorrect * make all parameters consistent * update the docs for the correct key name 'password' instead of 'password_options' or 'password_hashing' * reword some of the docs * apply suggestions from code review Co-Authored-By: Amir Zarrinkafsh <nightah@me.com>pull/855/head
parent
b0f81380c2
commit
b3ce7fc379
|
@ -140,7 +140,7 @@ authentication_backend:
|
||||||
# which is updated when users reset their passwords.
|
# which is updated when users reset their passwords.
|
||||||
# Therefore, this backend is meant to be used in a dev environment
|
# Therefore, this backend is meant to be used in a dev environment
|
||||||
# and not in production since it prevents Authelia to be scaled to
|
# and not in production since it prevents Authelia to be scaled to
|
||||||
# more than one instance. The options under password_options have sane
|
# more than one instance. The options under 'password' have sane
|
||||||
# defaults, and as it has security implications it is highly recommended
|
# defaults, and as it has security implications it is highly recommended
|
||||||
# you leave the default values. Before considering changing these settings
|
# you leave the default values. Before considering changing these settings
|
||||||
# please read the docs page below:
|
# please read the docs page below:
|
||||||
|
@ -148,7 +148,7 @@ authentication_backend:
|
||||||
#
|
#
|
||||||
## file:
|
## file:
|
||||||
## path: ./users_database.yml
|
## path: ./users_database.yml
|
||||||
## password_options:
|
## password:
|
||||||
## algorithm: argon2id
|
## algorithm: argon2id
|
||||||
## iterations: 1
|
## iterations: 1
|
||||||
## key_length: 32
|
## key_length: 32
|
||||||
|
|
|
@ -19,7 +19,7 @@ file in the configuration file.
|
||||||
disable_reset_password: false
|
disable_reset_password: false
|
||||||
file:
|
file:
|
||||||
path: /var/lib/authelia/users.yml
|
path: /var/lib/authelia/users.yml
|
||||||
password_hashing:
|
password:
|
||||||
algorithm: argon2id
|
algorithm: argon2id
|
||||||
iterations: 1
|
iterations: 1
|
||||||
salt_length: 16
|
salt_length: 16
|
||||||
|
@ -97,26 +97,38 @@ Flags:
|
||||||
|
|
||||||
## Password hash algorithm
|
## Password hash algorithm
|
||||||
|
|
||||||
The default hash algorithm is salted Argon2id version 19. Argon2id is currently considered
|
The default hash algorithm is Argon2id version 19 with a salt. Argon2id is currently considered
|
||||||
the best hashing algorithm, and in 2015 won the
|
the best hashing algorithm, and in 2015 won the
|
||||||
[Password Hashing Competition](https://en.wikipedia.org/wiki/Password_Hashing_Competition).
|
[Password Hashing Competition](https://en.wikipedia.org/wiki/Password_Hashing_Competition).
|
||||||
It benefits from customizable parameters allowing the cost of computing a hash to scale
|
It benefits from customizable parameters allowing the cost of computing a hash to scale
|
||||||
into the future which makes it harder to brute-force. Argon2id was implemented due to community
|
into the future which makes it harder to brute-force. Argon2id was implemented due to community
|
||||||
feedback as you can see in this closed [issue](https://github.com/authelia/authelia/issues/577).
|
feedback as you can see in this closed [issue](https://github.com/authelia/authelia/issues/577).
|
||||||
|
|
||||||
Additionally SHA512 is supported for backwards compatibility and user choice. While it's a reasonable
|
For backwards compatibility and user choice support for the SHA512 algorithm is still available.
|
||||||
hash function given high enough iterations, as hardware gets better it has a higher chance of being
|
While it's a reasonable hashing function given high enough iterations, as hardware improves it
|
||||||
brute-forced.
|
has a higher chance of being brute-forced.
|
||||||
|
|
||||||
Hashes are identifiable as argon2id or SHA512 by their prefix of either `$argon2id$` and `$6$`
|
Hashes are identifiable as argon2id or SHA512 by their prefix of either `$argon2id$` and `$6$`
|
||||||
respectively, as described in this [wiki page](https://en.wikipedia.org/wiki/Crypt_(C)).
|
respectively, as described in this [wiki page](https://en.wikipedia.org/wiki/Crypt_(C)).
|
||||||
|
|
||||||
|
**Important Note:** When using argon2id Authelia will appear to remain using the memory allocated
|
||||||
|
to creating the hash. This is due to how [Go](https://golang.org/) allocates memory to the heap when
|
||||||
|
generating an argon2id hash. Go periodically garbage collects the heap, however this doesn't remove
|
||||||
|
the memory allocation, it keeps it allocated even though it's technically unused. Under memory
|
||||||
|
pressure the unused allocated memory will be reclaimed by the operating system, you can test
|
||||||
|
this on linux with
|
||||||
|
`stress-ng --vm-bytes $(awk '/MemFree/{printf "%d\n", $2 * 0.9;}' < /proc/meminfo)k --vm-keep -m 1`.
|
||||||
|
If this is not desirable we recommend investigating the following options in order of most to least secure:
|
||||||
|
1. using the [LDAP authentication provider](./ldap.md)
|
||||||
|
2. adjusting the [memory](#memory) parameter
|
||||||
|
3. changing the [algorithm](#algorithm)
|
||||||
|
|
||||||
### Password hash algorithm tuning
|
### Password hash algorithm tuning
|
||||||
|
|
||||||
All algorithm tuning is supported for Argon2id. The only configuration variables that affect
|
All algorithm tuning for Argon2id is supported. The only configuration variables that affect
|
||||||
SHA512 are iterations and salt length. The configuration variables are unique to the file
|
SHA512 are iterations and salt length. The configuration variables are unique to the file
|
||||||
authentication provider, thus they all exist in a key under the file authentication configuration
|
authentication provider, thus they all exist in a key under the file authentication configuration
|
||||||
key called `password_hashing`. We have set what are considered as sane and recommended defaults
|
key called `password`. We have set what are considered as sane and recommended defaults
|
||||||
to cater for a reasonable system, if you're unsure about which settings to tune, please see the
|
to cater for a reasonable system, if you're unsure about which settings to tune, please see the
|
||||||
parameters below, or for a more in depth understanding see the referenced documentation in
|
parameters below, or for a more in depth understanding see the referenced documentation in
|
||||||
[Argon2 links](./file.md#argon2-links).
|
[Argon2 links](./file.md#argon2-links).
|
||||||
|
|
|
@ -118,18 +118,18 @@ func (p *FileUserProvider) UpdatePassword(username string, newPassword string) e
|
||||||
}
|
}
|
||||||
|
|
||||||
var algorithm string
|
var algorithm string
|
||||||
if p.configuration.PasswordHashing.Algorithm == "argon2id" {
|
if p.configuration.Password.Algorithm == "argon2id" {
|
||||||
algorithm = HashingAlgorithmArgon2id
|
algorithm = HashingAlgorithmArgon2id
|
||||||
} else if p.configuration.PasswordHashing.Algorithm == "sha512" {
|
} else if p.configuration.Password.Algorithm == "sha512" {
|
||||||
algorithm = HashingAlgorithmSHA512
|
algorithm = HashingAlgorithmSHA512
|
||||||
} else {
|
} else {
|
||||||
return errors.New("Invalid algorithm in configuration. It should be `argon2id` or `sha512`")
|
return errors.New("Invalid algorithm in configuration. It should be `argon2id` or `sha512`")
|
||||||
}
|
}
|
||||||
|
|
||||||
hash, err := HashPassword(
|
hash, err := HashPassword(
|
||||||
newPassword, "", algorithm, p.configuration.PasswordHashing.Iterations,
|
newPassword, "", algorithm, p.configuration.Password.Iterations,
|
||||||
p.configuration.PasswordHashing.Memory*1024, p.configuration.PasswordHashing.Parallelism,
|
p.configuration.Password.Memory*1024, p.configuration.Password.Parallelism,
|
||||||
p.configuration.PasswordHashing.KeyLength, p.configuration.PasswordHashing.SaltLength)
|
p.configuration.Password.KeyLength, p.configuration.Password.SaltLength)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -131,8 +131,8 @@ func TestShouldUpdatePasswordHashingAlgorithmToSHA512(t *testing.T) {
|
||||||
WithDatabase(UserDatabaseContent, func(path string) {
|
WithDatabase(UserDatabaseContent, func(path string) {
|
||||||
config := DefaultFileAuthenticationBackendConfiguration
|
config := DefaultFileAuthenticationBackendConfiguration
|
||||||
config.Path = path
|
config.Path = path
|
||||||
config.PasswordHashing.Algorithm = "sha512"
|
config.Password.Algorithm = "sha512"
|
||||||
config.PasswordHashing.Iterations = 50000
|
config.Password.Iterations = 50000
|
||||||
|
|
||||||
provider := NewFileUserProvider(&config)
|
provider := NewFileUserProvider(&config)
|
||||||
assert.True(t, strings.HasPrefix(provider.database.Users["john"].HashedPassword, "{CRYPT}$argon2id$"))
|
assert.True(t, strings.HasPrefix(provider.database.Users["john"].HashedPassword, "{CRYPT}$argon2id$"))
|
||||||
|
@ -223,13 +223,13 @@ func TestShouldSupportHashPasswordWithoutCRYPT(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
DefaultFileAuthenticationBackendConfiguration = schema.FileAuthenticationBackendConfiguration{
|
DefaultFileAuthenticationBackendConfiguration = schema.FileAuthenticationBackendConfiguration{
|
||||||
Path: "",
|
Path: "",
|
||||||
PasswordHashing: &schema.PasswordHashingConfiguration{
|
Password: &schema.PasswordConfiguration{
|
||||||
Iterations: schema.DefaultCIPasswordOptionsConfiguration.Iterations,
|
Iterations: schema.DefaultCIPasswordConfiguration.Iterations,
|
||||||
KeyLength: schema.DefaultCIPasswordOptionsConfiguration.KeyLength,
|
KeyLength: schema.DefaultCIPasswordConfiguration.KeyLength,
|
||||||
SaltLength: schema.DefaultCIPasswordOptionsConfiguration.SaltLength,
|
SaltLength: schema.DefaultCIPasswordConfiguration.SaltLength,
|
||||||
Algorithm: schema.DefaultCIPasswordOptionsConfiguration.Algorithm,
|
Algorithm: schema.DefaultCIPasswordConfiguration.Algorithm,
|
||||||
Memory: schema.DefaultCIPasswordOptionsConfiguration.Memory,
|
Memory: schema.DefaultCIPasswordConfiguration.Memory,
|
||||||
Parallelism: schema.DefaultCIPasswordOptionsConfiguration.Parallelism,
|
Parallelism: schema.DefaultCIPasswordConfiguration.Parallelism,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -22,14 +22,14 @@ func TestShouldHashSHA512Password(t *testing.T) {
|
||||||
assert.Equal(t, "6", code)
|
assert.Equal(t, "6", code)
|
||||||
assert.Equal(t, "aFr56HjK3DrB8t3S", salt)
|
assert.Equal(t, "aFr56HjK3DrB8t3S", salt)
|
||||||
assert.Equal(t, "zhPQiS85cgBlNhUKKE6n/AHMlpqrvYSnSL3fEVkK0yHFQ.oFFAd8D4OhPAy18K5U61Z2eBhxQXExGU/eknXlY1", hash)
|
assert.Equal(t, "zhPQiS85cgBlNhUKKE6n/AHMlpqrvYSnSL3fEVkK0yHFQ.oFFAd8D4OhPAy18K5U61Z2eBhxQXExGU/eknXlY1", hash)
|
||||||
assert.Equal(t, schema.DefaultPasswordOptionsSHA512Configuration.Iterations, parameters.GetInt("rounds", HashingDefaultSHA512Iterations))
|
assert.Equal(t, schema.DefaultPasswordSHA512Configuration.Iterations, parameters.GetInt("rounds", HashingDefaultSHA512Iterations))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestShouldHashArgon2idPassword(t *testing.T) {
|
func TestShouldHashArgon2idPassword(t *testing.T) {
|
||||||
hash, err := HashPassword("password", "BpLnfgDsc2WD8F2q", HashingAlgorithmArgon2id,
|
hash, err := HashPassword("password", "BpLnfgDsc2WD8F2q", HashingAlgorithmArgon2id,
|
||||||
schema.DefaultCIPasswordOptionsConfiguration.Iterations, schema.DefaultCIPasswordOptionsConfiguration.Memory*1024,
|
schema.DefaultCIPasswordConfiguration.Iterations, schema.DefaultCIPasswordConfiguration.Memory*1024,
|
||||||
schema.DefaultCIPasswordOptionsConfiguration.Parallelism, schema.DefaultCIPasswordOptionsConfiguration.KeyLength,
|
schema.DefaultCIPasswordConfiguration.Parallelism, schema.DefaultCIPasswordConfiguration.KeyLength,
|
||||||
schema.DefaultCIPasswordOptionsConfiguration.SaltLength)
|
schema.DefaultCIPasswordConfiguration.SaltLength)
|
||||||
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
@ -39,10 +39,10 @@ func TestShouldHashArgon2idPassword(t *testing.T) {
|
||||||
assert.Equal(t, "argon2id", code)
|
assert.Equal(t, "argon2id", code)
|
||||||
assert.Equal(t, "BpLnfgDsc2WD8F2q", salt)
|
assert.Equal(t, "BpLnfgDsc2WD8F2q", salt)
|
||||||
assert.Equal(t, "O126GHPeZ5fwj7OLSs7PndXsTbje76R+QW9/EGfhkJg", key)
|
assert.Equal(t, "O126GHPeZ5fwj7OLSs7PndXsTbje76R+QW9/EGfhkJg", key)
|
||||||
assert.Equal(t, schema.DefaultCIPasswordOptionsConfiguration.Iterations, parameters.GetInt("t", HashingDefaultArgon2idTime))
|
assert.Equal(t, schema.DefaultCIPasswordConfiguration.Iterations, parameters.GetInt("t", HashingDefaultArgon2idTime))
|
||||||
assert.Equal(t, schema.DefaultCIPasswordOptionsConfiguration.Memory*1024, parameters.GetInt("m", HashingDefaultArgon2idMemory))
|
assert.Equal(t, schema.DefaultCIPasswordConfiguration.Memory*1024, parameters.GetInt("m", HashingDefaultArgon2idMemory))
|
||||||
assert.Equal(t, schema.DefaultCIPasswordOptionsConfiguration.Parallelism, parameters.GetInt("p", HashingDefaultArgon2idParallelism))
|
assert.Equal(t, schema.DefaultCIPasswordConfiguration.Parallelism, parameters.GetInt("p", HashingDefaultArgon2idParallelism))
|
||||||
assert.Equal(t, schema.DefaultCIPasswordOptionsConfiguration.KeyLength, parameters.GetInt("k", HashingDefaultArgon2idKeyLength))
|
assert.Equal(t, schema.DefaultCIPasswordConfiguration.KeyLength, parameters.GetInt("k", HashingDefaultArgon2idKeyLength))
|
||||||
}
|
}
|
||||||
|
|
||||||
// This checks the method of hashing (for argon2id) supports all the characters we allow in Authelia's hash function
|
// This checks the method of hashing (for argon2id) supports all the characters we allow in Authelia's hash function
|
||||||
|
@ -73,9 +73,9 @@ func TestSHA512HashSaltValidValues(t *testing.T) {
|
||||||
|
|
||||||
func TestShouldNotHashPasswordWithNonExistentAlgorithm(t *testing.T) {
|
func TestShouldNotHashPasswordWithNonExistentAlgorithm(t *testing.T) {
|
||||||
hash, err := HashPassword("password", "BpLnfgDsc2WD8F2q", "bogus",
|
hash, err := HashPassword("password", "BpLnfgDsc2WD8F2q", "bogus",
|
||||||
schema.DefaultCIPasswordOptionsConfiguration.Iterations, schema.DefaultCIPasswordOptionsConfiguration.Memory*1024,
|
schema.DefaultCIPasswordConfiguration.Iterations, schema.DefaultCIPasswordConfiguration.Memory*1024,
|
||||||
schema.DefaultCIPasswordOptionsConfiguration.Parallelism, schema.DefaultCIPasswordOptionsConfiguration.KeyLength,
|
schema.DefaultCIPasswordConfiguration.Parallelism, schema.DefaultCIPasswordConfiguration.KeyLength,
|
||||||
schema.DefaultCIPasswordOptionsConfiguration.SaltLength)
|
schema.DefaultCIPasswordConfiguration.SaltLength)
|
||||||
|
|
||||||
assert.Equal(t, "", hash)
|
assert.Equal(t, "", hash)
|
||||||
assert.EqualError(t, err, "Hashing algorithm input of 'bogus' is invalid, only values of argon2id and 6 are supported")
|
assert.EqualError(t, err, "Hashing algorithm input of 'bogus' is invalid, only values of argon2id and 6 are supported")
|
||||||
|
@ -83,8 +83,8 @@ func TestShouldNotHashPasswordWithNonExistentAlgorithm(t *testing.T) {
|
||||||
|
|
||||||
func TestShouldNotHashArgon2idPasswordDueToMemoryParallelismMismatch(t *testing.T) {
|
func TestShouldNotHashArgon2idPasswordDueToMemoryParallelismMismatch(t *testing.T) {
|
||||||
hash, err := HashPassword("password", "BpLnfgDsc2WD8F2q", HashingAlgorithmArgon2id,
|
hash, err := HashPassword("password", "BpLnfgDsc2WD8F2q", HashingAlgorithmArgon2id,
|
||||||
schema.DefaultCIPasswordOptionsConfiguration.Iterations, 8, 2,
|
schema.DefaultCIPasswordConfiguration.Iterations, 8, 2,
|
||||||
schema.DefaultCIPasswordOptionsConfiguration.KeyLength, schema.DefaultCIPasswordOptionsConfiguration.SaltLength)
|
schema.DefaultCIPasswordConfiguration.KeyLength, schema.DefaultCIPasswordConfiguration.SaltLength)
|
||||||
|
|
||||||
assert.Equal(t, "", hash)
|
assert.Equal(t, "", hash)
|
||||||
assert.EqualError(t, err, "Memory (argon2id) input of 8 is invalid with a parallelism input of 2, it must be 16 (parallelism * 8) or higher")
|
assert.EqualError(t, err, "Memory (argon2id) input of 8 is invalid with a parallelism input of 2, it must be 16 (parallelism * 8) or higher")
|
||||||
|
@ -92,8 +92,8 @@ func TestShouldNotHashArgon2idPasswordDueToMemoryParallelismMismatch(t *testing.
|
||||||
|
|
||||||
func TestShouldNotHashArgon2idPasswordDueToMemoryLessThanEight(t *testing.T) {
|
func TestShouldNotHashArgon2idPasswordDueToMemoryLessThanEight(t *testing.T) {
|
||||||
hash, err := HashPassword("password", "BpLnfgDsc2WD8F2q", HashingAlgorithmArgon2id,
|
hash, err := HashPassword("password", "BpLnfgDsc2WD8F2q", HashingAlgorithmArgon2id,
|
||||||
schema.DefaultCIPasswordOptionsConfiguration.Iterations, 1, schema.DefaultCIPasswordOptionsConfiguration.Parallelism,
|
schema.DefaultCIPasswordConfiguration.Iterations, 1, schema.DefaultCIPasswordConfiguration.Parallelism,
|
||||||
schema.DefaultCIPasswordOptionsConfiguration.KeyLength, schema.DefaultCIPasswordOptionsConfiguration.SaltLength)
|
schema.DefaultCIPasswordConfiguration.KeyLength, schema.DefaultCIPasswordConfiguration.SaltLength)
|
||||||
|
|
||||||
assert.Equal(t, "", hash)
|
assert.Equal(t, "", hash)
|
||||||
assert.EqualError(t, err, "Memory (argon2id) input of 1 is invalid, it must be 8 or higher")
|
assert.EqualError(t, err, "Memory (argon2id) input of 1 is invalid, it must be 8 or higher")
|
||||||
|
@ -101,8 +101,8 @@ func TestShouldNotHashArgon2idPasswordDueToMemoryLessThanEight(t *testing.T) {
|
||||||
|
|
||||||
func TestShouldNotHashArgon2idPasswordDueToKeyLengthLessThanSixteen(t *testing.T) {
|
func TestShouldNotHashArgon2idPasswordDueToKeyLengthLessThanSixteen(t *testing.T) {
|
||||||
hash, err := HashPassword("password", "BpLnfgDsc2WD8F2q", HashingAlgorithmArgon2id,
|
hash, err := HashPassword("password", "BpLnfgDsc2WD8F2q", HashingAlgorithmArgon2id,
|
||||||
schema.DefaultCIPasswordOptionsConfiguration.Iterations, schema.DefaultCIPasswordOptionsConfiguration.Memory*1024,
|
schema.DefaultCIPasswordConfiguration.Iterations, schema.DefaultCIPasswordConfiguration.Memory*1024,
|
||||||
schema.DefaultCIPasswordOptionsConfiguration.Parallelism, 5, schema.DefaultCIPasswordOptionsConfiguration.SaltLength)
|
schema.DefaultCIPasswordConfiguration.Parallelism, 5, schema.DefaultCIPasswordConfiguration.SaltLength)
|
||||||
|
|
||||||
assert.Equal(t, "", hash)
|
assert.Equal(t, "", hash)
|
||||||
assert.EqualError(t, err, "Key length (argon2id) input of 5 is invalid, it must be 16 or higher")
|
assert.EqualError(t, err, "Key length (argon2id) input of 5 is invalid, it must be 16 or higher")
|
||||||
|
@ -110,8 +110,8 @@ func TestShouldNotHashArgon2idPasswordDueToKeyLengthLessThanSixteen(t *testing.T
|
||||||
|
|
||||||
func TestShouldNotHashArgon2idPasswordDueParallelismLessThanOne(t *testing.T) {
|
func TestShouldNotHashArgon2idPasswordDueParallelismLessThanOne(t *testing.T) {
|
||||||
hash, err := HashPassword("password", "BpLnfgDsc2WD8F2q", HashingAlgorithmArgon2id,
|
hash, err := HashPassword("password", "BpLnfgDsc2WD8F2q", HashingAlgorithmArgon2id,
|
||||||
schema.DefaultCIPasswordOptionsConfiguration.Iterations, schema.DefaultCIPasswordOptionsConfiguration.Memory*1024, -1,
|
schema.DefaultCIPasswordConfiguration.Iterations, schema.DefaultCIPasswordConfiguration.Memory*1024, -1,
|
||||||
schema.DefaultCIPasswordOptionsConfiguration.KeyLength, schema.DefaultCIPasswordOptionsConfiguration.SaltLength)
|
schema.DefaultCIPasswordConfiguration.KeyLength, schema.DefaultCIPasswordConfiguration.SaltLength)
|
||||||
|
|
||||||
assert.Equal(t, "", hash)
|
assert.Equal(t, "", hash)
|
||||||
assert.EqualError(t, err, "Parallelism (argon2id) input of -1 is invalid, it must be 1 or higher")
|
assert.EqualError(t, err, "Parallelism (argon2id) input of -1 is invalid, it must be 1 or higher")
|
||||||
|
@ -119,8 +119,8 @@ func TestShouldNotHashArgon2idPasswordDueParallelismLessThanOne(t *testing.T) {
|
||||||
|
|
||||||
func TestShouldNotHashArgon2idPasswordDueIterationsLessThanOne(t *testing.T) {
|
func TestShouldNotHashArgon2idPasswordDueIterationsLessThanOne(t *testing.T) {
|
||||||
hash, err := HashPassword("password", "BpLnfgDsc2WD8F2q", HashingAlgorithmArgon2id,
|
hash, err := HashPassword("password", "BpLnfgDsc2WD8F2q", HashingAlgorithmArgon2id,
|
||||||
0, schema.DefaultCIPasswordOptionsConfiguration.Memory*1024, schema.DefaultCIPasswordOptionsConfiguration.Parallelism,
|
0, schema.DefaultCIPasswordConfiguration.Memory*1024, schema.DefaultCIPasswordConfiguration.Parallelism,
|
||||||
schema.DefaultCIPasswordOptionsConfiguration.KeyLength, schema.DefaultCIPasswordOptionsConfiguration.SaltLength)
|
schema.DefaultCIPasswordConfiguration.KeyLength, schema.DefaultCIPasswordConfiguration.SaltLength)
|
||||||
|
|
||||||
assert.Equal(t, "", hash)
|
assert.Equal(t, "", hash)
|
||||||
assert.EqualError(t, err, "Iterations (argon2id) input of 0 is invalid, it must be 1 or more")
|
assert.EqualError(t, err, "Iterations (argon2id) input of 0 is invalid, it must be 1 or more")
|
||||||
|
@ -128,15 +128,15 @@ func TestShouldNotHashArgon2idPasswordDueIterationsLessThanOne(t *testing.T) {
|
||||||
|
|
||||||
func TestShouldNotHashPasswordDueToSaltLength(t *testing.T) {
|
func TestShouldNotHashPasswordDueToSaltLength(t *testing.T) {
|
||||||
hash, err := HashPassword("password", "", HashingAlgorithmArgon2id,
|
hash, err := HashPassword("password", "", HashingAlgorithmArgon2id,
|
||||||
schema.DefaultCIPasswordOptionsConfiguration.Iterations, schema.DefaultCIPasswordOptionsConfiguration.Memory*1024,
|
schema.DefaultCIPasswordConfiguration.Iterations, schema.DefaultCIPasswordConfiguration.Memory*1024,
|
||||||
schema.DefaultCIPasswordOptionsConfiguration.Parallelism, schema.DefaultCIPasswordOptionsConfiguration.KeyLength, 0)
|
schema.DefaultCIPasswordConfiguration.Parallelism, schema.DefaultCIPasswordConfiguration.KeyLength, 0)
|
||||||
|
|
||||||
assert.Equal(t, "", hash)
|
assert.Equal(t, "", hash)
|
||||||
assert.EqualError(t, err, "Salt length input of 0 is invalid, it must be 2 or higher")
|
assert.EqualError(t, err, "Salt length input of 0 is invalid, it must be 2 or higher")
|
||||||
|
|
||||||
hash, err = HashPassword("password", "", HashingAlgorithmArgon2id,
|
hash, err = HashPassword("password", "", HashingAlgorithmArgon2id,
|
||||||
schema.DefaultCIPasswordOptionsConfiguration.Iterations, schema.DefaultCIPasswordOptionsConfiguration.Memory*1024,
|
schema.DefaultCIPasswordConfiguration.Iterations, schema.DefaultCIPasswordConfiguration.Memory*1024,
|
||||||
schema.DefaultCIPasswordOptionsConfiguration.Parallelism, schema.DefaultCIPasswordOptionsConfiguration.KeyLength, 20)
|
schema.DefaultCIPasswordConfiguration.Parallelism, schema.DefaultCIPasswordConfiguration.KeyLength, 20)
|
||||||
|
|
||||||
assert.Equal(t, "", hash)
|
assert.Equal(t, "", hash)
|
||||||
assert.EqualError(t, err, "Salt length input of 20 is invalid, it must be 16 or lower")
|
assert.EqualError(t, err, "Salt length input of 20 is invalid, it must be 16 or lower")
|
||||||
|
@ -144,27 +144,27 @@ func TestShouldNotHashPasswordDueToSaltLength(t *testing.T) {
|
||||||
|
|
||||||
func TestShouldNotHashPasswordDueToSaltCharLengthTooLong(t *testing.T) {
|
func TestShouldNotHashPasswordDueToSaltCharLengthTooLong(t *testing.T) {
|
||||||
hash, err := HashPassword("password", "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", HashingAlgorithmArgon2id,
|
hash, err := HashPassword("password", "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", HashingAlgorithmArgon2id,
|
||||||
schema.DefaultCIPasswordOptionsConfiguration.Iterations, schema.DefaultCIPasswordOptionsConfiguration.Memory*1024,
|
schema.DefaultCIPasswordConfiguration.Iterations, schema.DefaultCIPasswordConfiguration.Memory*1024,
|
||||||
schema.DefaultCIPasswordOptionsConfiguration.Parallelism, schema.DefaultCIPasswordOptionsConfiguration.KeyLength,
|
schema.DefaultCIPasswordConfiguration.Parallelism, schema.DefaultCIPasswordConfiguration.KeyLength,
|
||||||
schema.DefaultCIPasswordOptionsConfiguration.SaltLength)
|
schema.DefaultCIPasswordConfiguration.SaltLength)
|
||||||
assert.Equal(t, "", hash)
|
assert.Equal(t, "", hash)
|
||||||
assert.EqualError(t, err, "Salt input of abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 is invalid (62 characters), it must be 16 or fewer characters")
|
assert.EqualError(t, err, "Salt input of abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 is invalid (62 characters), it must be 16 or fewer characters")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestShouldNotHashPasswordDueToSaltCharLengthTooShort(t *testing.T) {
|
func TestShouldNotHashPasswordDueToSaltCharLengthTooShort(t *testing.T) {
|
||||||
hash, err := HashPassword("password", "a", HashingAlgorithmArgon2id,
|
hash, err := HashPassword("password", "a", HashingAlgorithmArgon2id,
|
||||||
schema.DefaultCIPasswordOptionsConfiguration.Iterations, schema.DefaultCIPasswordOptionsConfiguration.Memory*1024,
|
schema.DefaultCIPasswordConfiguration.Iterations, schema.DefaultCIPasswordConfiguration.Memory*1024,
|
||||||
schema.DefaultCIPasswordOptionsConfiguration.Parallelism, schema.DefaultCIPasswordOptionsConfiguration.KeyLength,
|
schema.DefaultCIPasswordConfiguration.Parallelism, schema.DefaultCIPasswordConfiguration.KeyLength,
|
||||||
schema.DefaultCIPasswordOptionsConfiguration.SaltLength)
|
schema.DefaultCIPasswordConfiguration.SaltLength)
|
||||||
assert.Equal(t, "", hash)
|
assert.Equal(t, "", hash)
|
||||||
assert.EqualError(t, err, "Salt input of a is invalid (1 characters), it must be 2 or more characters")
|
assert.EqualError(t, err, "Salt input of a is invalid (1 characters), it must be 2 or more characters")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestShouldNotHashPasswordWithNonBase64CharsInSalt(t *testing.T) {
|
func TestShouldNotHashPasswordWithNonBase64CharsInSalt(t *testing.T) {
|
||||||
hash, err := HashPassword("password", "abc&123", HashingAlgorithmArgon2id,
|
hash, err := HashPassword("password", "abc&123", HashingAlgorithmArgon2id,
|
||||||
schema.DefaultCIPasswordOptionsConfiguration.Iterations, schema.DefaultCIPasswordOptionsConfiguration.Memory*1024,
|
schema.DefaultCIPasswordConfiguration.Iterations, schema.DefaultCIPasswordConfiguration.Memory*1024,
|
||||||
schema.DefaultCIPasswordOptionsConfiguration.Parallelism, schema.DefaultCIPasswordOptionsConfiguration.KeyLength,
|
schema.DefaultCIPasswordConfiguration.Parallelism, schema.DefaultCIPasswordConfiguration.KeyLength,
|
||||||
schema.DefaultCIPasswordOptionsConfiguration.SaltLength)
|
schema.DefaultCIPasswordConfiguration.SaltLength)
|
||||||
assert.Equal(t, "", hash)
|
assert.Equal(t, "", hash)
|
||||||
assert.EqualError(t, err, "Salt input of abc&123 is invalid, only characters [a-zA-Z0-9+/] are valid for input")
|
assert.EqualError(t, err, "Salt input of abc&123 is invalid, only characters [a-zA-Z0-9+/] are valid for input")
|
||||||
}
|
}
|
||||||
|
@ -223,10 +223,10 @@ func TestShouldNotParseArgon2idHashWithWrongKeyLength(t *testing.T) {
|
||||||
func TestShouldParseArgon2idHash(t *testing.T) {
|
func TestShouldParseArgon2idHash(t *testing.T) {
|
||||||
passwordHash, err := ParseHash("$argon2id$v=19$m=131072,t=1,p=8$BpLnfgDsc2WD8F2q$G4fD5nJwXHDMS+u0eEMKvU0LF23jxbSmJSxhSLTteHE")
|
passwordHash, err := ParseHash("$argon2id$v=19$m=131072,t=1,p=8$BpLnfgDsc2WD8F2q$G4fD5nJwXHDMS+u0eEMKvU0LF23jxbSmJSxhSLTteHE")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, schema.DefaultCIPasswordOptionsConfiguration.Iterations, passwordHash.Iterations)
|
assert.Equal(t, schema.DefaultCIPasswordConfiguration.Iterations, passwordHash.Iterations)
|
||||||
assert.Equal(t, schema.DefaultCIPasswordOptionsConfiguration.Parallelism, passwordHash.Parallelism)
|
assert.Equal(t, schema.DefaultCIPasswordConfiguration.Parallelism, passwordHash.Parallelism)
|
||||||
assert.Equal(t, schema.DefaultCIPasswordOptionsConfiguration.KeyLength, passwordHash.KeyLength)
|
assert.Equal(t, schema.DefaultCIPasswordConfiguration.KeyLength, passwordHash.KeyLength)
|
||||||
assert.Equal(t, schema.DefaultCIPasswordOptionsConfiguration.Memory*1024, passwordHash.Memory)
|
assert.Equal(t, schema.DefaultCIPasswordConfiguration.Memory*1024, passwordHash.Memory)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestShouldCheckSHA512Password(t *testing.T) {
|
func TestShouldCheckSHA512Password(t *testing.T) {
|
||||||
|
@ -300,9 +300,9 @@ func TestNumberOfRoundsNotInt(t *testing.T) {
|
||||||
|
|
||||||
func TestShouldCheckPasswordArgon2idHashedWithAuthelia(t *testing.T) {
|
func TestShouldCheckPasswordArgon2idHashedWithAuthelia(t *testing.T) {
|
||||||
password := "my;secure*password"
|
password := "my;secure*password"
|
||||||
hash, err := HashPassword(password, "", HashingAlgorithmArgon2id, schema.DefaultCIPasswordOptionsConfiguration.Iterations,
|
hash, err := HashPassword(password, "", HashingAlgorithmArgon2id, schema.DefaultCIPasswordConfiguration.Iterations,
|
||||||
schema.DefaultCIPasswordOptionsConfiguration.Memory*1024, schema.DefaultCIPasswordOptionsConfiguration.Parallelism,
|
schema.DefaultCIPasswordConfiguration.Memory*1024, schema.DefaultCIPasswordConfiguration.Parallelism,
|
||||||
schema.DefaultCIPasswordOptionsConfiguration.KeyLength, schema.DefaultCIPasswordOptionsConfiguration.SaltLength)
|
schema.DefaultCIPasswordConfiguration.KeyLength, schema.DefaultCIPasswordConfiguration.SaltLength)
|
||||||
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
@ -314,8 +314,8 @@ func TestShouldCheckPasswordArgon2idHashedWithAuthelia(t *testing.T) {
|
||||||
|
|
||||||
func TestShouldCheckPasswordSHA512HashedWithAuthelia(t *testing.T) {
|
func TestShouldCheckPasswordSHA512HashedWithAuthelia(t *testing.T) {
|
||||||
password := "my;secure*password"
|
password := "my;secure*password"
|
||||||
hash, err := HashPassword(password, "", HashingAlgorithmSHA512, schema.DefaultPasswordOptionsSHA512Configuration.Iterations,
|
hash, err := HashPassword(password, "", HashingAlgorithmSHA512, schema.DefaultPasswordSHA512Configuration.Iterations,
|
||||||
0, 0, 0, schema.DefaultPasswordOptionsSHA512Configuration.SaltLength)
|
0, 0, 0, schema.DefaultPasswordSHA512Configuration.SaltLength)
|
||||||
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
|
|
@ -10,13 +10,13 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
HashPasswordCmd.Flags().BoolP("sha512", "z", false, fmt.Sprintf("use sha512 as the algorithm (changes iterations to %d, change with -i)", schema.DefaultPasswordOptionsSHA512Configuration.Iterations))
|
HashPasswordCmd.Flags().BoolP("sha512", "z", false, fmt.Sprintf("use sha512 as the algorithm (changes iterations to %d, change with -i)", schema.DefaultPasswordSHA512Configuration.Iterations))
|
||||||
HashPasswordCmd.Flags().IntP("iterations", "i", schema.DefaultPasswordOptionsConfiguration.Iterations, "set the number of hashing iterations")
|
HashPasswordCmd.Flags().IntP("iterations", "i", schema.DefaultPasswordConfiguration.Iterations, "set the number of hashing iterations")
|
||||||
HashPasswordCmd.Flags().StringP("salt", "s", "", "set the salt string")
|
HashPasswordCmd.Flags().StringP("salt", "s", "", "set the salt string")
|
||||||
HashPasswordCmd.Flags().IntP("memory", "m", schema.DefaultPasswordOptionsConfiguration.Memory, "[argon2id] set the amount of memory param (in MB)")
|
HashPasswordCmd.Flags().IntP("memory", "m", schema.DefaultPasswordConfiguration.Memory, "[argon2id] set the amount of memory param (in MB)")
|
||||||
HashPasswordCmd.Flags().IntP("parallelism", "p", schema.DefaultPasswordOptionsConfiguration.Parallelism, "[argon2id] set the parallelism param")
|
HashPasswordCmd.Flags().IntP("parallelism", "p", schema.DefaultPasswordConfiguration.Parallelism, "[argon2id] set the parallelism param")
|
||||||
HashPasswordCmd.Flags().IntP("key-length", "k", schema.DefaultPasswordOptionsConfiguration.KeyLength, "[argon2id] set the key length param")
|
HashPasswordCmd.Flags().IntP("key-length", "k", schema.DefaultPasswordConfiguration.KeyLength, "[argon2id] set the key length param")
|
||||||
HashPasswordCmd.Flags().IntP("salt-length", "l", schema.DefaultPasswordOptionsConfiguration.SaltLength, "set the auto-generated salt length")
|
HashPasswordCmd.Flags().IntP("salt-length", "l", schema.DefaultPasswordConfiguration.SaltLength, "set the auto-generated salt length")
|
||||||
}
|
}
|
||||||
|
|
||||||
var HashPasswordCmd = &cobra.Command{
|
var HashPasswordCmd = &cobra.Command{
|
||||||
|
@ -36,8 +36,8 @@ var HashPasswordCmd = &cobra.Command{
|
||||||
var algorithm string
|
var algorithm string
|
||||||
|
|
||||||
if sha512 {
|
if sha512 {
|
||||||
if iterations == schema.DefaultPasswordOptionsConfiguration.Iterations {
|
if iterations == schema.DefaultPasswordConfiguration.Iterations {
|
||||||
iterations = schema.DefaultPasswordOptionsSHA512Configuration.Iterations
|
iterations = schema.DefaultPasswordSHA512Configuration.Iterations
|
||||||
}
|
}
|
||||||
algorithm = authentication.HashingAlgorithmSHA512
|
algorithm = authentication.HashingAlgorithmSHA512
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -19,10 +19,10 @@ type LDAPAuthenticationBackendConfiguration struct {
|
||||||
// FileAuthenticationBackendConfiguration represents the configuration related to file-based backend
|
// FileAuthenticationBackendConfiguration represents the configuration related to file-based backend
|
||||||
type FileAuthenticationBackendConfiguration struct {
|
type FileAuthenticationBackendConfiguration struct {
|
||||||
Path string `mapstructure:"path"`
|
Path string `mapstructure:"path"`
|
||||||
PasswordHashing *PasswordHashingConfiguration `mapstructure:"password"`
|
Password *PasswordConfiguration `mapstructure:"password"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type PasswordHashingConfiguration struct {
|
type PasswordConfiguration struct {
|
||||||
Iterations int `mapstructure:"iterations"`
|
Iterations int `mapstructure:"iterations"`
|
||||||
KeyLength int `mapstructure:"key_length"`
|
KeyLength int `mapstructure:"key_length"`
|
||||||
SaltLength int `mapstructure:"salt_length"`
|
SaltLength int `mapstructure:"salt_length"`
|
||||||
|
@ -31,8 +31,8 @@ type PasswordHashingConfiguration struct {
|
||||||
Parallelism int `mapstructure:"parallelism"`
|
Parallelism int `mapstructure:"parallelism"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultPasswordOptionsConfiguration represents the default configuration related to Argon2id hashing
|
// DefaultPasswordConfiguration represents the default configuration related to Argon2id hashing
|
||||||
var DefaultPasswordOptionsConfiguration = PasswordHashingConfiguration{
|
var DefaultPasswordConfiguration = PasswordConfiguration{
|
||||||
Iterations: 1,
|
Iterations: 1,
|
||||||
KeyLength: 32,
|
KeyLength: 32,
|
||||||
SaltLength: 16,
|
SaltLength: 16,
|
||||||
|
@ -41,8 +41,8 @@ var DefaultPasswordOptionsConfiguration = PasswordHashingConfiguration{
|
||||||
Parallelism: 8,
|
Parallelism: 8,
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultCIPasswordOptionsConfiguration represents the default configuration related to Argon2id hashing for CI
|
// DefaultCIPasswordConfiguration represents the default configuration related to Argon2id hashing for CI
|
||||||
var DefaultCIPasswordOptionsConfiguration = PasswordHashingConfiguration{
|
var DefaultCIPasswordConfiguration = PasswordConfiguration{
|
||||||
Iterations: 1,
|
Iterations: 1,
|
||||||
KeyLength: 32,
|
KeyLength: 32,
|
||||||
SaltLength: 16,
|
SaltLength: 16,
|
||||||
|
@ -51,8 +51,8 @@ var DefaultCIPasswordOptionsConfiguration = PasswordHashingConfiguration{
|
||||||
Parallelism: 8,
|
Parallelism: 8,
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultPasswordOptionsSHA512Configuration represents the default configuration related to SHA512 hashing
|
// DefaultPasswordSHA512Configuration represents the default configuration related to SHA512 hashing
|
||||||
var DefaultPasswordOptionsSHA512Configuration = PasswordHashingConfiguration{
|
var DefaultPasswordSHA512Configuration = PasswordConfiguration{
|
||||||
Iterations: 50000,
|
Iterations: 50000,
|
||||||
SaltLength: 16,
|
SaltLength: 16,
|
||||||
Algorithm: "sha512",
|
Algorithm: "sha512",
|
||||||
|
|
|
@ -15,58 +15,58 @@ func validateFileAuthenticationBackend(configuration *schema.FileAuthenticationB
|
||||||
validator.Push(errors.New("Please provide a `path` for the users database in `authentication_backend`"))
|
validator.Push(errors.New("Please provide a `path` for the users database in `authentication_backend`"))
|
||||||
}
|
}
|
||||||
|
|
||||||
if configuration.PasswordHashing == nil {
|
if configuration.Password == nil {
|
||||||
configuration.PasswordHashing = &schema.DefaultPasswordOptionsConfiguration
|
configuration.Password = &schema.DefaultPasswordConfiguration
|
||||||
} else {
|
} else {
|
||||||
if configuration.PasswordHashing.Algorithm == "" {
|
if configuration.Password.Algorithm == "" {
|
||||||
configuration.PasswordHashing.Algorithm = schema.DefaultPasswordOptionsConfiguration.Algorithm
|
configuration.Password.Algorithm = schema.DefaultPasswordConfiguration.Algorithm
|
||||||
} else {
|
} else {
|
||||||
configuration.PasswordHashing.Algorithm = strings.ToLower(configuration.PasswordHashing.Algorithm)
|
configuration.Password.Algorithm = strings.ToLower(configuration.Password.Algorithm)
|
||||||
if configuration.PasswordHashing.Algorithm != "argon2id" && configuration.PasswordHashing.Algorithm != "sha512" {
|
if configuration.Password.Algorithm != "argon2id" && configuration.Password.Algorithm != "sha512" {
|
||||||
validator.Push(fmt.Errorf("Unknown hashing algorithm supplied, valid values are argon2id and sha512, you configured '%s'", configuration.PasswordHashing.Algorithm))
|
validator.Push(fmt.Errorf("Unknown hashing algorithm supplied, valid values are argon2id and sha512, you configured '%s'", configuration.Password.Algorithm))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iterations (time)
|
// Iterations (time)
|
||||||
if configuration.PasswordHashing.Iterations == 0 {
|
if configuration.Password.Iterations == 0 {
|
||||||
if configuration.PasswordHashing.Algorithm == "argon2id" {
|
if configuration.Password.Algorithm == "argon2id" {
|
||||||
configuration.PasswordHashing.Iterations = schema.DefaultPasswordOptionsConfiguration.Iterations
|
configuration.Password.Iterations = schema.DefaultPasswordConfiguration.Iterations
|
||||||
} else {
|
} else {
|
||||||
configuration.PasswordHashing.Iterations = schema.DefaultPasswordOptionsSHA512Configuration.Iterations
|
configuration.Password.Iterations = schema.DefaultPasswordSHA512Configuration.Iterations
|
||||||
}
|
}
|
||||||
} else if configuration.PasswordHashing.Iterations < 1 {
|
} else if configuration.Password.Iterations < 1 {
|
||||||
validator.Push(fmt.Errorf("The number of iterations specified is invalid, must be 1 or more, you configured %d", configuration.PasswordHashing.Iterations))
|
validator.Push(fmt.Errorf("The number of iterations specified is invalid, must be 1 or more, you configured %d", configuration.Password.Iterations))
|
||||||
}
|
}
|
||||||
|
|
||||||
//Salt Length
|
//Salt Length
|
||||||
if configuration.PasswordHashing.SaltLength == 0 {
|
if configuration.Password.SaltLength == 0 {
|
||||||
configuration.PasswordHashing.SaltLength = schema.DefaultPasswordOptionsConfiguration.SaltLength
|
configuration.Password.SaltLength = schema.DefaultPasswordConfiguration.SaltLength
|
||||||
} else if configuration.PasswordHashing.SaltLength < 2 {
|
} else if configuration.Password.SaltLength < 2 {
|
||||||
validator.Push(fmt.Errorf("The salt length must be 2 or more, you configured %d", configuration.PasswordHashing.SaltLength))
|
validator.Push(fmt.Errorf("The salt length must be 2 or more, you configured %d", configuration.Password.SaltLength))
|
||||||
} else if configuration.PasswordHashing.SaltLength > 16 {
|
} else if configuration.Password.SaltLength > 16 {
|
||||||
validator.Push(fmt.Errorf("The salt length must be 16 or less, you configured %d", configuration.PasswordHashing.SaltLength))
|
validator.Push(fmt.Errorf("The salt length must be 16 or less, you configured %d", configuration.Password.SaltLength))
|
||||||
}
|
}
|
||||||
|
|
||||||
if configuration.PasswordHashing.Algorithm == "argon2id" {
|
if configuration.Password.Algorithm == "argon2id" {
|
||||||
// Parallelism
|
// Parallelism
|
||||||
if configuration.PasswordHashing.Parallelism == 0 {
|
if configuration.Password.Parallelism == 0 {
|
||||||
configuration.PasswordHashing.Parallelism = schema.DefaultPasswordOptionsConfiguration.Parallelism
|
configuration.Password.Parallelism = schema.DefaultPasswordConfiguration.Parallelism
|
||||||
} else if configuration.PasswordHashing.Parallelism < 1 {
|
} else if configuration.Password.Parallelism < 1 {
|
||||||
validator.Push(fmt.Errorf("Parallelism for argon2id must be 1 or more, you configured %d", configuration.PasswordHashing.Parallelism))
|
validator.Push(fmt.Errorf("Parallelism for argon2id must be 1 or more, you configured %d", configuration.Password.Parallelism))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Memory
|
// Memory
|
||||||
if configuration.PasswordHashing.Memory == 0 {
|
if configuration.Password.Memory == 0 {
|
||||||
configuration.PasswordHashing.Memory = schema.DefaultPasswordOptionsConfiguration.Memory
|
configuration.Password.Memory = schema.DefaultPasswordConfiguration.Memory
|
||||||
} else if configuration.PasswordHashing.Memory < configuration.PasswordHashing.Parallelism*8 {
|
} else if configuration.Password.Memory < configuration.Password.Parallelism*8 {
|
||||||
validator.Push(fmt.Errorf("Memory for argon2id must be %d or more (parallelism * 8), you configured memory as %d and parallelism as %d", configuration.PasswordHashing.Parallelism*8, configuration.PasswordHashing.Memory, configuration.PasswordHashing.Parallelism))
|
validator.Push(fmt.Errorf("Memory for argon2id must be %d or more (parallelism * 8), you configured memory as %d and parallelism as %d", configuration.Password.Parallelism*8, configuration.Password.Memory, configuration.Password.Parallelism))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Key Length
|
// Key Length
|
||||||
if configuration.PasswordHashing.KeyLength == 0 {
|
if configuration.Password.KeyLength == 0 {
|
||||||
configuration.PasswordHashing.KeyLength = schema.DefaultPasswordOptionsConfiguration.KeyLength
|
configuration.Password.KeyLength = schema.DefaultPasswordConfiguration.KeyLength
|
||||||
} else if configuration.PasswordHashing.KeyLength < 16 {
|
} else if configuration.Password.KeyLength < 16 {
|
||||||
validator.Push(fmt.Errorf("Key length for argon2id must be 16, you configured %d", configuration.PasswordHashing.KeyLength))
|
validator.Push(fmt.Errorf("Key length for argon2id must be 16, you configured %d", configuration.Password.KeyLength))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,15 +29,15 @@ type FileBasedAuthenticationBackend struct {
|
||||||
func (suite *FileBasedAuthenticationBackend) SetupTest() {
|
func (suite *FileBasedAuthenticationBackend) SetupTest() {
|
||||||
suite.validator = schema.NewStructValidator()
|
suite.validator = schema.NewStructValidator()
|
||||||
suite.configuration = schema.AuthenticationBackendConfiguration{}
|
suite.configuration = schema.AuthenticationBackendConfiguration{}
|
||||||
suite.configuration.File = &schema.FileAuthenticationBackendConfiguration{Path: "/a/path", PasswordHashing: &schema.PasswordHashingConfiguration{
|
suite.configuration.File = &schema.FileAuthenticationBackendConfiguration{Path: "/a/path", Password: &schema.PasswordConfiguration{
|
||||||
Algorithm: schema.DefaultPasswordOptionsConfiguration.Algorithm,
|
Algorithm: schema.DefaultPasswordConfiguration.Algorithm,
|
||||||
Iterations: schema.DefaultPasswordOptionsConfiguration.Iterations,
|
Iterations: schema.DefaultPasswordConfiguration.Iterations,
|
||||||
Parallelism: schema.DefaultPasswordOptionsConfiguration.Parallelism,
|
Parallelism: schema.DefaultPasswordConfiguration.Parallelism,
|
||||||
Memory: schema.DefaultPasswordOptionsConfiguration.Memory,
|
Memory: schema.DefaultPasswordConfiguration.Memory,
|
||||||
KeyLength: schema.DefaultPasswordOptionsConfiguration.KeyLength,
|
KeyLength: schema.DefaultPasswordConfiguration.KeyLength,
|
||||||
SaltLength: schema.DefaultPasswordOptionsConfiguration.SaltLength,
|
SaltLength: schema.DefaultPasswordConfiguration.SaltLength,
|
||||||
}}
|
}}
|
||||||
suite.configuration.File.PasswordHashing.Algorithm = schema.DefaultPasswordOptionsConfiguration.Algorithm
|
suite.configuration.File.Password.Algorithm = schema.DefaultPasswordConfiguration.Algorithm
|
||||||
}
|
}
|
||||||
func (suite *FileBasedAuthenticationBackend) TestShouldValidateCompleteConfiguration() {
|
func (suite *FileBasedAuthenticationBackend) TestShouldValidateCompleteConfiguration() {
|
||||||
ValidateAuthenticationBackend(&suite.configuration, suite.validator)
|
ValidateAuthenticationBackend(&suite.configuration, suite.validator)
|
||||||
|
@ -52,104 +52,104 @@ func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorWhenNoPathProvi
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorWhenMemoryNotMoreThanEightTimesParallelism() {
|
func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorWhenMemoryNotMoreThanEightTimesParallelism() {
|
||||||
suite.configuration.File.PasswordHashing.Memory = 8
|
suite.configuration.File.Password.Memory = 8
|
||||||
suite.configuration.File.PasswordHashing.Parallelism = 2
|
suite.configuration.File.Password.Parallelism = 2
|
||||||
ValidateAuthenticationBackend(&suite.configuration, suite.validator)
|
ValidateAuthenticationBackend(&suite.configuration, suite.validator)
|
||||||
assert.Len(suite.T(), suite.validator.Errors(), 1)
|
assert.Len(suite.T(), suite.validator.Errors(), 1)
|
||||||
assert.EqualError(suite.T(), suite.validator.Errors()[0], "Memory for argon2id must be 16 or more (parallelism * 8), you configured memory as 8 and parallelism as 2")
|
assert.EqualError(suite.T(), suite.validator.Errors()[0], "Memory for argon2id must be 16 or more (parallelism * 8), you configured memory as 8 and parallelism as 2")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *FileBasedAuthenticationBackend) TestShouldSetDefaultConfigurationWhenBlank() {
|
func (suite *FileBasedAuthenticationBackend) TestShouldSetDefaultConfigurationWhenBlank() {
|
||||||
suite.configuration.File.PasswordHashing = &schema.PasswordHashingConfiguration{}
|
suite.configuration.File.Password = &schema.PasswordConfiguration{}
|
||||||
|
|
||||||
assert.Equal(suite.T(), 0, suite.configuration.File.PasswordHashing.KeyLength)
|
assert.Equal(suite.T(), 0, suite.configuration.File.Password.KeyLength)
|
||||||
assert.Equal(suite.T(), 0, suite.configuration.File.PasswordHashing.Iterations)
|
assert.Equal(suite.T(), 0, suite.configuration.File.Password.Iterations)
|
||||||
assert.Equal(suite.T(), 0, suite.configuration.File.PasswordHashing.SaltLength)
|
assert.Equal(suite.T(), 0, suite.configuration.File.Password.SaltLength)
|
||||||
assert.Equal(suite.T(), "", suite.configuration.File.PasswordHashing.Algorithm)
|
assert.Equal(suite.T(), "", suite.configuration.File.Password.Algorithm)
|
||||||
assert.Equal(suite.T(), 0, suite.configuration.File.PasswordHashing.Memory)
|
assert.Equal(suite.T(), 0, suite.configuration.File.Password.Memory)
|
||||||
assert.Equal(suite.T(), 0, suite.configuration.File.PasswordHashing.Parallelism)
|
assert.Equal(suite.T(), 0, suite.configuration.File.Password.Parallelism)
|
||||||
|
|
||||||
ValidateAuthenticationBackend(&suite.configuration, suite.validator)
|
ValidateAuthenticationBackend(&suite.configuration, suite.validator)
|
||||||
|
|
||||||
assert.Len(suite.T(), suite.validator.Errors(), 0)
|
assert.Len(suite.T(), suite.validator.Errors(), 0)
|
||||||
assert.Equal(suite.T(), schema.DefaultPasswordOptionsConfiguration.KeyLength, suite.configuration.File.PasswordHashing.KeyLength)
|
assert.Equal(suite.T(), schema.DefaultPasswordConfiguration.KeyLength, suite.configuration.File.Password.KeyLength)
|
||||||
assert.Equal(suite.T(), schema.DefaultPasswordOptionsConfiguration.Iterations, suite.configuration.File.PasswordHashing.Iterations)
|
assert.Equal(suite.T(), schema.DefaultPasswordConfiguration.Iterations, suite.configuration.File.Password.Iterations)
|
||||||
assert.Equal(suite.T(), schema.DefaultPasswordOptionsConfiguration.SaltLength, suite.configuration.File.PasswordHashing.SaltLength)
|
assert.Equal(suite.T(), schema.DefaultPasswordConfiguration.SaltLength, suite.configuration.File.Password.SaltLength)
|
||||||
assert.Equal(suite.T(), schema.DefaultPasswordOptionsConfiguration.Algorithm, suite.configuration.File.PasswordHashing.Algorithm)
|
assert.Equal(suite.T(), schema.DefaultPasswordConfiguration.Algorithm, suite.configuration.File.Password.Algorithm)
|
||||||
assert.Equal(suite.T(), schema.DefaultPasswordOptionsConfiguration.Memory, suite.configuration.File.PasswordHashing.Memory)
|
assert.Equal(suite.T(), schema.DefaultPasswordConfiguration.Memory, suite.configuration.File.Password.Memory)
|
||||||
assert.Equal(suite.T(), schema.DefaultPasswordOptionsConfiguration.Parallelism, suite.configuration.File.PasswordHashing.Parallelism)
|
assert.Equal(suite.T(), schema.DefaultPasswordConfiguration.Parallelism, suite.configuration.File.Password.Parallelism)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *FileBasedAuthenticationBackend) TestShouldSetDefaultConfigurationWhenOnlySHA512Set() {
|
func (suite *FileBasedAuthenticationBackend) TestShouldSetDefaultConfigurationWhenOnlySHA512Set() {
|
||||||
suite.configuration.File.PasswordHashing = &schema.PasswordHashingConfiguration{}
|
suite.configuration.File.Password = &schema.PasswordConfiguration{}
|
||||||
assert.Equal(suite.T(), "", suite.configuration.File.PasswordHashing.Algorithm)
|
assert.Equal(suite.T(), "", suite.configuration.File.Password.Algorithm)
|
||||||
suite.configuration.File.PasswordHashing.Algorithm = "sha512"
|
suite.configuration.File.Password.Algorithm = "sha512"
|
||||||
|
|
||||||
ValidateAuthenticationBackend(&suite.configuration, suite.validator)
|
ValidateAuthenticationBackend(&suite.configuration, suite.validator)
|
||||||
|
|
||||||
assert.Len(suite.T(), suite.validator.Errors(), 0)
|
assert.Len(suite.T(), suite.validator.Errors(), 0)
|
||||||
assert.Equal(suite.T(), schema.DefaultPasswordOptionsSHA512Configuration.KeyLength, suite.configuration.File.PasswordHashing.KeyLength)
|
assert.Equal(suite.T(), schema.DefaultPasswordSHA512Configuration.KeyLength, suite.configuration.File.Password.KeyLength)
|
||||||
assert.Equal(suite.T(), schema.DefaultPasswordOptionsSHA512Configuration.Iterations, suite.configuration.File.PasswordHashing.Iterations)
|
assert.Equal(suite.T(), schema.DefaultPasswordSHA512Configuration.Iterations, suite.configuration.File.Password.Iterations)
|
||||||
assert.Equal(suite.T(), schema.DefaultPasswordOptionsSHA512Configuration.SaltLength, suite.configuration.File.PasswordHashing.SaltLength)
|
assert.Equal(suite.T(), schema.DefaultPasswordSHA512Configuration.SaltLength, suite.configuration.File.Password.SaltLength)
|
||||||
assert.Equal(suite.T(), schema.DefaultPasswordOptionsSHA512Configuration.Algorithm, suite.configuration.File.PasswordHashing.Algorithm)
|
assert.Equal(suite.T(), schema.DefaultPasswordSHA512Configuration.Algorithm, suite.configuration.File.Password.Algorithm)
|
||||||
assert.Equal(suite.T(), schema.DefaultPasswordOptionsSHA512Configuration.Memory, suite.configuration.File.PasswordHashing.Memory)
|
assert.Equal(suite.T(), schema.DefaultPasswordSHA512Configuration.Memory, suite.configuration.File.Password.Memory)
|
||||||
assert.Equal(suite.T(), schema.DefaultPasswordOptionsSHA512Configuration.Parallelism, suite.configuration.File.PasswordHashing.Parallelism)
|
assert.Equal(suite.T(), schema.DefaultPasswordSHA512Configuration.Parallelism, suite.configuration.File.Password.Parallelism)
|
||||||
}
|
}
|
||||||
func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorWhenKeyLengthTooLow() {
|
func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorWhenKeyLengthTooLow() {
|
||||||
suite.configuration.File.PasswordHashing.KeyLength = 1
|
suite.configuration.File.Password.KeyLength = 1
|
||||||
ValidateAuthenticationBackend(&suite.configuration, suite.validator)
|
ValidateAuthenticationBackend(&suite.configuration, suite.validator)
|
||||||
assert.Len(suite.T(), suite.validator.Errors(), 1)
|
assert.Len(suite.T(), suite.validator.Errors(), 1)
|
||||||
assert.EqualError(suite.T(), suite.validator.Errors()[0], "Key length for argon2id must be 16, you configured 1")
|
assert.EqualError(suite.T(), suite.validator.Errors()[0], "Key length for argon2id must be 16, you configured 1")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorWhenSaltLengthTooLow() {
|
func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorWhenSaltLengthTooLow() {
|
||||||
suite.configuration.File.PasswordHashing.SaltLength = -1
|
suite.configuration.File.Password.SaltLength = -1
|
||||||
ValidateAuthenticationBackend(&suite.configuration, suite.validator)
|
ValidateAuthenticationBackend(&suite.configuration, suite.validator)
|
||||||
assert.Len(suite.T(), suite.validator.Errors(), 1)
|
assert.Len(suite.T(), suite.validator.Errors(), 1)
|
||||||
assert.EqualError(suite.T(), suite.validator.Errors()[0], "The salt length must be 2 or more, you configured -1")
|
assert.EqualError(suite.T(), suite.validator.Errors()[0], "The salt length must be 2 or more, you configured -1")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorWhenSaltLengthTooHigh() {
|
func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorWhenSaltLengthTooHigh() {
|
||||||
suite.configuration.File.PasswordHashing.SaltLength = 20
|
suite.configuration.File.Password.SaltLength = 20
|
||||||
ValidateAuthenticationBackend(&suite.configuration, suite.validator)
|
ValidateAuthenticationBackend(&suite.configuration, suite.validator)
|
||||||
assert.Len(suite.T(), suite.validator.Errors(), 1)
|
assert.Len(suite.T(), suite.validator.Errors(), 1)
|
||||||
assert.EqualError(suite.T(), suite.validator.Errors()[0], "The salt length must be 16 or less, you configured 20")
|
assert.EqualError(suite.T(), suite.validator.Errors()[0], "The salt length must be 16 or less, you configured 20")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorWhenBadAlgorithmDefined() {
|
func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorWhenBadAlgorithmDefined() {
|
||||||
suite.configuration.File.PasswordHashing.Algorithm = "bogus"
|
suite.configuration.File.Password.Algorithm = "bogus"
|
||||||
ValidateAuthenticationBackend(&suite.configuration, suite.validator)
|
ValidateAuthenticationBackend(&suite.configuration, suite.validator)
|
||||||
assert.Len(suite.T(), suite.validator.Errors(), 1)
|
assert.Len(suite.T(), suite.validator.Errors(), 1)
|
||||||
assert.EqualError(suite.T(), suite.validator.Errors()[0], "Unknown hashing algorithm supplied, valid values are argon2id and sha512, you configured 'bogus'")
|
assert.EqualError(suite.T(), suite.validator.Errors()[0], "Unknown hashing algorithm supplied, valid values are argon2id and sha512, you configured 'bogus'")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorWhenIterationsTooLow() {
|
func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorWhenIterationsTooLow() {
|
||||||
suite.configuration.File.PasswordHashing.Iterations = -1
|
suite.configuration.File.Password.Iterations = -1
|
||||||
ValidateAuthenticationBackend(&suite.configuration, suite.validator)
|
ValidateAuthenticationBackend(&suite.configuration, suite.validator)
|
||||||
assert.Len(suite.T(), suite.validator.Errors(), 1)
|
assert.Len(suite.T(), suite.validator.Errors(), 1)
|
||||||
assert.EqualError(suite.T(), suite.validator.Errors()[0], "The number of iterations specified is invalid, must be 1 or more, you configured -1")
|
assert.EqualError(suite.T(), suite.validator.Errors()[0], "The number of iterations specified is invalid, must be 1 or more, you configured -1")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorWhenParallelismTooLow() {
|
func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorWhenParallelismTooLow() {
|
||||||
suite.configuration.File.PasswordHashing.Parallelism = -1
|
suite.configuration.File.Password.Parallelism = -1
|
||||||
ValidateAuthenticationBackend(&suite.configuration, suite.validator)
|
ValidateAuthenticationBackend(&suite.configuration, suite.validator)
|
||||||
assert.Len(suite.T(), suite.validator.Errors(), 1)
|
assert.Len(suite.T(), suite.validator.Errors(), 1)
|
||||||
assert.EqualError(suite.T(), suite.validator.Errors()[0], "Parallelism for argon2id must be 1 or more, you configured -1")
|
assert.EqualError(suite.T(), suite.validator.Errors()[0], "Parallelism for argon2id must be 1 or more, you configured -1")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *FileBasedAuthenticationBackend) TestShouldSetDefaultValues() {
|
func (suite *FileBasedAuthenticationBackend) TestShouldSetDefaultValues() {
|
||||||
suite.configuration.File.PasswordHashing.Algorithm = ""
|
suite.configuration.File.Password.Algorithm = ""
|
||||||
suite.configuration.File.PasswordHashing.Iterations = 0
|
suite.configuration.File.Password.Iterations = 0
|
||||||
suite.configuration.File.PasswordHashing.SaltLength = 0
|
suite.configuration.File.Password.SaltLength = 0
|
||||||
suite.configuration.File.PasswordHashing.Memory = 0
|
suite.configuration.File.Password.Memory = 0
|
||||||
suite.configuration.File.PasswordHashing.Parallelism = 0
|
suite.configuration.File.Password.Parallelism = 0
|
||||||
ValidateAuthenticationBackend(&suite.configuration, suite.validator)
|
ValidateAuthenticationBackend(&suite.configuration, suite.validator)
|
||||||
assert.Len(suite.T(), suite.validator.Errors(), 0)
|
assert.Len(suite.T(), suite.validator.Errors(), 0)
|
||||||
assert.Equal(suite.T(), schema.DefaultPasswordOptionsConfiguration.Algorithm, suite.configuration.File.PasswordHashing.Algorithm)
|
assert.Equal(suite.T(), schema.DefaultPasswordConfiguration.Algorithm, suite.configuration.File.Password.Algorithm)
|
||||||
assert.Equal(suite.T(), schema.DefaultPasswordOptionsConfiguration.Iterations, suite.configuration.File.PasswordHashing.Iterations)
|
assert.Equal(suite.T(), schema.DefaultPasswordConfiguration.Iterations, suite.configuration.File.Password.Iterations)
|
||||||
assert.Equal(suite.T(), schema.DefaultPasswordOptionsConfiguration.SaltLength, suite.configuration.File.PasswordHashing.SaltLength)
|
assert.Equal(suite.T(), schema.DefaultPasswordConfiguration.SaltLength, suite.configuration.File.Password.SaltLength)
|
||||||
assert.Equal(suite.T(), schema.DefaultPasswordOptionsConfiguration.Memory, suite.configuration.File.PasswordHashing.Memory)
|
assert.Equal(suite.T(), schema.DefaultPasswordConfiguration.Memory, suite.configuration.File.Password.Memory)
|
||||||
assert.Equal(suite.T(), schema.DefaultPasswordOptionsConfiguration.Parallelism, suite.configuration.File.PasswordHashing.Parallelism)
|
assert.Equal(suite.T(), schema.DefaultPasswordConfiguration.Parallelism, suite.configuration.File.Password.Parallelism)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFileBasedAuthenticationBackend(t *testing.T) {
|
func TestFileBasedAuthenticationBackend(t *testing.T) {
|
||||||
|
|
|
@ -29,7 +29,7 @@ const (
|
||||||
const operationFailedMessage = "Operation failed."
|
const operationFailedMessage = "Operation failed."
|
||||||
const authenticationFailedMessage = "Authentication failed. Check your credentials."
|
const authenticationFailedMessage = "Authentication failed. Check your credentials."
|
||||||
const userBannedMessage = "Please retry in a few minutes."
|
const userBannedMessage = "Please retry in a few minutes."
|
||||||
const unableToRegisterOneTimePasswordMessage = "Unable to set up one-time passwords."
|
const unableToRegisterOneTimePasswordMessage = "Unable to set up one-time passwords." //nolint:gosec
|
||||||
const unableToRegisterSecurityKeyMessage = "Unable to register your security key."
|
const unableToRegisterSecurityKeyMessage = "Unable to register your security key."
|
||||||
const unableToResetPasswordMessage = "Unable to reset your password."
|
const unableToResetPasswordMessage = "Unable to reset your password."
|
||||||
const mfaValidationFailedMessage = "Authentication failed, please retry later."
|
const mfaValidationFailedMessage = "Authentication failed, please retry later."
|
||||||
|
|
Loading…
Reference in New Issue