From 19a2de6ceacd980d922d7871e0bee2d99f28df62 Mon Sep 17 00:00:00 2001 From: GreyXor Date: Wed, 30 Jul 2025 16:54:42 +0200 Subject: [PATCH] feat: add bcrypt cost parameter to hash-password --- modules/caddyhttp/caddyauth/basicauth.go | 4 ++-- modules/caddyhttp/caddyauth/caddyauth.go | 5 +++-- modules/caddyhttp/caddyauth/command.go | 10 ++++++++-- modules/caddyhttp/caddyauth/hashes.go | 7 ++++--- 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/modules/caddyhttp/caddyauth/basicauth.go b/modules/caddyhttp/caddyauth/basicauth.go index 5a9e167e1..228e45e10 100644 --- a/modules/caddyhttp/caddyauth/basicauth.go +++ b/modules/caddyhttp/caddyauth/basicauth.go @@ -234,7 +234,7 @@ func (c *Cache) makeRoom() { // we delete more than just 1 entry so that we don't have // to do this on every request; assuming the capacity of // the cache is on a long tail, we can save a lot of CPU - // time by doing a whole bunch of deletions now and then + // time by doing a whole bunch of deletions now, and then // we won't have to do them again for a while numToDelete := max(len(c.cache)/10, 1) for deleted := 0; deleted <= numToDelete; deleted++ { @@ -276,7 +276,7 @@ type Comparer interface { // features. A hasher also returns a fake hash which // can be used for timing side-channel mitigation. type Hasher interface { - Hash(plaintext []byte) ([]byte, error) + Hash(plaintext []byte, cost int) ([]byte, error) FakeHash() []byte } diff --git a/modules/caddyhttp/caddyauth/caddyauth.go b/modules/caddyhttp/caddyauth/caddyauth.go index 69db62a5c..9d4c3cb5f 100644 --- a/modules/caddyhttp/caddyauth/caddyauth.go +++ b/modules/caddyhttp/caddyauth/caddyauth.go @@ -60,7 +60,8 @@ func (Authentication) CaddyModule() caddy.ModuleInfo { } } -// Provision sets up a. +// Provision sets up an Authentication module by initializing its logger, +// loading and registering all configured authentication providers. func (a *Authentication) Provision(ctx caddy.Context) error { a.logger = ctx.Logger() a.Providers = make(map[string]Authenticator) @@ -120,7 +121,7 @@ type User struct { ID string // Any other relevant data about this - // user. Keys should be adhere to Caddy + // user. Keys should be adhered to Caddy // conventions (snake_casing), as all // keys will be made available as // placeholders. diff --git a/modules/caddyhttp/caddyauth/command.go b/modules/caddyhttp/caddyauth/command.go index c9f440060..5cca698aa 100644 --- a/modules/caddyhttp/caddyauth/command.go +++ b/modules/caddyhttp/caddyauth/command.go @@ -32,7 +32,7 @@ import ( func init() { caddycmd.RegisterCommand(caddycmd.Command{ Name: "hash-password", - Usage: "[--plaintext ] [--algorithm ]", + Usage: "[--plaintext ] [--algorithm ] [--cost ]", Short: "Hashes a password and writes base64", Long: ` Convenient way to hash a plaintext password. The resulting @@ -43,10 +43,15 @@ Caddy is attached to a controlling tty, the plaintext will not be echoed. --algorithm currently only supports 'bcrypt', and is the default. + +--cost defines the bcrypt hashing difficulty. +A higher cost increases security by making hashing slower and more CPU-intensive. +Default is 14; higher values may significantly impact performance on slower machines. `, CobraFunc: func(cmd *cobra.Command) { cmd.Flags().StringP("plaintext", "p", "", "The plaintext password") cmd.Flags().StringP("algorithm", "a", "bcrypt", "Name of the hash algorithm") + cmd.Flags().IntP("cost", "c", 14, "Hashing cost used to create the given hashed password") cmd.RunE = caddycmd.WrapCommandFuncForCobra(cmdHashPassword) }, }) @@ -57,6 +62,7 @@ func cmdHashPassword(fs caddycmd.Flags) (int, error) { algorithm := fs.String("algorithm") plaintext := []byte(fs.String("plaintext")) + cost := fs.Int("cost") if len(plaintext) == 0 { fd := int(os.Stdin.Fd()) @@ -108,7 +114,7 @@ func cmdHashPassword(fs caddycmd.Flags) (int, error) { var hashString string switch algorithm { case "bcrypt": - hash, err = BcryptHash{}.Hash(plaintext) + hash, err = BcryptHash{}.Hash(plaintext, cost) hashString = string(hash) default: return caddy.ExitCodeFailedStartup, fmt.Errorf("unrecognized hash algorithm: %s", algorithm) diff --git a/modules/caddyhttp/caddyauth/hashes.go b/modules/caddyhttp/caddyauth/hashes.go index ce3df901e..da168c039 100644 --- a/modules/caddyhttp/caddyauth/hashes.go +++ b/modules/caddyhttp/caddyauth/hashes.go @@ -15,6 +15,7 @@ package caddyauth import ( + "errors" "golang.org/x/crypto/bcrypt" "github.com/caddyserver/caddy/v2" @@ -38,7 +39,7 @@ func (BcryptHash) CaddyModule() caddy.ModuleInfo { // Compare compares passwords. func (BcryptHash) Compare(hashed, plaintext []byte) (bool, error) { err := bcrypt.CompareHashAndPassword(hashed, plaintext) - if err == bcrypt.ErrMismatchedHashAndPassword { + if errors.Is(err, bcrypt.ErrMismatchedHashAndPassword) { return false, nil } if err != nil { @@ -48,8 +49,8 @@ func (BcryptHash) Compare(hashed, plaintext []byte) (bool, error) { } // Hash hashes plaintext using a random salt. -func (BcryptHash) Hash(plaintext []byte) ([]byte, error) { - return bcrypt.GenerateFromPassword(plaintext, 14) +func (BcryptHash) Hash(plaintext []byte, cost int) ([]byte, error) { + return bcrypt.GenerateFromPassword(plaintext, cost) } // FakeHash returns a fake hash.