Commit 8628344a authored by Juliusz Chroboczek's avatar Juliusz Chroboczek

Add support for hashing password with BCrypt.

parent 2a32ac8f
...@@ -9,22 +9,31 @@ import ( ...@@ -9,22 +9,31 @@ import (
"fmt" "fmt"
"log" "log"
"os" "os"
"strings"
"golang.org/x/crypto/bcrypt"
"golang.org/x/crypto/pbkdf2" "golang.org/x/crypto/pbkdf2"
"github.com/jech/galene/group" "github.com/jech/galene/group"
) )
func main() { func main() {
var algorithm string
var iterations int var iterations int
var cost int
var length int var length int
var saltLen int var saltLen int
var username string var username string
flag.StringVar(&username, "user", "", flag.StringVar(&username, "user", "",
"generate entry for given `username`") "generate entry for given `username`")
flag.IntVar(&iterations, "iterations", 4096, "`number` of iterations") flag.StringVar(&algorithm, "hash", "pbkdf2",
flag.IntVar(&length, "key", 32, "key `length`") "hashing `algorithm`")
flag.IntVar(&saltLen, "salt", 8, "salt `length`") flag.IntVar(&iterations, "iterations", 4096,
"`number` of iterations (pbkdf2)")
flag.IntVar(&cost, "cost", bcrypt.DefaultCost,
"`cost` (bcrypt)")
flag.IntVar(&length, "key", 32, "key `length` (pbkdf2)")
flag.IntVar(&saltLen, "salt", 8, "salt `length` (pbkdf2)")
flag.Parse() flag.Parse()
if len(flag.Args()) == 0 { if len(flag.Args()) == 0 {
...@@ -43,17 +52,34 @@ func main() { ...@@ -43,17 +52,34 @@ func main() {
if err != nil { if err != nil {
log.Fatalf("Salt: %v", err) log.Fatalf("Salt: %v", err)
} }
var p group.Password
if strings.EqualFold(algorithm, "pbkdf2") {
key := pbkdf2.Key( key := pbkdf2.Key(
[]byte(pw), salt, iterations, length, sha256.New, []byte(pw), salt, iterations, length, sha256.New,
) )
p = group.Password{
p := group.Password{
Type: "pbkdf2", Type: "pbkdf2",
Hash: "sha-256", Hash: "sha-256",
Key: hex.EncodeToString(key), Key: hex.EncodeToString(key),
Salt: hex.EncodeToString(salt), Salt: hex.EncodeToString(salt),
Iterations: iterations, Iterations: iterations,
} }
} else if strings.EqualFold(algorithm, "bcrypt") {
key, err := bcrypt.GenerateFromPassword(
[]byte(pw), cost,
)
if err != nil {
log.Fatalf("Couldn't hash password: %v", err)
}
p = group.Password{
Type: "bcrypt",
Key: string(key),
}
} else {
log.Fatalf("Unknown hash type %v", algorithm)
}
e := json.NewEncoder(os.Stdout) e := json.NewEncoder(os.Stdout)
if username != "" { if username != "" {
creds := group.ClientPattern{ creds := group.ClientPattern{
......
...@@ -8,6 +8,7 @@ import ( ...@@ -8,6 +8,7 @@ import (
"errors" "errors"
"hash" "hash"
"golang.org/x/crypto/bcrypt"
"golang.org/x/crypto/pbkdf2" "golang.org/x/crypto/pbkdf2"
"github.com/jech/galene/conn" "github.com/jech/galene/conn"
...@@ -47,6 +48,12 @@ func (p Password) Match(pw string) (bool, error) { ...@@ -47,6 +48,12 @@ func (p Password) Match(pw string) (bool, error) {
[]byte(pw), salt, p.Iterations, len(key), h, []byte(pw), salt, p.Iterations, len(key), h,
) )
return bytes.Equal(key, theirKey), nil return bytes.Equal(key, theirKey), nil
case "bcrypt":
err := bcrypt.CompareHashAndPassword([]byte(p.Key), []byte(pw))
if err == bcrypt.ErrMismatchedHashAndPassword {
return false, nil
}
return err == nil, err
default: default:
return false, errors.New("unknown password type") return false, errors.New("unknown password type")
} }
......
...@@ -17,6 +17,10 @@ var pw3 = Password{ ...@@ -17,6 +17,10 @@ var pw3 = Password{
Iterations: 4096, Iterations: 4096,
} }
var pw4 = Password{ var pw4 = Password{
Type: "bcrypt",
Key: "$2a$10$afOr2f33onT/nDFFyT3mbOq5FMSw1wWXfyTXQTBMbKvZpBkoD3Qwu",
}
var pw5 = Password{
Type: "bad", Type: "bad",
} }
...@@ -27,6 +31,9 @@ func TestGood(t *testing.T) { ...@@ -27,6 +31,9 @@ func TestGood(t *testing.T) {
if match, err := pw3.Match("pass"); err != nil || !match { if match, err := pw3.Match("pass"); err != nil || !match {
t.Errorf("pw3 doesn't match (%v)", err) t.Errorf("pw3 doesn't match (%v)", err)
} }
if match, err := pw4.Match("pass"); err != nil || !match {
t.Errorf("pw4 doesn't match (%v)", err)
}
} }
func TestBad(t *testing.T) { func TestBad(t *testing.T) {
...@@ -39,7 +46,10 @@ func TestBad(t *testing.T) { ...@@ -39,7 +46,10 @@ func TestBad(t *testing.T) {
if match, err := pw3.Match("bad"); err != nil || match { if match, err := pw3.Match("bad"); err != nil || match {
t.Errorf("pw3 matches") t.Errorf("pw3 matches")
} }
if match, err := pw4.Match("bad"); err == nil || match { if match, err := pw4.Match("bad"); err != nil || match {
t.Errorf("pw4 matches")
}
if match, err := pw5.Match("bad"); err == nil || match {
t.Errorf("pw4 matches") t.Errorf("pw4 matches")
} }
} }
...@@ -50,7 +60,7 @@ func TestJSON(t *testing.T) { ...@@ -50,7 +60,7 @@ func TestJSON(t *testing.T) {
t.Errorf("Expected \"pass\", got %v", string(plain)) t.Errorf("Expected \"pass\", got %v", string(plain))
} }
for _, pw := range []Password{pw1, pw2, pw3, pw4} { for _, pw := range []Password{pw1, pw2, pw3, pw4, pw5} {
j, err := json.Marshal(pw) j, err := json.Marshal(pw)
if err != nil { if err != nil {
t.Fatalf("Marshal: %v", err) t.Fatalf("Marshal: %v", err)
...@@ -85,3 +95,12 @@ func BenchmarkPBKDF2(b *testing.B) { ...@@ -85,3 +95,12 @@ func BenchmarkPBKDF2(b *testing.B) {
} }
} }
} }
func BenchmarkBCrypt(b *testing.B) {
for i := 0; i < b.N; i++ {
match, err := pw4.Match("bad")
if err != nil || match {
b.Errorf("pw3 matched")
}
}
}
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