Skip to content

Commit efc9484

Browse files
committed
bcrypt support
1 parent 46b9627 commit efc9484

File tree

5 files changed

+58
-18
lines changed

5 files changed

+58
-18
lines changed

auth.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1+
// Package auth is an implementation of HTTP Basic and HTTP Digest authentication.
12
package auth
23

34
import "net/http"
45

5-
/*
6+
/*
67
Request handlers must take AuthenticatedRequest instead of http.Request
78
*/
89
type AuthenticatedRequest struct {
910
http.Request
10-
/*
11+
/*
1112
Authenticated user name. Current API implies that Username is
1213
never empty, which means that authentication is always done
1314
before calling the request handler.

basic.go

Lines changed: 52 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,30 @@
11
package auth
22

33
import (
4+
"bytes"
45
"crypto/sha1"
56
"crypto/subtle"
67
"encoding/base64"
8+
"errors"
79
"net/http"
810
"strings"
11+
12+
"golang.org/x/crypto/bcrypt"
13+
)
14+
15+
type compareFunc func(hashedPassword, password []byte) error
16+
17+
var (
18+
errMismatchedHashAndPassword = errors.New("mismatched hash and password")
19+
20+
compareFuncs = []struct {
21+
prefix string
22+
compare compareFunc
23+
}{
24+
{"", compareMD5HashAndPassword}, // default compareFunc
25+
{"{SHA}", compareShaHashAndPassword},
26+
{"$2y$", bcrypt.CompareHashAndPassword},
27+
}
928
)
1029

1130
type BasicAuth struct {
@@ -34,28 +53,46 @@ func (a *BasicAuth) CheckAuth(r *http.Request) string {
3453
if len(pair) != 2 {
3554
return ""
3655
}
37-
passwd := a.Secrets(pair[0], a.Realm)
38-
if passwd == "" {
56+
user, password := pair[0], pair[1]
57+
secret := a.Secrets(user, a.Realm)
58+
if secret == "" {
3959
return ""
4060
}
41-
if strings.HasPrefix(passwd, "{SHA}") {
42-
d := sha1.New()
43-
d.Write([]byte(pair[1]))
44-
if subtle.ConstantTimeCompare([]byte(passwd[5:]), []byte(base64.StdEncoding.EncodeToString(d.Sum(nil)))) != 1 {
45-
return ""
46-
}
47-
} else {
48-
e := NewMD5Entry(passwd)
49-
if e == nil {
50-
return ""
51-
}
52-
if subtle.ConstantTimeCompare([]byte(passwd), MD5Crypt([]byte(pair[1]), e.Salt, e.Magic)) != 1 {
53-
return ""
61+
compare := compareFuncs[0].compare
62+
for _, cmp := range compareFuncs[1:] {
63+
if strings.HasPrefix(secret, cmp.prefix) {
64+
compare = cmp.compare
65+
break
5466
}
5567
}
68+
if compare([]byte(secret), []byte(password)) != nil {
69+
return ""
70+
}
5671
return pair[0]
5772
}
5873

74+
func compareShaHashAndPassword(hashedPassword, password []byte) error {
75+
d := sha1.New()
76+
d.Write(password)
77+
if subtle.ConstantTimeCompare(hashedPassword[5:], []byte(base64.StdEncoding.EncodeToString(d.Sum(nil)))) != 1 {
78+
return errMismatchedHashAndPassword
79+
}
80+
return nil
81+
}
82+
83+
func compareMD5HashAndPassword(hashedPassword, password []byte) error {
84+
parts := bytes.SplitN(hashedPassword, []byte("$"), 4)
85+
if len(parts) != 4 {
86+
return errMismatchedHashAndPassword
87+
}
88+
magic := []byte("$" + string(parts[1]) + "$")
89+
salt := parts[2]
90+
if subtle.ConstantTimeCompare(hashedPassword, MD5Crypt(password, salt, magic)) != 1 {
91+
return errMismatchedHashAndPassword
92+
}
93+
return nil
94+
}
95+
5996
/*
6097
http.Handler for BasicAuth which initiates the authentication process
6198
(or requires reauthentication).

basic_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ func TestAuthBasic(t *testing.T) {
2727
data := [][]string{
2828
{"test", "hello"},
2929
{"test2", "hello2"},
30+
{"test3", "hello3"},
3031
{"test16", "topsecret"},
3132
}
3233
for _, tc := range data {

test.htpasswd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
test:{SHA}qvTGHdzF6KLavt4PO0gs2a6pQ00=
22
test2:$apr1$a0j62R97$mYqFkloXH0/UOaUnAiV2b0
33
test16:$apr1$JI4wh3am$AmhephVqLTUyAVpFQeHZC0
4+
test3:$2y$05$ih3C91zUBSTFcAh2mQnZYuob0UOZVEf16wl/ukgjDhjvj.xgM1WwS

users_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ func TestHtpasswdFile(t *testing.T) {
2626
if passwd != "{SHA}qvTGHdzF6KLavt4PO0gs2a6pQ00=" {
2727
t.Fatal("Incorrect passwd for test user:", passwd)
2828
}
29-
passwd = secrets("test3", "blah")
29+
passwd = secrets("nosuchuser", "blah")
3030
if passwd != "" {
3131
t.Fatal("Got passwd for non-existant user:", passwd)
3232
}

0 commit comments

Comments
 (0)