mirror of
https://github.com/therootcompany/golib.git
synced 2025-10-07 01:28:19 +00:00
ref!(envauth): change Verify return from bool to error
This commit is contained in:
parent
0893b3cb2d
commit
e8fbe603af
@ -12,7 +12,7 @@ creds := envauth.BasicCredentials{
|
|||||||
Password: os.Getenv("BASIC_AUTH_PASSWORD"),
|
Password: os.Getenv("BASIC_AUTH_PASSWORD"),
|
||||||
}
|
}
|
||||||
|
|
||||||
verified := creds.Verify("username", "password")
|
err := creds.Verify("username", "password")
|
||||||
```
|
```
|
||||||
|
|
||||||
## Basic Credentials: Username + Password
|
## Basic Credentials: Username + Password
|
||||||
@ -44,12 +44,17 @@ func main() {
|
|||||||
Password: password,
|
Password: password,
|
||||||
}
|
}
|
||||||
|
|
||||||
verified := creds.Verify("api", "secret")
|
if err := creds.Verify("api", "secret"); err != nil {
|
||||||
if verified {
|
switch err {
|
||||||
println("Authentication successful")
|
case envauth.ErrUnauthorized:
|
||||||
} else {
|
println("Authentication failed")
|
||||||
println("Authentication failed")
|
default:
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
println("Authentication successful")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -104,11 +109,16 @@ func main() {
|
|||||||
Iterations: iterations,
|
Iterations: iterations,
|
||||||
}
|
}
|
||||||
|
|
||||||
verified := creds.Verify("api", "secret")
|
if err := creds.Verify("api", "secret"); err != nil {
|
||||||
if verified {
|
switch err {
|
||||||
println("Authentication successful")
|
case envauth.ErrUnauthorized:
|
||||||
} else {
|
println("Authentication failed")
|
||||||
println("Authentication failed")
|
default:
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
println("Authentication successful")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -4,10 +4,13 @@ import (
|
|||||||
"crypto/pbkdf2"
|
"crypto/pbkdf2"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"crypto/subtle"
|
"crypto/subtle"
|
||||||
|
"errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var ErrUnauthorized = errors.New("unauthorized")
|
||||||
|
|
||||||
type BasicAuthVerifier interface {
|
type BasicAuthVerifier interface {
|
||||||
Verify(string, string) bool
|
Verify(string, string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// BasicCredentials holds user credentials
|
// BasicCredentials holds user credentials
|
||||||
@ -18,9 +21,9 @@ type BasicCredentials struct {
|
|||||||
|
|
||||||
// Returns true if username and password match.
|
// Returns true if username and password match.
|
||||||
// Uses SHA-256 and constant-time techniques to avoid revealing whether the username or password matches through timing attacks.
|
// Uses SHA-256 and constant-time techniques to avoid revealing whether the username or password matches through timing attacks.
|
||||||
func (c BasicCredentials) Verify(username string, password string) bool {
|
func (c BasicCredentials) Verify(username string, password string) error {
|
||||||
if len(password) == 0 {
|
if len(password) == 0 {
|
||||||
return false
|
return ErrUnauthorized
|
||||||
}
|
}
|
||||||
|
|
||||||
equal := 1
|
equal := 1
|
||||||
@ -38,7 +41,10 @@ func (c BasicCredentials) Verify(username string, password string) bool {
|
|||||||
v = subtle.ConstantTimeCompare(knownPasswordHash[:], passwordHash[:]) // 1 if same
|
v = subtle.ConstantTimeCompare(knownPasswordHash[:], passwordHash[:]) // 1 if same
|
||||||
equal = subtle.ConstantTimeSelect(v, equal, 0) // v ? x : y
|
equal = subtle.ConstantTimeSelect(v, equal, 0) // v ? x : y
|
||||||
|
|
||||||
return equal == 1
|
if equal == 1 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return ErrUnauthorized
|
||||||
}
|
}
|
||||||
|
|
||||||
// PBKDF2Credentials holds user credentials
|
// PBKDF2Credentials holds user credentials
|
||||||
@ -51,24 +57,24 @@ type PBKDF2Credentials struct {
|
|||||||
|
|
||||||
// Returns true if username and password match.
|
// Returns true if username and password match.
|
||||||
// Uses PBKDF2 and constant-time techniques to avoid revealing whether the username or password matches through timing attacks.
|
// Uses PBKDF2 and constant-time techniques to avoid revealing whether the username or password matches through timing attacks.
|
||||||
func (c PBKDF2Credentials) Verify(username string, password string) bool {
|
func (c PBKDF2Credentials) Verify(username string, password string) error {
|
||||||
keyLen := len(c.DerivedKey)
|
keyLen := len(c.DerivedKey)
|
||||||
dkKnownUser, err := pbkdf2.Key(sha256.New, c.Username, c.Salt, c.Iterations, keyLen)
|
dkKnownUser, err := pbkdf2.Key(sha256.New, c.Username, c.Salt, c.Iterations, keyLen)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(password) == 0 {
|
if len(password) == 0 {
|
||||||
return false
|
return ErrUnauthorized
|
||||||
}
|
}
|
||||||
|
|
||||||
dkUser, err := pbkdf2.Key(sha256.New, username, c.Salt, c.Iterations, keyLen)
|
dkUser, err := pbkdf2.Key(sha256.New, username, c.Salt, c.Iterations, keyLen)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return err
|
||||||
}
|
}
|
||||||
dkPass, err := pbkdf2.Key(sha256.New, password, c.Salt, c.Iterations, keyLen)
|
dkPass, err := pbkdf2.Key(sha256.New, password, c.Salt, c.Iterations, keyLen)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
equal := 1
|
equal := 1
|
||||||
@ -79,7 +85,11 @@ func (c PBKDF2Credentials) Verify(username string, password string) bool {
|
|||||||
v = subtle.ConstantTimeCompare(dkPass, c.DerivedKey) // 1 if same
|
v = subtle.ConstantTimeCompare(dkPass, c.DerivedKey) // 1 if same
|
||||||
equal = subtle.ConstantTimeSelect(v, equal, 0) // v ? x : y
|
equal = subtle.ConstantTimeSelect(v, equal, 0) // v ? x : y
|
||||||
|
|
||||||
return equal == 1
|
if equal == 1 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return ErrUnauthorized
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c PBKDF2Credentials) DeriveKey(username string, password string, keyLen int) ([]byte, error) {
|
func (c PBKDF2Credentials) DeriveKey(username string, password string, keyLen int) ([]byte, error) {
|
||||||
|
@ -14,50 +14,50 @@ func TestBasicCredentials_Verify(t *testing.T) {
|
|||||||
creds BasicCredentials
|
creds BasicCredentials
|
||||||
username string
|
username string
|
||||||
password string
|
password string
|
||||||
want bool
|
want error
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "empty username, correct password",
|
name: "empty username, correct password",
|
||||||
creds: BasicCredentials{Username: "", Password: "secret"},
|
creds: BasicCredentials{Username: "", Password: "secret"},
|
||||||
username: "",
|
username: "",
|
||||||
password: "secret",
|
password: "secret",
|
||||||
want: true,
|
want: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "correct username, correct password",
|
name: "correct username, correct password",
|
||||||
creds: BasicCredentials{Username: "user", Password: "secret"},
|
creds: BasicCredentials{Username: "user", Password: "secret"},
|
||||||
username: "user",
|
username: "user",
|
||||||
password: "secret",
|
password: "secret",
|
||||||
want: true,
|
want: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "incorrect username, correct password",
|
name: "incorrect username, correct password",
|
||||||
creds: BasicCredentials{Username: "user", Password: "secret"},
|
creds: BasicCredentials{Username: "user", Password: "secret"},
|
||||||
username: "wrong",
|
username: "wrong",
|
||||||
password: "secret",
|
password: "secret",
|
||||||
want: false,
|
want: ErrUnauthorized,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "correct username, incorrect password",
|
name: "correct username, incorrect password",
|
||||||
creds: BasicCredentials{Username: "user", Password: "secret"},
|
creds: BasicCredentials{Username: "user", Password: "secret"},
|
||||||
username: "user",
|
username: "user",
|
||||||
password: "wrong",
|
password: "wrong",
|
||||||
want: false,
|
want: ErrUnauthorized,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "correct username, empty password",
|
name: "correct username, empty password",
|
||||||
creds: BasicCredentials{Username: "user", Password: "secret"},
|
creds: BasicCredentials{Username: "user", Password: "secret"},
|
||||||
username: "user",
|
username: "user",
|
||||||
password: "",
|
password: "",
|
||||||
want: false,
|
want: ErrUnauthorized,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
got := tt.creds.Verify(tt.username, tt.password)
|
err := tt.creds.Verify(tt.username, tt.password)
|
||||||
if got != tt.want {
|
if err != tt.want {
|
||||||
t.Errorf("Verify(%q, %q) = %v; want %v", tt.username, tt.password, got, tt.want)
|
t.Errorf("Verify(%q, %q) = %v; want %v", tt.username, tt.password, err, tt.want)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -78,57 +78,57 @@ func TestPBKDF2Credentials_Verify(t *testing.T) {
|
|||||||
creds PBKDF2Credentials
|
creds PBKDF2Credentials
|
||||||
username string
|
username string
|
||||||
password string
|
password string
|
||||||
want bool
|
want error
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "empty username, correct password",
|
name: "empty username, correct password",
|
||||||
creds: PBKDF2Credentials{Username: "", DerivedKey: secretDigest, Salt: salt, Iterations: 1000},
|
creds: PBKDF2Credentials{Username: "", DerivedKey: secretDigest, Salt: salt, Iterations: 1000},
|
||||||
username: "",
|
username: "",
|
||||||
password: "secret",
|
password: "secret",
|
||||||
want: true,
|
want: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "correct username, correct password",
|
name: "correct username, correct password",
|
||||||
creds: PBKDF2Credentials{Username: "user", DerivedKey: secretDigest, Salt: salt, Iterations: 1000},
|
creds: PBKDF2Credentials{Username: "user", DerivedKey: secretDigest, Salt: salt, Iterations: 1000},
|
||||||
username: "user",
|
username: "user",
|
||||||
password: "secret",
|
password: "secret",
|
||||||
want: true,
|
want: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "incorrect username, correct password",
|
name: "incorrect username, correct password",
|
||||||
creds: PBKDF2Credentials{Username: "user", DerivedKey: secretDigest, Salt: salt, Iterations: 1000},
|
creds: PBKDF2Credentials{Username: "user", DerivedKey: secretDigest, Salt: salt, Iterations: 1000},
|
||||||
username: "wrong",
|
username: "wrong",
|
||||||
password: "secret",
|
password: "secret",
|
||||||
want: false,
|
want: ErrUnauthorized,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "correct username, incorrect password",
|
name: "correct username, incorrect password",
|
||||||
creds: PBKDF2Credentials{Username: "user", DerivedKey: secretDigest, Salt: salt, Iterations: 1000},
|
creds: PBKDF2Credentials{Username: "user", DerivedKey: secretDigest, Salt: salt, Iterations: 1000},
|
||||||
username: "user",
|
username: "user",
|
||||||
password: "wrong",
|
password: "wrong",
|
||||||
want: false,
|
want: ErrUnauthorized,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "correct username, empty password",
|
name: "correct username, empty password",
|
||||||
creds: PBKDF2Credentials{Username: "user", DerivedKey: secretDigest, Salt: salt, Iterations: 1000},
|
creds: PBKDF2Credentials{Username: "user", DerivedKey: secretDigest, Salt: salt, Iterations: 1000},
|
||||||
username: "user",
|
username: "user",
|
||||||
password: "",
|
password: "",
|
||||||
want: false,
|
want: ErrUnauthorized,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "empty username, empty pre-computed digest",
|
name: "empty username, empty pre-computed digest",
|
||||||
creds: PBKDF2Credentials{Username: "", DerivedKey: emptyDigest, Salt: salt, Iterations: 1000},
|
creds: PBKDF2Credentials{Username: "", DerivedKey: emptyDigest, Salt: salt, Iterations: 1000},
|
||||||
username: "",
|
username: "",
|
||||||
password: "",
|
password: "",
|
||||||
want: false,
|
want: ErrUnauthorized,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
got := tt.creds.Verify(tt.username, tt.password)
|
err := tt.creds.Verify(tt.username, tt.password)
|
||||||
if got != tt.want {
|
if err != tt.want {
|
||||||
t.Errorf("Verify(%q, %q) = %v; want %v", tt.username, tt.password, got, tt.want)
|
t.Errorf("Verify(%q, %q) = %v; want %v", tt.username, tt.password, err, tt.want)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user