Add option to check for weak passwords

This commit is contained in:
eikendev 2021-01-16 15:29:04 +01:00
parent ad56422838
commit b06bd51d21
No known key found for this signature in database
GPG key ID: A1BDB1B28C8EF694
12 changed files with 141 additions and 15 deletions

View file

@ -10,11 +10,12 @@ import (
// Manager holds information for managing credentials.
type Manager struct {
checkHIBP bool
argon2Params *argon2id.Params
}
// CreateManager instanciates a credential manager.
func CreateManager(c configuration.CryptoConfig) *Manager {
func CreateManager(checkHIBP bool, c configuration.CryptoConfig) *Manager {
log.Println("Setting up credential manager.")
argon2Params := &argon2id.Params{
@ -25,5 +26,8 @@ func CreateManager(c configuration.CryptoConfig) *Manager {
KeyLength: c.Argon2.KeyLength,
}
return &Manager{argon2Params: argon2Params}
return &Manager{
checkHIBP: checkHIBP,
argon2Params: argon2Params,
}
}

View file

@ -0,0 +1,61 @@
package credentials
import (
"crypto/sha1"
"fmt"
"io/ioutil"
"log"
"net/http"
"strings"
)
const (
base = "https://api.pwnedpasswords.com"
pwnedHashesEndpoint = "/range"
pwnedHashesURL = base + pwnedHashesEndpoint + "/"
)
// IsPasswordPwned determines whether or not the password is weak.
func IsPasswordPwned(password string) (bool, error) {
if len(password) == 0 {
return true, nil
}
hash := sha1.Sum([]byte(password))
hashStr := fmt.Sprintf("%X", hash)
lookup := hashStr[0:5]
match := hashStr[5:]
log.Printf("Checking HIBP for hashes starting with '%s'.\n", lookup)
resp, err := http.Get(pwnedHashesURL + lookup)
if err != nil {
return false, err
}
if resp.StatusCode != http.StatusOK {
log.Fatalf("Request failed with HTTP %s.", resp.Status)
}
defer resp.Body.Close()
bodyText, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
bodyStr := string(bodyText)
lines := strings.Split(bodyStr, "\n")
for _, line := range lines {
separated := strings.Split(line, ":")
if len(separated) != 2 {
return false, fmt.Errorf("HIPB API returned malformed response: %s", line)
}
if separated[0] == match {
return true, nil
}
}
return false, nil
}

View file

@ -0,0 +1,23 @@
package credentials
import "testing"
type isPasswordPwnedTest struct {
arg string
exp1 bool
exp2 error
}
var isPasswordPwnedTests = []isPasswordPwnedTest{
{"", true, nil},
{"password", true, nil},
{"2y6bWMETuHpNP08HCZq00QAAzE6nmwEb", false, nil},
}
func TestIsPasswordPwned(t *testing.T) {
for _, test := range isPasswordPwnedTests {
if out1, out2 := IsPasswordPwned(test.arg); out1 != test.exp1 || out2 != test.exp2 {
t.Errorf("Output (%t,%q) not equal to expected (%t,%q)", out1, out2, test.exp1, test.exp2)
}
}
}

View file

@ -1,13 +1,23 @@
package credentials
import (
"errors"
"log"
"github.com/alexedwards/argon2id"
)
// CreatePasswordHash returns a hashed version of the given password.
func (m *Manager) CreatePasswordHash(password string) []byte {
func (m *Manager) CreatePasswordHash(password string) ([]byte, error) {
if m.checkHIBP {
pwned, err := IsPasswordPwned(password)
if err != nil {
return []byte{}, errors.New("HIBP is not available, please wait until service is available again")
} else if pwned {
return []byte{}, errors.New("Password is pwned, please choose another one")
}
}
hash, err := argon2id.CreateHash(password, m.argon2Params)
if err != nil {
@ -15,7 +25,7 @@ func (m *Manager) CreatePasswordHash(password string) []byte {
panic(err)
}
return []byte(hash)
return []byte(hash), nil
}
// ComparePassword compares a hashed password with its possible plaintext equivalent.