diff --git a/pwhash/password.go b/pwhash/password.go index f8f84e36a180e2d6634cf0cb7eb0071a548d5f6c..1318a92df1c41d929afdb476110d26954040368a 100644 --- a/pwhash/password.go +++ b/pwhash/password.go @@ -1,3 +1,24 @@ +// 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 ( diff --git a/pwhash/password_test.go b/pwhash/password_test.go index 27ef8f4e23d28f90c4baf17ff674a153e6ae8c98..ab08ea213a7e90d2d812c3f377146ff5be45a61e 100644 --- a/pwhash/password_test.go +++ b/pwhash/password_test.go @@ -1,6 +1,9 @@ package pwhash -import "testing" +import ( + "fmt" + "testing" +) func TestArgon2(t *testing.T) { testImpl(t, NewArgon2()) @@ -57,3 +60,34 @@ func testImpl(t *testing.T, h PasswordHash) { } } } + +func BenchmarkArgon2(b *testing.B) { + var testParams []argon2Params + for iTime := 1; iTime <= 5; iTime++ { + for iThreads := 1; iThreads <= 8; iThreads *= 2 { + for iMem := 1; iMem <= 16; iMem *= 2 { + testParams = append(testParams, argon2Params{ + Time: uint32(iTime), + Memory: uint32(iMem * 1024), + Threads: uint8(iThreads), + }) + } + } + } + + goodPw := "good password" + badPw := "definitely not the good password" + + for _, tp := range testParams { + name := fmt.Sprintf("%d/%d/%d", tp.Time, tp.Memory, tp.Threads) + b.Run(name, func(b *testing.B) { + h := NewArgon2WithParams(tp.Time, tp.Memory, tp.Threads) + encPw := h.Encrypt(goodPw) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + h.ComparePassword(encPw, badPw) // nolint + } + }) + } +}