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:
Jeff Hodges
2012-10-31 12:35:46 -04:00
committed by Adam Langley
parent 1582bf0781
commit 2c5e207468
2 changed files with 42 additions and 8 deletions
+18 -7
View File
@@ -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
View File
@@ -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)
}
}