Commit 89944f5e authored by ale's avatar ale

Make password hashing parameters configurable

parent f542e625
Pipeline #2666 passed with stages
in 5 minutes and 41 seconds
...@@ -3,11 +3,13 @@ package main ...@@ -3,11 +3,13 @@ package main
import ( import (
"errors" "errors"
"flag" "flag"
"fmt"
"io/ioutil" "io/ioutil"
"log" "log"
"strings" "strings"
"git.autistici.org/ai3/accountserver" "git.autistici.org/ai3/accountserver"
"git.autistici.org/ai3/go-common/pwhash"
"git.autistici.org/ai3/go-common/serverutil" "git.autistici.org/ai3/go-common/serverutil"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
...@@ -30,6 +32,10 @@ type config struct { ...@@ -30,6 +32,10 @@ type config struct {
} `yaml:"ldap"` } `yaml:"ldap"`
AccountServerConfig accountserver.Config `yaml:",inline"` AccountServerConfig accountserver.Config `yaml:",inline"`
ServerConfig *serverutil.ServerConfig `yaml:"http_server"` ServerConfig *serverutil.ServerConfig `yaml:"http_server"`
PwHash struct {
Algo string `yaml:"algo"`
Params map[string]int `yaml:"params"`
} `yaml:"pwhash"`
} }
func (c *config) validate() error { func (c *config) validate() error {
...@@ -54,6 +60,45 @@ func (c *config) validate() error { ...@@ -54,6 +60,45 @@ func (c *config) validate() error {
return nil return nil
} }
func (c *config) getPasswordHash() (h pwhash.PasswordHash, err error) {
switch c.PwHash.Algo {
case "":
// Just use the defaults.
h = pwhash.DefaultEncryptAlgorithm
case "argon2":
pTime := 1
if i, ok := c.PwHash.Params["time"]; ok {
pTime = i
}
pMem := 4
if i, ok := c.PwHash.Params["mem"]; ok {
pMem = i
}
pThreads := 4
if i, ok := c.PwHash.Params["threads"]; ok {
pThreads = i
}
h = pwhash.NewArgon2WithParams(uint32(pTime), uint32(pMem), uint8(pThreads))
case "scrypt":
pN := 16384
if i, ok := c.PwHash.Params["n"]; ok {
pN = i
}
pR := 8
if i, ok := c.PwHash.Params["r"]; ok {
pR = i
}
pP := 1
if i, ok := c.PwHash.Params["p"]; ok {
pP = i
}
h = pwhash.NewScryptWithParams(pN, pR, pP)
default:
err = fmt.Errorf("unknown pwhash algo '%s'", c.PwHash.Algo)
}
return
}
func loadConfig(path string) (*config, error) { func loadConfig(path string) (*config, error) {
// Read YAML config. // Read YAML config.
data, err := ioutil.ReadFile(path) data, err := ioutil.ReadFile(path)
...@@ -93,6 +138,12 @@ func main() { ...@@ -93,6 +138,12 @@ func main() {
log.Fatal(err) log.Fatal(err)
} }
h, err := config.getPasswordHash()
if err != nil {
log.Fatal(err)
}
accountserver.DefaultPasswordHash = h
bindPw, err := getBindPw(config) bindPw, err := getBindPw(config)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
......
...@@ -16,6 +16,13 @@ const ( ...@@ -16,6 +16,13 @@ const (
UserStatusInactive = "inactive" UserStatusInactive = "inactive"
) )
// The password hashing algorithm to use when updating credentials.
var DefaultPasswordHash pwhash.PasswordHash
func init() {
DefaultPasswordHash = pwhash.DefaultEncryptAlgorithm
}
// User information, public: includes data *about* credentials, but // User information, public: includes data *about* credentials, but
// not the credentials themselves. Every user has a unique // not the credentials themselves. Every user has a unique
// identifier, which may be an email address. // identifier, which may be an email address.
...@@ -216,7 +223,7 @@ func (u *RawUser) setPrimaryPassword(ctx context.Context, tx TX, unlockPassword, ...@@ -216,7 +223,7 @@ func (u *RawUser) setPrimaryPassword(ctx context.Context, tx TX, unlockPassword,
} }
} }
enc := pwhash.Encrypt(password) enc := DefaultPasswordHash.Encrypt(password)
u.Password = enc u.Password = enc
return tx.SetUserPassword(ctx, &u.User, enc) return tx.SetUserPassword(ctx, &u.User, enc)
} }
...@@ -235,7 +242,7 @@ func (u *RawUser) setAccountRecoveryHint(ctx context.Context, tx TX, unlockPassw ...@@ -235,7 +242,7 @@ func (u *RawUser) setAccountRecoveryHint(ctx context.Context, tx TX, unlockPassw
} }
} }
enc := pwhash.Encrypt(response) enc := DefaultPasswordHash.Encrypt(response)
u.RecoveryPassword = enc u.RecoveryPassword = enc
return tx.SetAccountRecoveryHint(ctx, &u.User, hint, enc) return tx.SetAccountRecoveryHint(ctx, &u.User, hint, enc)
} }
...@@ -285,7 +292,7 @@ func (u *RawUser) resetPassword(ctx context.Context, tx TX, password string) err ...@@ -285,7 +292,7 @@ func (u *RawUser) resetPassword(ctx context.Context, tx TX, password string) err
} }
} }
enc := pwhash.Encrypt(password) enc := DefaultPasswordHash.Encrypt(password)
return tx.SetUserPassword(ctx, &u.User, enc) return tx.SetUserPassword(ctx, &u.User, enc)
} }
...@@ -302,7 +309,7 @@ func (u *RawUser) addApplicationSpecificPassword(ctx context.Context, tx TX, unl ...@@ -302,7 +309,7 @@ func (u *RawUser) addApplicationSpecificPassword(ctx context.Context, tx TX, unl
} }
} }
enc := pwhash.Encrypt(password) enc := DefaultPasswordHash.Encrypt(password)
return tx.SetApplicationSpecificPassword(ctx, &u.User, asp, enc) return tx.SetApplicationSpecificPassword(ctx, &u.User, asp, enc)
} }
......
// Package pwhash provides a simple interface to hashed passwords with
// support for multiple hashing algorithms.
//
// The format is the well-known dollar-separated field string,
// extended with optional algorithm-specific parameters:
//
// $id[$params...]$salt$encrypted
//
// We extend 'id' beyond the values supported by the libc crypt(3)
// function with the following hashing algorithms:
//
// Scrypt (id '$s$'), in which case the parameters are N, R and P.
//
// Argon2 (id '$a2$'), with parameters time, memory and threads. To
// tune Argon2 parameters, you can run the benchmarks in this package:
// the parameterized benchmarks are named with
// time/memory(MB)/threads. For nicer results:
//
// go test -bench=Argon2 -run=none . 2>&1 | \
// awk '/^Bench/ {ops=1000000000 / $3; print $1 " " ops " ops/sec"}'
//
package pwhash package pwhash
import ( import (
......
...@@ -75,19 +75,19 @@ func Sum256(data []byte) [Size256]byte { ...@@ -75,19 +75,19 @@ func Sum256(data []byte) [Size256]byte {
} }
// New512 returns a new hash.Hash computing the BLAKE2b-512 checksum. A non-nil // New512 returns a new hash.Hash computing the BLAKE2b-512 checksum. A non-nil
// key turns the hash into a MAC. The key must between zero and 64 bytes long. // key turns the hash into a MAC. The key must be between zero and 64 bytes long.
func New512(key []byte) (hash.Hash, error) { return newDigest(Size, key) } func New512(key []byte) (hash.Hash, error) { return newDigest(Size, key) }
// New384 returns a new hash.Hash computing the BLAKE2b-384 checksum. A non-nil // New384 returns a new hash.Hash computing the BLAKE2b-384 checksum. A non-nil
// key turns the hash into a MAC. The key must between zero and 64 bytes long. // key turns the hash into a MAC. The key must be between zero and 64 bytes long.
func New384(key []byte) (hash.Hash, error) { return newDigest(Size384, key) } func New384(key []byte) (hash.Hash, error) { return newDigest(Size384, key) }
// New256 returns a new hash.Hash computing the BLAKE2b-256 checksum. A non-nil // New256 returns a new hash.Hash computing the BLAKE2b-256 checksum. A non-nil
// key turns the hash into a MAC. The key must between zero and 64 bytes long. // key turns the hash into a MAC. The key must be between zero and 64 bytes long.
func New256(key []byte) (hash.Hash, error) { return newDigest(Size256, key) } func New256(key []byte) (hash.Hash, error) { return newDigest(Size256, key) }
// New returns a new hash.Hash computing the BLAKE2b checksum with a custom length. // New returns a new hash.Hash computing the BLAKE2b checksum with a custom length.
// A non-nil key turns the hash into a MAC. The key must between zero and 64 bytes long. // A non-nil key turns the hash into a MAC. The key must be between zero and 64 bytes long.
// The hash size can be a value between 1 and 64 but it is highly recommended to use // The hash size can be a value between 1 and 64 but it is highly recommended to use
// values equal or greater than: // values equal or greater than:
// - 32 if BLAKE2b is used as a hash function (The key is zero bytes long). // - 32 if BLAKE2b is used as a hash function (The key is zero bytes long).
......
...@@ -29,7 +29,7 @@ type XOF interface { ...@@ -29,7 +29,7 @@ type XOF interface {
} }
// OutputLengthUnknown can be used as the size argument to NewXOF to indicate // OutputLengthUnknown can be used as the size argument to NewXOF to indicate
// the the length of the output is not known in advance. // the length of the output is not known in advance.
const OutputLengthUnknown = 0 const OutputLengthUnknown = 0
// magicUnknownOutputLength is a magic value for the output size that indicates // magicUnknownOutputLength is a magic value for the output size that indicates
......
...@@ -29,7 +29,7 @@ func blockXOR(dst, src []uint32, n int) { ...@@ -29,7 +29,7 @@ func blockXOR(dst, src []uint32, n int) {
} }
// salsaXOR applies Salsa20/8 to the XOR of 16 numbers from tmp and in, // salsaXOR applies Salsa20/8 to the XOR of 16 numbers from tmp and in,
// and puts the result into both both tmp and out. // and puts the result into both tmp and out.
func salsaXOR(tmp *[16]uint32, in, out []uint32) { func salsaXOR(tmp *[16]uint32, in, out []uint32) {
w0 := tmp[0] ^ in[0] w0 := tmp[0] ^ in[0]
w1 := tmp[1] ^ in[1] w1 := tmp[1] ^ in[1]
......
...@@ -27,10 +27,10 @@ ...@@ -27,10 +27,10 @@
"revisionTime": "2019-03-27T08:48:39Z" "revisionTime": "2019-03-27T08:48:39Z"
}, },
{ {
"checksumSHA1": "mfFIqmwojDqQdJvjLI3y7YCQ+2c=", "checksumSHA1": "JcfbQfBD7HTqjhFYT1gBjbThSI4=",
"path": "git.autistici.org/ai3/go-common/pwhash", "path": "git.autistici.org/ai3/go-common/pwhash",
"revision": "1f95fcdd58ebf63d338f05ceae29d2de811a2d2f", "revision": "b4364e842290fdecd412056674b471af77663757",
"revisionTime": "2018-11-18T16:11:30Z" "revisionTime": "2019-04-03T06:59:52Z"
}, },
{ {
"checksumSHA1": "TKGUNmKxj7KH3qhwiCh/6quUnwc=", "checksumSHA1": "TKGUNmKxj7KH3qhwiCh/6quUnwc=",
...@@ -402,40 +402,40 @@ ...@@ -402,40 +402,40 @@
{ {
"checksumSHA1": "FwW3Vv4jW0Nv7V2SZC7x/Huj5M4=", "checksumSHA1": "FwW3Vv4jW0Nv7V2SZC7x/Huj5M4=",
"path": "golang.org/x/crypto/argon2", "path": "golang.org/x/crypto/argon2",
"revision": "e84da0312774c21d64ee2317962ef669b27ffb41", "revision": "a5d413f7728c81fb97d96a2b722368945f651e78",
"revisionTime": "2018-10-24T13:26:30Z" "revisionTime": "2019-03-25T14:57:12Z"
}, },
{ {
"checksumSHA1": "ejjxT0+wDWWncfh0Rt3lSH4IbXQ=", "checksumSHA1": "eaK7NuGdfEVypOnqYniZSuF2S6s=",
"path": "golang.org/x/crypto/blake2b", "path": "golang.org/x/crypto/blake2b",
"revision": "e84da0312774c21d64ee2317962ef669b27ffb41", "revision": "a5d413f7728c81fb97d96a2b722368945f651e78",
"revisionTime": "2018-10-24T13:26:30Z" "revisionTime": "2019-03-25T14:57:12Z"
}, },
{ {
"checksumSHA1": "2LpxYGSf068307b7bhAuVjvzLLc=", "checksumSHA1": "2LpxYGSf068307b7bhAuVjvzLLc=",
"origin": "git.autistici.org/id/go-sso/vendor/golang.org/x/crypto/ed25519", "origin": "git.autistici.org/id/go-sso/vendor/golang.org/x/crypto/ed25519",
"path": "golang.org/x/crypto/ed25519", "path": "golang.org/x/crypto/ed25519",
"revision": "bb8238eef0a55b1020e35a05c95f4247e7a35090", "revision": "347348a99d9c23c8b3c9884176492d4b7d74037e",
"revisionTime": "2018-10-31T07:04:43Z" "revisionTime": "2019-03-18T20:43:40Z"
}, },
{ {
"checksumSHA1": "0JTAFXPkankmWcZGQJGScLDiaN8=", "checksumSHA1": "0JTAFXPkankmWcZGQJGScLDiaN8=",
"origin": "git.autistici.org/id/go-sso/vendor/golang.org/x/crypto/ed25519/internal/edwards25519", "origin": "git.autistici.org/id/go-sso/vendor/golang.org/x/crypto/ed25519/internal/edwards25519",
"path": "golang.org/x/crypto/ed25519/internal/edwards25519", "path": "golang.org/x/crypto/ed25519/internal/edwards25519",
"revision": "bb8238eef0a55b1020e35a05c95f4247e7a35090", "revision": "347348a99d9c23c8b3c9884176492d4b7d74037e",
"revisionTime": "2018-10-31T07:04:43Z" "revisionTime": "2019-03-18T20:43:40Z"
}, },
{ {
"checksumSHA1": "1MGpGDQqnUoRpv7VEcQrXOBydXE=", "checksumSHA1": "1MGpGDQqnUoRpv7VEcQrXOBydXE=",
"path": "golang.org/x/crypto/pbkdf2", "path": "golang.org/x/crypto/pbkdf2",
"revision": "e84da0312774c21d64ee2317962ef669b27ffb41", "revision": "a5d413f7728c81fb97d96a2b722368945f651e78",
"revisionTime": "2018-10-24T13:26:30Z" "revisionTime": "2019-03-25T14:57:12Z"
}, },
{ {
"checksumSHA1": "sx1nQShs40UKtcJZNJuvYtGesaI=", "checksumSHA1": "q+Rqy6Spw6qDSj75TGEZF7nzoFM=",
"path": "golang.org/x/crypto/scrypt", "path": "golang.org/x/crypto/scrypt",
"revision": "e84da0312774c21d64ee2317962ef669b27ffb41", "revision": "a5d413f7728c81fb97d96a2b722368945f651e78",
"revisionTime": "2018-10-24T13:26:30Z" "revisionTime": "2019-03-25T14:57:12Z"
}, },
{ {
"checksumSHA1": "/yo2FIWZX5FozwxE8ZZol2ekbxE=", "checksumSHA1": "/yo2FIWZX5FozwxE8ZZol2ekbxE=",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment