2022-10-17 10:51:59 +00:00
package commands
import (
"fmt"
"strings"
"github.com/go-crypt/crypt"
2022-12-04 22:37:08 +00:00
"github.com/go-crypt/crypt/algorithm"
2022-10-17 10:51:59 +00:00
"github.com/spf13/cobra"
"github.com/authelia/authelia/v4/internal/authentication"
"github.com/authelia/authelia/v4/internal/configuration"
"github.com/authelia/authelia/v4/internal/configuration/schema"
)
2022-12-22 00:21:29 +00:00
func newCryptoHashCmd ( ctx * CmdCtx ) ( cmd * cobra . Command ) {
2022-10-17 10:51:59 +00:00
cmd = & cobra . Command {
Use : cmdUseHash ,
Short : cmdAutheliaCryptoHashShort ,
Long : cmdAutheliaCryptoHashLong ,
Example : cmdAutheliaCryptoHashExample ,
Args : cobra . NoArgs ,
DisableAutoGenTag : true ,
}
cmd . AddCommand (
2022-12-22 00:21:29 +00:00
newCryptoHashValidateCmd ( ctx ) ,
newCryptoHashGenerateCmd ( ctx ) ,
2022-10-17 10:51:59 +00:00
)
return cmd
}
2022-12-22 00:21:29 +00:00
func newCryptoHashGenerateCmd ( ctx * CmdCtx ) ( cmd * cobra . Command ) {
defaults := map [ string ] any {
prefixFilePassword + ".algorithm" : schema . DefaultPasswordConfig . Algorithm ,
prefixFilePassword + ".argon2.variant" : schema . DefaultPasswordConfig . Argon2 . Variant ,
prefixFilePassword + ".argon2.iterations" : schema . DefaultPasswordConfig . Argon2 . Iterations ,
prefixFilePassword + ".argon2.memory" : schema . DefaultPasswordConfig . Argon2 . Memory ,
prefixFilePassword + ".argon2.parallelism" : schema . DefaultPasswordConfig . Argon2 . Parallelism ,
prefixFilePassword + ".argon2.key_length" : schema . DefaultPasswordConfig . Argon2 . KeyLength ,
prefixFilePassword + ".argon2.salt_length" : schema . DefaultPasswordConfig . Argon2 . SaltLength ,
prefixFilePassword + ".sha2crypt.variant" : schema . DefaultPasswordConfig . SHA2Crypt . Variant ,
prefixFilePassword + ".sha2crypt.iterations" : schema . DefaultPasswordConfig . SHA2Crypt . Iterations ,
prefixFilePassword + ".sha2crypt.salt_length" : schema . DefaultPasswordConfig . SHA2Crypt . SaltLength ,
prefixFilePassword + ".pbkdf2.variant" : schema . DefaultPasswordConfig . PBKDF2 . Variant ,
prefixFilePassword + ".pbkdf2.iterations" : schema . DefaultPasswordConfig . PBKDF2 . Iterations ,
prefixFilePassword + ".pbkdf2.salt_length" : schema . DefaultPasswordConfig . PBKDF2 . SaltLength ,
prefixFilePassword + ".bcrypt.variant" : schema . DefaultPasswordConfig . BCrypt . Variant ,
prefixFilePassword + ".bcrypt.cost" : schema . DefaultPasswordConfig . BCrypt . Cost ,
prefixFilePassword + ".scrypt.iterations" : schema . DefaultPasswordConfig . SCrypt . Iterations ,
prefixFilePassword + ".scrypt.block_size" : schema . DefaultPasswordConfig . SCrypt . BlockSize ,
prefixFilePassword + ".scrypt.parallelism" : schema . DefaultPasswordConfig . SCrypt . Parallelism ,
prefixFilePassword + ".scrypt.key_length" : schema . DefaultPasswordConfig . SCrypt . KeyLength ,
prefixFilePassword + ".scrypt.salt_length" : schema . DefaultPasswordConfig . SCrypt . SaltLength ,
}
2022-10-17 10:51:59 +00:00
cmd = & cobra . Command {
Use : cmdUseGenerate ,
Short : cmdAutheliaCryptoHashGenerateShort ,
Long : cmdAutheliaCryptoHashGenerateLong ,
Example : cmdAutheliaCryptoHashGenerateExample ,
2023-04-14 10:46:43 +00:00
Args : cobra . NoArgs ,
2022-12-22 00:21:29 +00:00
PreRunE : ctx . ChainRunE (
ctx . ConfigSetDefaultsRunE ( defaults ) ,
2022-12-23 04:00:23 +00:00
ctx . CryptoHashGenerateMapFlagsRunE ,
2022-12-22 00:21:29 +00:00
ctx . ConfigLoadRunE ,
ctx . ConfigValidateSectionPasswordRunE ,
) ,
RunE : ctx . CryptoHashGenerateRunE ,
2022-10-17 10:51:59 +00:00
DisableAutoGenTag : true ,
}
cmdFlagPassword ( cmd , true )
2022-10-20 20:41:46 +00:00
cmdFlagRandomPassword ( cmd )
2022-10-17 10:51:59 +00:00
for _ , use := range [ ] string { cmdUseHashArgon2 , cmdUseHashSHA2Crypt , cmdUseHashPBKDF2 , cmdUseHashBCrypt , cmdUseHashSCrypt } {
2022-12-22 00:21:29 +00:00
cmd . AddCommand ( newCryptoHashGenerateSubCmd ( ctx , use ) )
2022-10-17 10:51:59 +00:00
}
return cmd
}
2022-12-22 00:21:29 +00:00
func newCryptoHashGenerateSubCmd ( ctx * CmdCtx , use string ) ( cmd * cobra . Command ) {
defaults := map [ string ] any {
prefixFilePassword + ".algorithm" : schema . DefaultPasswordConfig . Algorithm ,
prefixFilePassword + ".argon2.variant" : schema . DefaultPasswordConfig . Argon2 . Variant ,
prefixFilePassword + ".argon2.iterations" : schema . DefaultPasswordConfig . Argon2 . Iterations ,
prefixFilePassword + ".argon2.memory" : schema . DefaultPasswordConfig . Argon2 . Memory ,
prefixFilePassword + ".argon2.parallelism" : schema . DefaultPasswordConfig . Argon2 . Parallelism ,
prefixFilePassword + ".argon2.key_length" : schema . DefaultPasswordConfig . Argon2 . KeyLength ,
prefixFilePassword + ".argon2.salt_length" : schema . DefaultPasswordConfig . Argon2 . SaltLength ,
prefixFilePassword + ".sha2crypt.variant" : schema . DefaultPasswordConfig . SHA2Crypt . Variant ,
prefixFilePassword + ".sha2crypt.iterations" : schema . DefaultPasswordConfig . SHA2Crypt . Iterations ,
prefixFilePassword + ".sha2crypt.salt_length" : schema . DefaultPasswordConfig . SHA2Crypt . SaltLength ,
prefixFilePassword + ".pbkdf2.variant" : schema . DefaultPasswordConfig . PBKDF2 . Variant ,
prefixFilePassword + ".pbkdf2.iterations" : schema . DefaultPasswordConfig . PBKDF2 . Iterations ,
prefixFilePassword + ".pbkdf2.salt_length" : schema . DefaultPasswordConfig . PBKDF2 . SaltLength ,
prefixFilePassword + ".bcrypt.variant" : schema . DefaultPasswordConfig . BCrypt . Variant ,
prefixFilePassword + ".bcrypt.cost" : schema . DefaultPasswordConfig . BCrypt . Cost ,
prefixFilePassword + ".scrypt.iterations" : schema . DefaultPasswordConfig . SCrypt . Iterations ,
prefixFilePassword + ".scrypt.block_size" : schema . DefaultPasswordConfig . SCrypt . BlockSize ,
prefixFilePassword + ".scrypt.parallelism" : schema . DefaultPasswordConfig . SCrypt . Parallelism ,
prefixFilePassword + ".scrypt.key_length" : schema . DefaultPasswordConfig . SCrypt . KeyLength ,
prefixFilePassword + ".scrypt.salt_length" : schema . DefaultPasswordConfig . SCrypt . SaltLength ,
}
2022-10-17 10:51:59 +00:00
useFmt := fmtCryptoHashUse ( use )
cmd = & cobra . Command {
Use : use ,
Short : fmt . Sprintf ( fmtCmdAutheliaCryptoHashGenerateSubShort , useFmt ) ,
Long : fmt . Sprintf ( fmtCmdAutheliaCryptoHashGenerateSubLong , useFmt , useFmt ) ,
Example : fmt . Sprintf ( fmtCmdAutheliaCryptoHashGenerateSubExample , use ) ,
Args : cobra . NoArgs ,
2022-12-22 00:21:29 +00:00
PersistentPreRunE : ctx . ChainRunE (
ctx . ConfigSetDefaultsRunE ( defaults ) ,
2022-12-23 04:00:23 +00:00
ctx . CryptoHashGenerateMapFlagsRunE ,
2022-12-22 00:21:29 +00:00
ctx . ConfigLoadRunE ,
ctx . ConfigValidateSectionPasswordRunE ,
) ,
RunE : ctx . CryptoHashGenerateRunE ,
2022-10-17 10:51:59 +00:00
DisableAutoGenTag : true ,
}
switch use {
case cmdUseHashArgon2 :
cmdFlagIterations ( cmd , schema . DefaultPasswordConfig . Argon2 . Iterations )
cmdFlagParallelism ( cmd , schema . DefaultPasswordConfig . Argon2 . Parallelism )
cmdFlagKeySize ( cmd , schema . DefaultPasswordConfig . Argon2 . KeyLength )
cmdFlagSaltSize ( cmd , schema . DefaultPasswordConfig . Argon2 . SaltLength )
cmd . Flags ( ) . StringP ( cmdFlagNameVariant , "v" , schema . DefaultPasswordConfig . Argon2 . Variant , "variant, options are 'argon2id', 'argon2i', and 'argon2d'" )
cmd . Flags ( ) . IntP ( cmdFlagNameMemory , "m" , schema . DefaultPasswordConfig . Argon2 . Memory , "memory in kibibytes" )
cmd . Flags ( ) . String ( cmdFlagNameProfile , "" , "profile to use, options are low-memory and recommended" )
case cmdUseHashSHA2Crypt :
cmdFlagIterations ( cmd , schema . DefaultPasswordConfig . SHA2Crypt . Iterations )
cmdFlagSaltSize ( cmd , schema . DefaultPasswordConfig . SHA2Crypt . SaltLength )
cmd . Flags ( ) . StringP ( cmdFlagNameVariant , "v" , schema . DefaultPasswordConfig . SHA2Crypt . Variant , "variant, options are sha256 and sha512" )
2022-12-22 00:21:29 +00:00
cmd . PreRunE = ctx . ChainRunE ( )
2022-10-17 10:51:59 +00:00
case cmdUseHashPBKDF2 :
cmdFlagIterations ( cmd , schema . DefaultPasswordConfig . PBKDF2 . Iterations )
cmdFlagSaltSize ( cmd , schema . DefaultPasswordConfig . PBKDF2 . SaltLength )
cmd . Flags ( ) . StringP ( cmdFlagNameVariant , "v" , schema . DefaultPasswordConfig . PBKDF2 . Variant , "variant, options are 'sha1', 'sha224', 'sha256', 'sha384', and 'sha512'" )
case cmdUseHashBCrypt :
cmd . Flags ( ) . StringP ( cmdFlagNameVariant , "v" , schema . DefaultPasswordConfig . BCrypt . Variant , "variant, options are 'standard' and 'sha256'" )
cmd . Flags ( ) . IntP ( cmdFlagNameCost , "i" , schema . DefaultPasswordConfig . BCrypt . Cost , "hashing cost" )
case cmdUseHashSCrypt :
cmdFlagIterations ( cmd , schema . DefaultPasswordConfig . SCrypt . Iterations )
cmdFlagKeySize ( cmd , schema . DefaultPasswordConfig . SCrypt . KeyLength )
cmdFlagSaltSize ( cmd , schema . DefaultPasswordConfig . SCrypt . SaltLength )
cmdFlagParallelism ( cmd , schema . DefaultPasswordConfig . SCrypt . Parallelism )
cmd . Flags ( ) . IntP ( cmdFlagNameBlockSize , "r" , schema . DefaultPasswordConfig . SCrypt . BlockSize , "block size" )
}
return cmd
}
2022-12-22 00:21:29 +00:00
func newCryptoHashValidateCmd ( ctx * CmdCtx ) ( cmd * cobra . Command ) {
2022-10-17 10:51:59 +00:00
cmd = & cobra . Command {
Use : fmt . Sprintf ( cmdUseFmtValidate , cmdUseValidate ) ,
Short : cmdAutheliaCryptoHashValidateShort ,
Long : cmdAutheliaCryptoHashValidateLong ,
Example : cmdAutheliaCryptoHashValidateExample ,
Args : cobra . ExactArgs ( 1 ) ,
2022-12-22 00:21:29 +00:00
RunE : ctx . CryptoHashValidateRunE ,
2022-10-17 10:51:59 +00:00
DisableAutoGenTag : true ,
}
cmdFlagPassword ( cmd , false )
return cmd
}
2022-12-22 00:21:29 +00:00
// CryptoHashValidateRunE is the RunE for the authelia crypto hash validate command.
func ( ctx * CmdCtx ) CryptoHashValidateRunE ( cmd * cobra . Command , args [ ] string ) ( err error ) {
2022-10-17 10:51:59 +00:00
var (
2022-12-22 00:21:29 +00:00
password string
valid bool
2022-10-17 10:51:59 +00:00
)
2022-12-22 00:21:29 +00:00
if password , _ , err = cmdCryptoHashGetPassword ( cmd , args , false , false ) ; err != nil {
return fmt . Errorf ( "error occurred trying to obtain the password: %w" , err )
2022-10-17 10:51:59 +00:00
}
2022-12-22 00:21:29 +00:00
if len ( password ) == 0 {
return fmt . Errorf ( "no password provided" )
2022-10-17 10:51:59 +00:00
}
2022-12-22 00:21:29 +00:00
if valid , err = crypt . CheckPassword ( password , args [ 0 ] ) ; err != nil {
return fmt . Errorf ( "error occurred trying to validate the password against the digest: %w" , err )
}
2022-10-17 10:51:59 +00:00
switch {
2022-12-22 00:21:29 +00:00
case valid :
fmt . Println ( "The password matches the digest." )
2022-10-17 10:51:59 +00:00
default :
2022-12-22 00:21:29 +00:00
fmt . Println ( "The password does not match the digest." )
2022-10-17 10:51:59 +00:00
}
2022-12-22 00:21:29 +00:00
return nil
}
2022-12-23 04:00:23 +00:00
// CryptoHashGenerateMapFlagsRunE is the RunE which configures the flags map configuration source for the
2022-12-22 00:21:29 +00:00
// authelia crypto hash generate commands.
2022-12-23 04:00:23 +00:00
func ( ctx * CmdCtx ) CryptoHashGenerateMapFlagsRunE ( cmd * cobra . Command , args [ ] string ) ( err error ) {
2022-12-22 00:21:29 +00:00
var flagsMap map [ string ] string
switch cmd . Use {
case cmdUseHashArgon2 :
flagsMap = map [ string ] string {
cmdFlagNameVariant : prefixFilePassword + ".argon2.variant" ,
cmdFlagNameIterations : prefixFilePassword + ".argon2.iterations" ,
cmdFlagNameMemory : prefixFilePassword + ".argon2.memory" ,
cmdFlagNameParallelism : prefixFilePassword + ".argon2.parallelism" ,
cmdFlagNameKeySize : prefixFilePassword + ".argon2.key_length" ,
cmdFlagNameSaltSize : prefixFilePassword + ".argon2.salt_length" ,
}
case cmdUseHashSHA2Crypt :
flagsMap = map [ string ] string {
cmdFlagNameVariant : prefixFilePassword + ".sha2crypt.variant" ,
cmdFlagNameIterations : prefixFilePassword + ".sha2crypt.iterations" ,
cmdFlagNameSaltSize : prefixFilePassword + ".sha2crypt.salt_length" ,
}
case cmdUseHashPBKDF2 :
flagsMap = map [ string ] string {
cmdFlagNameVariant : prefixFilePassword + ".pbkdf2.variant" ,
cmdFlagNameIterations : prefixFilePassword + ".pbkdf2.iterations" ,
cmdFlagNameKeySize : prefixFilePassword + ".pbkdf2.key_length" ,
cmdFlagNameSaltSize : prefixFilePassword + ".pbkdf2.salt_length" ,
}
case cmdUseHashBCrypt :
flagsMap = map [ string ] string {
cmdFlagNameVariant : prefixFilePassword + ".bcrypt.variant" ,
cmdFlagNameCost : prefixFilePassword + ".bcrypt.cost" ,
}
case cmdUseHashSCrypt :
flagsMap = map [ string ] string {
cmdFlagNameIterations : prefixFilePassword + ".scrypt.iterations" ,
cmdFlagNameBlockSize : prefixFilePassword + ".scrypt.block_size" ,
cmdFlagNameParallelism : prefixFilePassword + ".scrypt.parallelism" ,
cmdFlagNameKeySize : prefixFilePassword + ".scrypt.key_length" ,
cmdFlagNameSaltSize : prefixFilePassword + ".scrypt.salt_length" ,
}
2022-10-17 10:51:59 +00:00
}
2022-12-22 00:21:29 +00:00
if flagsMap != nil {
ctx . cconfig . sources = append ( ctx . cconfig . sources , configuration . NewCommandLineSourceWithMapping ( cmd . Flags ( ) , flagsMap , false , false ) )
2022-10-17 10:51:59 +00:00
}
2022-12-22 00:21:29 +00:00
return nil
}
// CryptoHashGenerateRunE is the RunE for the authelia crypto hash generate commands.
func ( ctx * CmdCtx ) CryptoHashGenerateRunE ( cmd * cobra . Command , args [ ] string ) ( err error ) {
2022-10-17 10:51:59 +00:00
var (
2022-12-04 22:37:08 +00:00
hash algorithm . Hash
digest algorithm . Digest
2022-10-17 10:51:59 +00:00
password string
random bool
)
2022-12-22 00:21:29 +00:00
if password , random , err = cmdCryptoHashGetPassword ( cmd , args , false , true ) ; err != nil {
2022-10-17 10:51:59 +00:00
return err
}
if len ( password ) == 0 {
return fmt . Errorf ( "no password provided" )
}
2022-12-22 00:21:29 +00:00
switch cmd . Use {
case cmdUseGenerate :
break
default :
ctx . config . AuthenticationBackend . File . Password . Algorithm = cmd . Use
}
if hash , err = authentication . NewFileCryptoHashFromConfig ( ctx . config . AuthenticationBackend . File . Password ) ; err != nil {
2022-10-17 10:51:59 +00:00
return err
}
if digest , err = hash . Hash ( password ) ; err != nil {
return err
}
if random {
fmt . Printf ( "Random Password: %s\n" , password )
}
fmt . Printf ( "Digest: %s\n" , digest . Encode ( ) )
return nil
}
func cmdCryptoHashGetPassword ( cmd * cobra . Command , args [ ] string , useArgs , useRandom bool ) ( password string , random bool , err error ) {
if useRandom {
if random , err = cmd . Flags ( ) . GetBool ( cmdFlagNameRandom ) ; err != nil {
return
}
}
switch {
case random :
2022-10-20 20:41:46 +00:00
password , err = flagsGetRandomCharacters ( cmd . Flags ( ) , cmdFlagNameRandomLength , cmdFlagNameRandomCharSet , cmdFlagNameCharacters )
2022-10-17 10:51:59 +00:00
return
case cmd . Flags ( ) . Changed ( cmdFlagNamePassword ) :
password , err = cmd . Flags ( ) . GetString ( cmdFlagNamePassword )
return
case useArgs && len ( args ) != 0 :
password , err = strings . Join ( args , " " ) , nil
return
}
var (
noConfirm bool
)
2022-12-22 00:21:29 +00:00
if password , err = termReadPasswordWithPrompt ( "Enter Password: " , "password" ) ; err != nil {
2022-10-17 10:51:59 +00:00
err = fmt . Errorf ( "failed to read the password from the terminal: %w" , err )
return
}
if cmd . Use == fmt . Sprintf ( cmdUseFmtValidate , cmdUseValidate ) {
fmt . Println ( "" )
return
}
if noConfirm , err = cmd . Flags ( ) . GetBool ( cmdFlagNameNoConfirm ) ; err == nil && ! noConfirm {
2022-12-22 00:21:29 +00:00
var confirm string
if confirm , err = termReadPasswordWithPrompt ( "Confirm Password: " , "" ) ; err != nil {
2022-10-17 10:51:59 +00:00
return
}
2022-12-22 00:21:29 +00:00
if password != confirm {
2022-10-17 10:51:59 +00:00
fmt . Println ( "" )
err = fmt . Errorf ( "the password did not match the confirmation password" )
return
}
}
fmt . Println ( "" )
return
}
func cmdFlagPassword ( cmd * cobra . Command , noConfirm bool ) {
2022-10-20 20:41:46 +00:00
cmd . PersistentFlags ( ) . String ( cmdFlagNamePassword , "" , "manually supply the password rather than using the terminal prompt" )
2022-10-17 10:51:59 +00:00
if noConfirm {
2022-10-20 20:41:46 +00:00
cmd . PersistentFlags ( ) . Bool ( cmdFlagNameNoConfirm , false , "skip the password confirmation prompt" )
2022-10-17 10:51:59 +00:00
}
}
func cmdFlagRandomPassword ( cmd * cobra . Command ) {
2022-10-20 20:41:46 +00:00
cmd . PersistentFlags ( ) . Bool ( cmdFlagNameRandom , false , "uses a randomly generated password" )
2022-11-04 00:32:49 +00:00
cmd . PersistentFlags ( ) . String ( cmdFlagNameRandomCharSet , cmdFlagValueCharSet , cmdFlagUsageCharset )
cmd . PersistentFlags ( ) . String ( cmdFlagNameRandomCharacters , "" , cmdFlagUsageCharacters )
cmd . PersistentFlags ( ) . Int ( cmdFlagNameRandomLength , 72 , cmdFlagUsageLength )
2022-10-17 10:51:59 +00:00
}
func cmdFlagIterations ( cmd * cobra . Command , value int ) {
cmd . Flags ( ) . IntP ( cmdFlagNameIterations , "i" , value , "number of iterations" )
}
func cmdFlagKeySize ( cmd * cobra . Command , value int ) {
cmd . Flags ( ) . IntP ( cmdFlagNameKeySize , "k" , value , "key size in bytes" )
}
func cmdFlagSaltSize ( cmd * cobra . Command , value int ) {
cmd . Flags ( ) . IntP ( cmdFlagNameSaltSize , "s" , value , "salt size in bytes" )
}
func cmdFlagParallelism ( cmd * cobra . Command , value int ) {
cmd . Flags ( ) . IntP ( cmdFlagNameParallelism , "p" , value , "parallelism or threads" )
}