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
import (
"errors"
"flag"
"fmt"
"io/ioutil"
"log"
"strings"
"git.autistici.org/ai3/accountserver"
"git.autistici.org/ai3/go-common/pwhash"
"git.autistici.org/ai3/go-common/serverutil"
"gopkg.in/yaml.v2"
......@@ -30,6 +32,10 @@ type config struct {
} `yaml:"ldap"`
AccountServerConfig accountserver.Config `yaml:",inline"`
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 {
......@@ -54,6 +60,45 @@ func (c *config) validate() error {
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) {
// Read YAML config.
data, err := ioutil.ReadFile(path)
......@@ -93,6 +138,12 @@ func main() {
log.Fatal(err)
}
h, err := config.getPasswordHash()
if err != nil {
log.Fatal(err)
}
accountserver.DefaultPasswordHash = h
bindPw, err := getBindPw(config)
if err != nil {
log.Fatal(err)
......
......@@ -16,6 +16,13 @@ const (
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
// not the credentials themselves. Every user has a unique
// identifier, which may be an email address.
......@@ -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
return tx.SetUserPassword(ctx, &u.User, enc)
}
......@@ -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
return tx.SetAccountRecoveryHint(ctx, &u.User, hint, enc)
}
......@@ -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)
}
......@@ -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)
}
......
// 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
import (
......
......@@ -75,19 +75,19 @@ func Sum256(data []byte) [Size256]byte {
}
// 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) }
// 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) }
// 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) }
// 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
// values equal or greater than:
// - 32 if BLAKE2b is used as a hash function (The key is zero bytes long).
......
......@@ -29,7 +29,7 @@ type XOF interface {
}
// 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
// magicUnknownOutputLength is a magic value for the output size that indicates
......
......@@ -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,
// 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) {
w0 := tmp[0] ^ in[0]
w1 := tmp[1] ^ in[1]
......
......@@ -27,10 +27,10 @@
"revisionTime": "2019-03-27T08:48:39Z"
},
{
"checksumSHA1": "mfFIqmwojDqQdJvjLI3y7YCQ+2c=",
"checksumSHA1": "JcfbQfBD7HTqjhFYT1gBjbThSI4=",
"path": "git.autistici.org/ai3/go-common/pwhash",
"revision": "1f95fcdd58ebf63d338f05ceae29d2de811a2d2f",
"revisionTime": "2018-11-18T16:11:30Z"
"revision": "b4364e842290fdecd412056674b471af77663757",
"revisionTime": "2019-04-03T06:59:52Z"
},
{
"checksumSHA1": "TKGUNmKxj7KH3qhwiCh/6quUnwc=",
......@@ -402,40 +402,40 @@
{
"checksumSHA1": "FwW3Vv4jW0Nv7V2SZC7x/Huj5M4=",
"path": "golang.org/x/crypto/argon2",
"revision": "e84da0312774c21d64ee2317962ef669b27ffb41",
"revisionTime": "2018-10-24T13:26:30Z"
"revision": "a5d413f7728c81fb97d96a2b722368945f651e78",
"revisionTime": "2019-03-25T14:57:12Z"
},
{
"checksumSHA1": "ejjxT0+wDWWncfh0Rt3lSH4IbXQ=",
"checksumSHA1": "eaK7NuGdfEVypOnqYniZSuF2S6s=",
"path": "golang.org/x/crypto/blake2b",
"revision": "e84da0312774c21d64ee2317962ef669b27ffb41",
"revisionTime": "2018-10-24T13:26:30Z"
"revision": "a5d413f7728c81fb97d96a2b722368945f651e78",
"revisionTime": "2019-03-25T14:57:12Z"
},
{
"checksumSHA1": "2LpxYGSf068307b7bhAuVjvzLLc=",
"origin": "git.autistici.org/id/go-sso/vendor/golang.org/x/crypto/ed25519",
"path": "golang.org/x/crypto/ed25519",
"revision": "bb8238eef0a55b1020e35a05c95f4247e7a35090",
"revisionTime": "2018-10-31T07:04:43Z"
"revision": "347348a99d9c23c8b3c9884176492d4b7d74037e",
"revisionTime": "2019-03-18T20:43:40Z"
},
{
"checksumSHA1": "0JTAFXPkankmWcZGQJGScLDiaN8=",
"origin": "git.autistici.org/id/go-sso/vendor/golang.org/x/crypto/ed25519/internal/edwards25519",
"path": "golang.org/x/crypto/ed25519/internal/edwards25519",
"revision": "bb8238eef0a55b1020e35a05c95f4247e7a35090",
"revisionTime": "2018-10-31T07:04:43Z"
"revision": "347348a99d9c23c8b3c9884176492d4b7d74037e",
"revisionTime": "2019-03-18T20:43:40Z"
},
{
"checksumSHA1": "1MGpGDQqnUoRpv7VEcQrXOBydXE=",
"path": "golang.org/x/crypto/pbkdf2",
"revision": "e84da0312774c21d64ee2317962ef669b27ffb41",
"revisionTime": "2018-10-24T13:26:30Z"
"revision": "a5d413f7728c81fb97d96a2b722368945f651e78",
"revisionTime": "2019-03-25T14:57:12Z"
},
{
"checksumSHA1": "sx1nQShs40UKtcJZNJuvYtGesaI=",
"checksumSHA1": "q+Rqy6Spw6qDSj75TGEZF7nzoFM=",
"path": "golang.org/x/crypto/scrypt",
"revision": "e84da0312774c21d64ee2317962ef669b27ffb41",
"revisionTime": "2018-10-24T13:26:30Z"
"revision": "a5d413f7728c81fb97d96a2b722368945f651e78",
"revisionTime": "2019-03-25T14:57:12Z"
},
{
"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