1
Fork 0

Distinguish empty key in password from absence of key.

This commit is contained in:
Juliusz Chroboczek 2024-04-12 01:05:48 +02:00
parent 4ad7c2f303
commit 695c379a6c
7 changed files with 46 additions and 19 deletions

View File

@ -60,10 +60,11 @@ func main() {
key := pbkdf2.Key( key := pbkdf2.Key(
[]byte(pw), salt, iterations, length, sha256.New, []byte(pw), salt, iterations, length, sha256.New,
) )
encoded := hex.EncodeToString(key)
p = group.Password{ p = group.Password{
Type: "pbkdf2", Type: "pbkdf2",
Hash: "sha-256", Hash: "sha-256",
Key: hex.EncodeToString(key), Key: &encoded,
Salt: hex.EncodeToString(salt), Salt: hex.EncodeToString(salt),
Iterations: iterations, Iterations: iterations,
} }
@ -75,9 +76,10 @@ func main() {
log.Fatalf("Couldn't hash password: %v", err) log.Fatalf("Couldn't hash password: %v", err)
} }
k := string(key)
p = group.Password{ p = group.Password{
Type: "bcrypt", Type: "bcrypt",
Key: string(key), Key: &k,
} }
} else { } else {
log.Fatalf("Unknown hash type %v", algorithm) log.Fatalf("Unknown hash type %v", algorithm)

View File

@ -17,7 +17,7 @@ import (
type RawPassword struct { type RawPassword struct {
Type string `json:"type,omitempty"` Type string `json:"type,omitempty"`
Hash string `json:"hash,omitempty"` Hash string `json:"hash,omitempty"`
Key string `json:"key"` Key *string `json:"key,omitempty"`
Salt string `json:"salt,omitempty"` Salt string `json:"salt,omitempty"`
Iterations int `json:"iterations,omitempty"` Iterations int `json:"iterations,omitempty"`
} }
@ -29,11 +29,17 @@ func (p Password) Match(pw string) (bool, error) {
case "": case "":
return false, errors.New("missing password") return false, errors.New("missing password")
case "plain": case "plain":
return p.Key == pw, nil if p.Key == nil {
return false, errors.New("missing key")
}
return *p.Key == pw, nil
case "wildcard": case "wildcard":
return true, nil return true, nil
case "pbkdf2": case "pbkdf2":
key, err := hex.DecodeString(p.Key) if p.Key == nil {
return false, errors.New("missing key")
}
key, err := hex.DecodeString(*p.Key)
if err != nil { if err != nil {
return false, err return false, err
} }
@ -53,7 +59,10 @@ func (p Password) Match(pw string) (bool, error) {
) )
return bytes.Equal(key, theirKey), nil return bytes.Equal(key, theirKey), nil
case "bcrypt": case "bcrypt":
err := bcrypt.CompareHashAndPassword([]byte(p.Key), []byte(pw)) if p.Key == nil {
return false, errors.New("missing key")
}
err := bcrypt.CompareHashAndPassword([]byte(*p.Key), []byte(pw))
if err == bcrypt.ErrMismatchedHashAndPassword { if err == bcrypt.ErrMismatchedHashAndPassword {
return false, nil return false, nil
} }
@ -69,7 +78,7 @@ func (p *Password) UnmarshalJSON(b []byte) error {
if err == nil { if err == nil {
*p = Password{ *p = Password{
Type: "plain", Type: "plain",
Key: k, Key: &k,
} }
return nil return nil
} }

View File

@ -7,23 +7,28 @@ import (
"testing" "testing"
) )
var key1 = ""
var pw1 = Password{ var pw1 = Password{
Type: "plain", Type: "plain",
Key: &key1,
} }
var key2 = "pass"
var pw2 = Password{ var pw2 = Password{
Type: "plain", Type: "plain",
Key: "pass", Key: &key2,
} }
var key3 = "fe499504e8f144693fae828e8e371d50e019d0e4c84994fa03f7f445bd8a570a"
var pw3 = Password{ var pw3 = Password{
Type: "pbkdf2", Type: "pbkdf2",
Hash: "sha-256", Hash: "sha-256",
Key: "fe499504e8f144693fae828e8e371d50e019d0e4c84994fa03f7f445bd8a570a", Key: &key3,
Salt: "bcc1717851030776", Salt: "bcc1717851030776",
Iterations: 4096, Iterations: 4096,
} }
var key4 = "$2a$10$afOr2f33onT/nDFFyT3mbOq5FMSw1wWXfyTXQTBMbKvZpBkoD3Qwu"
var pw4 = Password{ var pw4 = Password{
Type: "bcrypt", Type: "bcrypt",
Key: "$2a$10$afOr2f33onT/nDFFyT3mbOq5FMSw1wWXfyTXQTBMbKvZpBkoD3Qwu", Key: &key4,
} }
var pw5 = Password{} var pw5 = Password{}
var pw6 = Password{ var pw6 = Password{
@ -66,6 +71,15 @@ func TestBad(t *testing.T) {
} }
} }
func TestEmptyKey(t *testing.T) {
for _, tpe := range []string{"", "plain", "pbkdf2", "bcrypt", "bad"} {
pw := Password{Type: tpe}
if match, err := pw.Match(""); err == nil || match {
t.Errorf("empty password of type %v didn't error", tpe)
}
}
}
func TestJSON(t *testing.T) { func TestJSON(t *testing.T) {
plain, err := json.Marshal(pw2) plain, err := json.Marshal(pw2)
if err != nil || string(plain) != `"pass"` { if err != nil || string(plain) != `"pass"` {

View File

@ -629,7 +629,7 @@ func DeleteUser(group, username, etag string) error {
} }
func UpdateUser(group, username, etag string, user *UserDescription) error { func UpdateUser(group, username, etag string, user *UserDescription) error {
if user.Password.Type != "" || user.Password.Key != "" { if user.Password.Type != "" || user.Password.Key != nil {
return errors.New("user description is not sanitised") return errors.New("user description is not sanitised")
} }
groups.mu.Lock() groups.mu.Lock()

View File

@ -216,16 +216,17 @@ func TestWritableGroups(t *testing.T) {
t.Errorf("UpdateUser: got %v", err) t.Errorf("UpdateUser: got %v", err)
} }
pw := "pw"
err = SetUserPassword("test", "jch", Password{ err = SetUserPassword("test", "jch", Password{
Type: "", Type: "",
Key: "pw", Key: &pw,
}) })
if err != nil { if err != nil {
t.Errorf("SetUserPassword: got %v", err) t.Errorf("SetUserPassword: got %v", err)
} }
desc, err = GetDescription("test") desc, err = GetDescription("test")
if err != nil || desc.Users["jch"].Password.Key != "pw" { if err != nil || *desc.Users["jch"].Password.Key != "pw" {
t.Errorf("GetDescription: got %v %v, expected %v %v", t.Errorf("GetDescription: got %v %v, expected %v %v",
err, desc.Users["jch"].Password.Key, nil, "pw", err, desc.Users["jch"].Password.Key, nil, "pw",
) )

View File

@ -408,10 +408,11 @@ func passwordHandler(w http.ResponseWriter, r *http.Request, g, user string) {
} }
iterations := 4096 iterations := 4096
key := pbkdf2.Key(body, salt, iterations, 32, sha256.New) key := pbkdf2.Key(body, salt, iterations, 32, sha256.New)
encoded := hex.EncodeToString(key)
pw := group.Password{ pw := group.Password{
Type: "pbkdf2", Type: "pbkdf2",
Hash: "sha-256", Hash: "sha-256",
Key: hex.EncodeToString(key), Key: &encoded,
Salt: hex.EncodeToString(salt), Salt: hex.EncodeToString(salt),
Iterations: iterations, Iterations: iterations,
} }

View File

@ -236,7 +236,7 @@ func TestApi(t *testing.T) {
if err != nil { if err != nil {
t.Errorf("Get user: %v", err) t.Errorf("Get user: %v", err)
} }
if user.Password.Type != "" && user.Password.Key != "" { if user.Password.Type != "" && user.Password.Key != nil {
t.Errorf("User not sanitised properly") t.Errorf("User not sanitised properly")
} }