Left: | ||
Right: |
LEFT | RIGHT |
---|---|
1 // Copyright 2011 The Go Authors. All rights reserved. | 1 // Copyright 2011 The Go Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style | 2 // Use of this source code is governed by a BSD-style |
3 // license that can be found in the LICENSE file. | 3 // license that can be found in the LICENSE file. |
4 | 4 |
5 // Package bcrypt implements Provos and Mazières's bcrypt adapative hashing | 5 // Package bcrypt implements Provos and Mazières's bcrypt adapative hashing |
6 // algorithm. See http://www.usenix.org/event/usenix99/provos/provos.pdf | 6 // algorithm. See http://www.usenix.org/event/usenix99/provos/provos.pdf |
7 package bcrypt | 7 package bcrypt |
8 | 8 |
9 // The code is a port of Provos and Mazières's C implementation.· | 9 // The code is a port of Provos and Mazières's C implementation.· |
10 import ( | 10 import ( |
11 "crypto/blowfish" | 11 "crypto/blowfish" |
12 "crypto/rand" | 12 "crypto/rand" |
13 "crypto/subtle" | 13 "crypto/subtle" |
14 "fmt" | 14 "fmt" |
15 "io" | 15 "io" |
16 "os" | 16 "os" |
17 "strconv" | 17 "strconv" |
18 ) | 18 ) |
19 | 19 |
20 const ( | 20 const ( |
21 MinCost int = 4 // the minimum allowable cost as passed in to Gener ateFromPassword | 21 MinCost int = 4 // the minimum allowable cost as passed in to Gener ateFromPassword |
22 MaxCost int = 31 // the maximum allowable cost as passed in to Gener ateFromPassword | 22 MaxCost int = 31 // the maximum allowable cost as passed in to Gener ateFromPassword |
23 DefaultCost int = 10 // the cost that will actually be set if a cost bel ow MinCost is passed into GenerateFromPassword | 23 DefaultCost int = 10 // the cost that will actually be set if a cost bel ow MinCost is passed into GenerateFromPassword |
24 ) | 24 ) |
25 | 25 |
26 // The error returned from CompareHashAndPassword when a password and hash do | 26 // The error returned from CompareHashAndPassword when a password and hash do |
27 // not match. | 27 // not match. |
28 var MismatchedHashAndPassword = os.NewError("crypto:bcrypt: hashedPassword is no t the hash of the given password") | 28 var MismatchedHashAndPasswordError = os.NewError("crypto/bcrypt: hashedPassword is not the hash of the given password") |
agl1
2011/09/16 20:43:47
The convention seems to be that errors have a name
agl1
2011/09/16 20:43:47
s!crypto:!crypto/!
| |
29 | 29 |
30 // The error returned from CompareHashAndPassword when a hash is too short to | 30 // The error returned from CompareHashAndPassword when a hash is too short to |
31 // be a bcrypt hash. | 31 // be a bcrypt hash. |
32 var HashTooShort = os.NewError("crypto/bcrypt: hashedSecret too short to be a bc rypted password") | 32 var HashTooShortError = os.NewError("crypto/bcrypt: hashedSecret too short to be a bcrypted password") |
33 | 33 |
34 // The error returned from CompareHashAndPassword when a hash was created with | 34 // The error returned from CompareHashAndPassword when a hash was created with |
35 // a bcrypt algorithm newer than this implementation. | 35 // a bcrypt algorithm newer than this implementation. |
36 type HashVersionTooNew byte | 36 type HashVersionTooNewError byte |
37 | 37 |
38 func (hv HashVersionTooNew) String() string { | 38 func (hv HashVersionTooNewError) String() string { |
39 return fmt.Sprintf("crypto/bcrypt: bcrypt algorithm version '%c' request ed is newer than current version '%c'", byte(hv), majorVersion) | 39 return fmt.Sprintf("crypto/bcrypt: bcrypt algorithm version '%c' request ed is newer than current version '%c'", byte(hv), majorVersion) |
40 } | 40 } |
41 | 41 |
42 // The error returned from CompareHashAndPassword when a hash starts with someth ing other than '$' | 42 // The error returned from CompareHashAndPassword when a hash starts with someth ing other than '$' |
43 type InvalidHashPrefix byte | 43 type InvalidHashPrefixError byte |
44 | 44 |
45 func (ih InvalidHashPrefix) String() string { | 45 func (ih InvalidHashPrefixError) String() string { |
46 return fmt.Sprintf("crypto/bcrypt: bcrypt hashes must start with '$', bu t hashedSecret started with '%c'", byte(ih)) | 46 return fmt.Sprintf("crypto/bcrypt: bcrypt hashes must start with '$', bu t hashedSecret started with '%c'", byte(ih)) |
47 } | 47 } |
48 | 48 |
49 type InvalidCost int | 49 type InvalidCostError int |
50 | 50 |
51 func (ic InvalidCost) String() string { | 51 func (ic InvalidCostError) String() string { |
52 return fmt.Sprintf("crypto/bcrypt: cost %d is outside allowed range (%d, %d)", int(ic), int(MinCost), int(MaxCost)) | 52 return fmt.Sprintf("crypto/bcrypt: cost %d is outside allowed range (%d, %d)", int(ic), int(MinCost), int(MaxCost)) |
53 } | 53 } |
54 | 54 |
55 const ( | 55 const ( |
56 majorVersion = '2' | 56 majorVersion = '2' |
57 minorVersion = 'a' | 57 minorVersion = 'a' |
58 maxSaltSize = 16 | 58 maxSaltSize = 16 |
59 maxCryptedHashSize = 23 | 59 maxCryptedHashSize = 23 |
60 encodedSaltSize = 22 | 60 encodedSaltSize = 22 |
61 encodedHashSize = 31 | 61 encodedHashSize = 31 |
62 minHashSize = 59 | |
62 ) | 63 ) |
63 | 64 |
64 // magicCipherData is an IV for the 64 Blowfish encryption calls in | 65 // magicCipherData is an IV for the 64 Blowfish encryption calls in |
65 // bcrypt(). It's the string "OrpheanBeholderScryDoubt" in big-endian bytes. | 66 // bcrypt(). It's the string "OrpheanBeholderScryDoubt" in big-endian bytes. |
66 var magicCipherData = []byte{ | 67 var magicCipherData = []byte{ |
67 0x4f, 0x72, 0x70, 0x68, | 68 0x4f, 0x72, 0x70, 0x68, |
68 0x65, 0x61, 0x6e, 0x42, | 69 0x65, 0x61, 0x6e, 0x42, |
69 0x65, 0x68, 0x6f, 0x6c, | 70 0x65, 0x68, 0x6f, 0x6c, |
70 0x64, 0x65, 0x72, 0x53, | 71 0x64, 0x65, 0x72, 0x53, |
71 0x63, 0x72, 0x79, 0x44, | 72 0x63, 0x72, 0x79, 0x44, |
72 0x6f, 0x75, 0x62, 0x74, | 73 0x6f, 0x75, 0x62, 0x74, |
73 } | 74 } |
74 | 75 |
75 type hashed struct { | 76 type hashed struct { |
76 hash []byte | 77 hash []byte |
77 salt []byte | 78 salt []byte |
78 cost uint32 // allowed range is MinCost to MaxCost | 79 cost uint32 // allowed range is MinCost to MaxCost |
79 major byte | 80 major byte |
80 minor byte | 81 minor byte |
81 } | 82 } |
82 | 83 |
83 // GenerateFromPassword returns the bcrypt hash of the password at the given | 84 // GenerateFromPassword returns the bcrypt hash of the password at the given |
84 // cost. If the cost given is less than MinCost, the cost will be set to | 85 // cost. If the cost given is less than MinCost, the cost will be set to |
85 // MinCost, instead. Use Equal, as defined in this package, to compare the | 86 // MinCost, instead. Use CompareHashAndPassword, as defined in this package, |
agl1
2011/09/16 20:43:47
Equal has been renamed, right?
| |
86 // returned hashed password with its cleartext version. | 87 // to compare the returned hashed password with its cleartext version. |
87 func GenerateFromPassword(password []byte, cost int) ([]byte, os.Error) { | 88 func GenerateFromPassword(password []byte, cost int) ([]byte, os.Error) { |
88 p, err := newFromPassword(password, cost) | 89 p, err := newFromPassword(password, cost) |
89 if err != nil { | 90 if err != nil { |
90 return nil, err | 91 return nil, err |
91 } | 92 } |
92 return p.Hash(), nil | 93 return p.Hash(), nil |
93 } | 94 } |
94 | 95 |
95 // CompareHashAndPassword compares a bcrypt hashed password with its possible | 96 // CompareHashAndPassword compares a bcrypt hashed password with its possible |
96 // plaintext equivalent. Note: Using bytes.Equal for this job is | 97 // plaintext equivalent. Note: Using bytes.Equal for this job is |
97 // insecure. Returns nil on success, or an error on failure. | 98 // insecure. Returns nil on success, or an error on failure. |
98 func CompareHashAndPassword(hashedPassword, password []byte) os.Error { | 99 func CompareHashAndPassword(hashedPassword, password []byte) os.Error { |
99 p, err := newFromHash(hashedPassword) | 100 p, err := newFromHash(hashedPassword) |
100 if err != nil { | 101 if err != nil { |
101 return err | 102 return err |
102 } | 103 } |
103 | 104 |
104 otherHash, err := bcrypt(password, p.cost, p.salt) | 105 otherHash, err := bcrypt(password, p.cost, p.salt) |
105 if err != nil { | 106 if err != nil { |
106 return err | 107 return err |
107 } | 108 } |
108 | 109 |
109 otherP := &hashed{otherHash, p.salt, p.cost, p.major, p.minor} | 110 otherP := &hashed{otherHash, p.salt, p.cost, p.major, p.minor} |
110 if subtle.ConstantTimeCompare(p.Hash(), otherP.Hash()) == 1 { | 111 if subtle.ConstantTimeCompare(p.Hash(), otherP.Hash()) == 1 { |
111 return nil | 112 return nil |
112 } | 113 } |
113 | 114 |
114 » return MismatchedHashAndPassword | 115 » return MismatchedHashAndPasswordError |
115 } | 116 } |
116 | 117 |
117 func newFromPassword(password []byte, cost int) (*hashed, os.Error) { | 118 func newFromPassword(password []byte, cost int) (*hashed, os.Error) { |
118 if cost < MinCost { | 119 if cost < MinCost { |
119 cost = DefaultCost | 120 cost = DefaultCost |
120 } | 121 } |
121 p := new(hashed) | 122 p := new(hashed) |
122 p.major = majorVersion | 123 p.major = majorVersion |
123 p.minor = minorVersion | 124 p.minor = minorVersion |
124 | 125 |
125 err := checkCost(cost) | 126 err := checkCost(cost) |
126 if err != nil { | 127 if err != nil { |
127 return nil, err | 128 return nil, err |
128 } | 129 } |
129 p.cost = uint32(cost) | 130 p.cost = uint32(cost) |
130 | 131 |
131 unencodedSalt := make([]byte, maxSaltSize) | 132 unencodedSalt := make([]byte, maxSaltSize) |
132 » io.ReadFull(rand.Reader, unencodedSalt) | 133 » _, err = io.ReadFull(rand.Reader, unencodedSalt) |
134 » if err != nil { | |
135 » » return nil, err | |
136 » } | |
133 | 137 |
134 p.salt = base64Encode(unencodedSalt) | 138 p.salt = base64Encode(unencodedSalt) |
135 hash, err := bcrypt(password, p.cost, p.salt) | 139 hash, err := bcrypt(password, p.cost, p.salt) |
136 if err != nil { | 140 if err != nil { |
137 return nil, err | 141 return nil, err |
138 } | 142 } |
139 p.hash = hash | 143 p.hash = hash |
140 return p, err | 144 return p, err |
141 } | 145 } |
142 | 146 |
143 func newFromHash(hashedSecret []byte) (*hashed, os.Error) { | 147 func newFromHash(hashedSecret []byte) (*hashed, os.Error) { |
148 if len(hashedSecret) < minHashSize { | |
149 return nil, HashTooShortError | |
150 } | |
144 p := new(hashed) | 151 p := new(hashed) |
145 n, err := p.decodeVersion(hashedSecret) | 152 n, err := p.decodeVersion(hashedSecret) |
146 if err != nil { | 153 if err != nil { |
147 return nil, err | 154 return nil, err |
148 } | 155 } |
149 hashedSecret = hashedSecret[n:] | 156 hashedSecret = hashedSecret[n:] |
150 n, err = p.decodeCost(hashedSecret) | 157 n, err = p.decodeCost(hashedSecret) |
151 if err != nil { | 158 if err != nil { |
152 return nil, err | 159 return nil, err |
153 } | 160 } |
154 hashedSecret = hashedSecret[n:] | 161 hashedSecret = hashedSecret[n:] |
155 | |
156 if len(hashedSecret) < encodedSaltSize+encodedHashSize { | |
157 return nil, HashTooShort | |
158 } | |
159 | 162 |
160 // The "+2" is here because we'll have to append at most 2 '=' to the sa lt | 163 // The "+2" is here because we'll have to append at most 2 '=' to the sa lt |
161 // when base64 decoding it in expensiveBlowfishSetup(). | 164 // when base64 decoding it in expensiveBlowfishSetup(). |
162 p.salt = make([]byte, encodedSaltSize, encodedSaltSize+2) | 165 p.salt = make([]byte, encodedSaltSize, encodedSaltSize+2) |
163 copy(p.salt, hashedSecret[:encodedSaltSize]) | 166 copy(p.salt, hashedSecret[:encodedSaltSize]) |
164 | 167 |
165 hashedSecret = hashedSecret[encodedSaltSize:] | 168 hashedSecret = hashedSecret[encodedSaltSize:] |
166 p.hash = make([]byte, len(hashedSecret)) | 169 p.hash = make([]byte, len(hashedSecret)) |
167 copy(p.hash, hashedSecret) | 170 copy(p.hash, hashedSecret) |
168 | 171 |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
232 n += 1 | 235 n += 1 |
233 copy(arr[n:], p.salt) | 236 copy(arr[n:], p.salt) |
234 n += encodedSaltSize | 237 n += encodedSaltSize |
235 copy(arr[n:], p.hash) | 238 copy(arr[n:], p.hash) |
236 n += encodedHashSize | 239 n += encodedHashSize |
237 return arr[:n] | 240 return arr[:n] |
238 } | 241 } |
239 | 242 |
240 func (p *hashed) decodeVersion(sbytes []byte) (int, os.Error) { | 243 func (p *hashed) decodeVersion(sbytes []byte) (int, os.Error) { |
241 if sbytes[0] != '$' { | 244 if sbytes[0] != '$' { |
242 » » return -1, InvalidHashPrefix(sbytes[0]) | 245 » » return -1, InvalidHashPrefixError(sbytes[0]) |
243 } | 246 } |
244 if sbytes[1] > majorVersion { | 247 if sbytes[1] > majorVersion { |
245 » » return -1, HashVersionTooNew(sbytes[1]) | 248 » » return -1, HashVersionTooNewError(sbytes[1]) |
246 } | 249 } |
247 p.major = sbytes[1] | 250 p.major = sbytes[1] |
248 n := 3 | 251 n := 3 |
249 if sbytes[2] != '$' { | 252 if sbytes[2] != '$' { |
250 p.minor = sbytes[2] | 253 p.minor = sbytes[2] |
251 n++ | 254 n++ |
252 } | 255 } |
253 return n, nil | 256 return n, nil |
254 } | 257 } |
255 | 258 |
(...skipping 10 matching lines...) Expand all Loading... | |
266 p.cost = uint32(cost) | 269 p.cost = uint32(cost) |
267 return 3, nil | 270 return 3, nil |
268 } | 271 } |
269 | 272 |
270 func (p *hashed) String() string { | 273 func (p *hashed) String() string { |
271 return fmt.Sprintf("&{hash: %#v, salt: %#v, cost: %d, major: %c, minor: %c}", string(p.hash), p.salt, p.cost, p.major, p.minor) | 274 return fmt.Sprintf("&{hash: %#v, salt: %#v, cost: %d, major: %c, minor: %c}", string(p.hash), p.salt, p.cost, p.major, p.minor) |
272 } | 275 } |
273 | 276 |
274 func checkCost(cost int) os.Error { | 277 func checkCost(cost int) os.Error { |
275 if cost < MinCost || cost > MaxCost { | 278 if cost < MinCost || cost > MaxCost { |
276 » » return InvalidCost(cost) | 279 » » return InvalidCostError(cost) |
277 } | 280 } |
278 return nil | 281 return nil |
279 } | 282 } |
LEFT | RIGHT |