go.crypto/bcrypt: add Cost function
Extracts the cost used to generate a bcrypt hash from the hash itself. R=agl, agl, dchest CC=golang-dev https://golang.org/cl/6810051
This commit is contained in:
committed by
Adam Langley
parent
1582bf0781
commit
2c5e207468
+18
-7
@@ -76,7 +76,7 @@ var magicCipherData = []byte{
|
||||
type hashed struct {
|
||||
hash []byte
|
||||
salt []byte
|
||||
cost uint32 // allowed range is MinCost to MaxCost
|
||||
cost int // allowed range is MinCost to MaxCost
|
||||
major byte
|
||||
minor byte
|
||||
}
|
||||
@@ -94,8 +94,7 @@ func GenerateFromPassword(password []byte, cost int) ([]byte, error) {
|
||||
}
|
||||
|
||||
// CompareHashAndPassword compares a bcrypt hashed password with its possible
|
||||
// plaintext equivalent. Note: Using bytes.Equal for this job is
|
||||
// insecure. Returns nil on success, or an error on failure.
|
||||
// plaintext equivalent. Returns nil on success, or an error on failure.
|
||||
func CompareHashAndPassword(hashedPassword, password []byte) error {
|
||||
p, err := newFromHash(hashedPassword)
|
||||
if err != nil {
|
||||
@@ -115,6 +114,18 @@ func CompareHashAndPassword(hashedPassword, password []byte) error {
|
||||
return ErrMismatchedHashAndPassword
|
||||
}
|
||||
|
||||
// Cost returns the hashing cost used to create the given hashed
|
||||
// password. When, in the future, the hashing cost of a password system needs
|
||||
// to be increased in order to adjust for greater computational power, this
|
||||
// function allows one to establish which passwords need to be updated.
|
||||
func Cost(hashedPassword []byte) (int, error) {
|
||||
p, err := newFromHash(hashedPassword)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return p.cost, nil
|
||||
}
|
||||
|
||||
func newFromPassword(password []byte, cost int) (*hashed, error) {
|
||||
if cost < MinCost {
|
||||
cost = DefaultCost
|
||||
@@ -127,7 +138,7 @@ func newFromPassword(password []byte, cost int) (*hashed, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p.cost = uint32(cost)
|
||||
p.cost = cost
|
||||
|
||||
unencodedSalt := make([]byte, maxSaltSize)
|
||||
_, err = io.ReadFull(rand.Reader, unencodedSalt)
|
||||
@@ -172,11 +183,11 @@ func newFromHash(hashedSecret []byte) (*hashed, error) {
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func bcrypt(password []byte, cost uint32, salt []byte) ([]byte, error) {
|
||||
func bcrypt(password []byte, cost int, salt []byte) ([]byte, error) {
|
||||
cipherData := make([]byte, len(magicCipherData))
|
||||
copy(cipherData, magicCipherData)
|
||||
|
||||
c, err := expensiveBlowfishSetup(password, cost, salt)
|
||||
c, err := expensiveBlowfishSetup(password, uint32(cost), salt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -266,7 +277,7 @@ func (p *hashed) decodeCost(sbytes []byte) (int, error) {
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
p.cost = uint32(cost)
|
||||
p.cost = cost
|
||||
return 3, nil
|
||||
}
|
||||
|
||||
|
||||
+24
-1
@@ -6,6 +6,7 @@ package bcrypt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -117,6 +118,28 @@ func TestUnpaddedBase64Encoding(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCost(t *testing.T) {
|
||||
suffix := "XajjQvNhvvRt5GSeFk1xFe5l47dONXg781AmZtd869sO8zfsHuw7C"
|
||||
for _, vers := range []string{"2a", "2"} {
|
||||
for _, cost := range []int{4, 10} {
|
||||
s := fmt.Sprintf("$%s$%02d$%s", vers, cost, suffix)
|
||||
h := []byte(s)
|
||||
actual, err := Cost(h)
|
||||
if err != nil {
|
||||
t.Errorf("Cost, error: %s", err)
|
||||
continue
|
||||
}
|
||||
if actual != cost {
|
||||
t.Errorf("Cost, expected: %d, actual: %d", cost, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
_, err := Cost([]byte("$a$a$" + suffix))
|
||||
if err == nil {
|
||||
t.Errorf("Cost, malformed but no error returned")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCostValidationInHash(t *testing.T) {
|
||||
if testing.Short() {
|
||||
return
|
||||
}
|
||||
@@ -125,7 +148,7 @@ func TestCost(t *testing.T) {
|
||||
|
||||
for c := 0; c < MinCost; c++ {
|
||||
p, _ := newFromPassword(pass, c)
|
||||
if p.cost != uint32(DefaultCost) {
|
||||
if p.cost != DefaultCost {
|
||||
t.Errorf("newFromPassword should default costs below %d to %d, but was %d", MinCost, DefaultCost, p.cost)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user