1// Copyright 2011 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5// Package bcrypt implements Provos and Mazières's bcrypt adaptive hashing 6// algorithm. See http://www.usenix.org/event/usenix99/provos/provos.pdf 7package bcrypt // import "golang.org/x/crypto/bcrypt" 8 9// The code is a port of Provos and Mazières's C implementation. 10import ( 11 "crypto/rand" 12 "crypto/subtle" 13 "errors" 14 "fmt" 15 "io" 16 "strconv" 17 18 "golang.org/x/crypto/blowfish" 19) 20 21const ( 22 MinCost int = 4 // the minimum allowable cost as passed in to GenerateFromPassword 23 MaxCost int = 31 // the maximum allowable cost as passed in to GenerateFromPassword 24 DefaultCost int = 10 // the cost that will actually be set if a cost below MinCost is passed into GenerateFromPassword 25) 26 27// The error returned from CompareHashAndPassword when a password and hash do 28// not match. 29var ErrMismatchedHashAndPassword = errors.New("crypto/bcrypt: hashedPassword is not the hash of the given password") 30 31// The error returned from CompareHashAndPassword when a hash is too short to 32// be a bcrypt hash. 33var ErrHashTooShort = errors.New("crypto/bcrypt: hashedSecret too short to be a bcrypted password") 34 35// The error returned from CompareHashAndPassword when a hash was created with 36// a bcrypt algorithm newer than this implementation. 37type HashVersionTooNewError byte 38 39func (hv HashVersionTooNewError) Error() string { 40 return fmt.Sprintf("crypto/bcrypt: bcrypt algorithm version '%c' requested is newer than current version '%c'", byte(hv), majorVersion) 41} 42 43// The error returned from CompareHashAndPassword when a hash starts with something other than '$' 44type InvalidHashPrefixError byte 45 46func (ih InvalidHashPrefixError) Error() string { 47 return fmt.Sprintf("crypto/bcrypt: bcrypt hashes must start with '$', but hashedSecret started with '%c'", byte(ih)) 48} 49 50type InvalidCostError int 51 52func (ic InvalidCostError) Error() string { 53 return fmt.Sprintf("crypto/bcrypt: cost %d is outside allowed range (%d,%d)", int(ic), int(MinCost), int(MaxCost)) 54} 55 56const ( 57 majorVersion = '2' 58 minorVersion = 'a' 59 maxSaltSize = 16 60 maxCryptedHashSize = 23 61 encodedSaltSize = 22 62 encodedHashSize = 31 63 minHashSize = 59 64) 65 66// magicCipherData is an IV for the 64 Blowfish encryption calls in 67// bcrypt(). It's the string "OrpheanBeholderScryDoubt" in big-endian bytes. 68var magicCipherData = []byte{ 69 0x4f, 0x72, 0x70, 0x68, 70 0x65, 0x61, 0x6e, 0x42, 71 0x65, 0x68, 0x6f, 0x6c, 72 0x64, 0x65, 0x72, 0x53, 73 0x63, 0x72, 0x79, 0x44, 74 0x6f, 0x75, 0x62, 0x74, 75} 76 77type hashed struct { 78 hash []byte 79 salt []byte 80 cost int // allowed range is MinCost to MaxCost 81 major byte 82 minor byte 83} 84 85// GenerateFromPassword returns the bcrypt hash of the password at the given 86// cost. If the cost given is less than MinCost, the cost will be set to 87// DefaultCost, instead. Use CompareHashAndPassword, as defined in this package, 88// to compare the returned hashed password with its cleartext version. 89func GenerateFromPassword(password []byte, cost int) ([]byte, error) { 90 p, err := newFromPassword(password, cost) 91 if err != nil { 92 return nil, err 93 } 94 return p.Hash(), nil 95} 96 97// CompareHashAndPassword compares a bcrypt hashed password with its possible 98// plaintext equivalent. Returns nil on success, or an error on failure. 99func CompareHashAndPassword(hashedPassword, password []byte) error { 100 p, err := newFromHash(hashedPassword) 101 if err != nil { 102 return err 103 } 104 105 otherHash, err := bcrypt(password, p.cost, p.salt) 106 if err != nil { 107 return err 108 } 109 110 otherP := &hashed{otherHash, p.salt, p.cost, p.major, p.minor} 111 if subtle.ConstantTimeCompare(p.Hash(), otherP.Hash()) == 1 { 112 return nil 113 } 114 115 return ErrMismatchedHashAndPassword 116} 117 118// Cost returns the hashing cost used to create the given hashed 119// password. When, in the future, the hashing cost of a password system needs 120// to be increased in order to adjust for greater computational power, this 121// function allows one to establish which passwords need to be updated. 122func Cost(hashedPassword []byte) (int, error) { 123 p, err := newFromHash(hashedPassword) 124 if err != nil { 125 return 0, err 126 } 127 return p.cost, nil 128} 129 130func newFromPassword(password []byte, cost int) (*hashed, error) { 131 if cost < MinCost { 132 cost = DefaultCost 133 } 134 p := new(hashed) 135 p.major = majorVersion 136 p.minor = minorVersion 137 138 err := checkCost(cost) 139 if err != nil { 140 return nil, err 141 } 142 p.cost = cost 143 144 unencodedSalt := make([]byte, maxSaltSize) 145 _, err = io.ReadFull(rand.Reader, unencodedSalt) 146 if err != nil { 147 return nil, err 148 } 149 150 p.salt = base64Encode(unencodedSalt) 151 hash, err := bcrypt(password, p.cost, p.salt) 152 if err != nil { 153 return nil, err 154 } 155 p.hash = hash 156 return p, err 157} 158 159func newFromHash(hashedSecret []byte) (*hashed, error) { 160 if len(hashedSecret) < minHashSize { 161 return nil, ErrHashTooShort 162 } 163 p := new(hashed) 164 n, err := p.decodeVersion(hashedSecret) 165 if err != nil { 166 return nil, err 167 } 168 hashedSecret = hashedSecret[n:] 169 n, err = p.decodeCost(hashedSecret) 170 if err != nil { 171 return nil, err 172 } 173 hashedSecret = hashedSecret[n:] 174 175 // The "+2" is here because we'll have to append at most 2 '=' to the salt 176 // when base64 decoding it in expensiveBlowfishSetup(). 177 p.salt = make([]byte, encodedSaltSize, encodedSaltSize+2) 178 copy(p.salt, hashedSecret[:encodedSaltSize]) 179 180 hashedSecret = hashedSecret[encodedSaltSize:] 181 p.hash = make([]byte, len(hashedSecret)) 182 copy(p.hash, hashedSecret) 183 184 return p, nil 185} 186 187func bcrypt(password []byte, cost int, salt []byte) ([]byte, error) { 188 cipherData := make([]byte, len(magicCipherData)) 189 copy(cipherData, magicCipherData) 190 191 c, err := expensiveBlowfishSetup(password, uint32(cost), salt) 192 if err != nil { 193 return nil, err 194 } 195 196 for i := 0; i < 24; i += 8 { 197 for j := 0; j < 64; j++ { 198 c.Encrypt(cipherData[i:i+8], cipherData[i:i+8]) 199 } 200 } 201 202 // Bug compatibility with C bcrypt implementations. We only encode 23 of 203 // the 24 bytes encrypted. 204 hsh := base64Encode(cipherData[:maxCryptedHashSize]) 205 return hsh, nil 206} 207 208func expensiveBlowfishSetup(key []byte, cost uint32, salt []byte) (*blowfish.Cipher, error) { 209 csalt, err := base64Decode(salt) 210 if err != nil { 211 return nil, err 212 } 213 214 // Bug compatibility with C bcrypt implementations. They use the trailing 215 // NULL in the key string during expansion. 216 // We copy the key to prevent changing the underlying array. 217 ckey := append(key[:len(key):len(key)], 0) 218 219 c, err := blowfish.NewSaltedCipher(ckey, csalt) 220 if err != nil { 221 return nil, err 222 } 223 224 var i, rounds uint64 225 rounds = 1 << cost 226 for i = 0; i < rounds; i++ { 227 blowfish.ExpandKey(ckey, c) 228 blowfish.ExpandKey(csalt, c) 229 } 230 231 return c, nil 232} 233 234func (p *hashed) Hash() []byte { 235 arr := make([]byte, 60) 236 arr[0] = '$' 237 arr[1] = p.major 238 n := 2 239 if p.minor != 0 { 240 arr[2] = p.minor 241 n = 3 242 } 243 arr[n] = '$' 244 n++ 245 copy(arr[n:], []byte(fmt.Sprintf("%02d", p.cost))) 246 n += 2 247 arr[n] = '$' 248 n++ 249 copy(arr[n:], p.salt) 250 n += encodedSaltSize 251 copy(arr[n:], p.hash) 252 n += encodedHashSize 253 return arr[:n] 254} 255 256func (p *hashed) decodeVersion(sbytes []byte) (int, error) { 257 if sbytes[0] != '$' { 258 return -1, InvalidHashPrefixError(sbytes[0]) 259 } 260 if sbytes[1] > majorVersion { 261 return -1, HashVersionTooNewError(sbytes[1]) 262 } 263 p.major = sbytes[1] 264 n := 3 265 if sbytes[2] != '$' { 266 p.minor = sbytes[2] 267 n++ 268 } 269 return n, nil 270} 271 272// sbytes should begin where decodeVersion left off. 273func (p *hashed) decodeCost(sbytes []byte) (int, error) { 274 cost, err := strconv.Atoi(string(sbytes[0:2])) 275 if err != nil { 276 return -1, err 277 } 278 err = checkCost(cost) 279 if err != nil { 280 return -1, err 281 } 282 p.cost = cost 283 return 3, nil 284} 285 286func (p *hashed) String() string { 287 return fmt.Sprintf("&{hash: %#v, salt: %#v, cost: %d, major: %c, minor: %c}", string(p.hash), p.salt, p.cost, p.major, p.minor) 288} 289 290func checkCost(cost int) error { 291 if cost < MinCost || cost > MaxCost { 292 return InvalidCostError(cost) 293 } 294 return nil 295} 296