OSDN Git Service

new repo
[bytom/vapor.git] / vendor / github.com / tendermint / go-crypto / bcrypt / bcrypt.go
1 package bcrypt
2
3 // MODIFIED BY TENDERMINT TO EXPOSE NONCE
4 // Copyright 2011 The Go Authors. All rights reserved.
5 // Use of this source code is governed by a BSD-style
6 // license that can be found in the LICENSE file.
7
8 // Package bcrypt implements Provos and Mazières's bcrypt adaptive hashing
9 // algorithm. See http://www.usenix.org/event/usenix99/provos/provos.pdf
10
11 // The code is a port of Provos and Mazières's C implementation.
12 import (
13         "crypto/subtle"
14         "errors"
15         "fmt"
16         "strconv"
17
18         "golang.org/x/crypto/blowfish"
19 )
20
21 const (
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.
29 var 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.
33 var 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.
37 type HashVersionTooNewError byte
38
39 func (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 '$'
44 type InvalidHashPrefixError byte
45
46 func (ih InvalidHashPrefixError) Error() string {
47         return fmt.Sprintf("crypto/bcrypt: bcrypt hashes must start with '$', but hashedSecret started with '%c'", byte(ih))
48 }
49
50 type InvalidCostError int
51
52 func (ic InvalidCostError) Error() string {
53         return fmt.Sprintf("crypto/bcrypt: cost %d is outside allowed range (%d,%d)", int(ic), int(MinCost), int(MaxCost)) // nolint: unconvert
54 }
55
56 const (
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.
68 var 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
77 type 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.
89 func GenerateFromPassword(salt []byte, password []byte, cost int) ([]byte, error) {
90         if len(salt) != maxSaltSize {
91                 return nil, fmt.Errorf("Salt len must be %v", maxSaltSize)
92         }
93         p, err := newFromPassword(salt, password, cost)
94         if err != nil {
95                 return nil, err
96         }
97         return p.Hash(), nil
98 }
99
100 // CompareHashAndPassword compares a bcrypt hashed password with its possible
101 // plaintext equivalent. Returns nil on success, or an error on failure.
102 func CompareHashAndPassword(hashedPassword, password []byte) error {
103         p, err := newFromHash(hashedPassword)
104         if err != nil {
105                 return err
106         }
107
108         otherHash, err := bcrypt(password, p.cost, p.salt)
109         if err != nil {
110                 return err
111         }
112
113         otherP := &hashed{otherHash, p.salt, p.cost, p.major, p.minor}
114         if subtle.ConstantTimeCompare(p.Hash(), otherP.Hash()) == 1 {
115                 return nil
116         }
117
118         return ErrMismatchedHashAndPassword
119 }
120
121 // Cost returns the hashing cost used to create the given hashed
122 // password. When, in the future, the hashing cost of a password system needs
123 // to be increased in order to adjust for greater computational power, this
124 // function allows one to establish which passwords need to be updated.
125 func Cost(hashedPassword []byte) (int, error) {
126         p, err := newFromHash(hashedPassword)
127         if err != nil {
128                 return 0, err
129         }
130         return p.cost, nil
131 }
132
133 func newFromPassword(salt []byte, password []byte, cost int) (*hashed, error) {
134         if cost < MinCost {
135                 cost = DefaultCost
136         }
137         p := new(hashed)
138         p.major = majorVersion
139         p.minor = minorVersion
140
141         err := checkCost(cost)
142         if err != nil {
143                 return nil, err
144         }
145         p.cost = cost
146
147         p.salt = base64Encode(salt)
148         hash, err := bcrypt(password, p.cost, p.salt)
149         if err != nil {
150                 return nil, err
151         }
152         p.hash = hash
153         return p, err
154 }
155
156 func newFromHash(hashedSecret []byte) (*hashed, error) {
157         if len(hashedSecret) < minHashSize {
158                 return nil, ErrHashTooShort
159         }
160         p := new(hashed)
161         n, err := p.decodeVersion(hashedSecret)
162         if err != nil {
163                 return nil, err
164         }
165         hashedSecret = hashedSecret[n:]
166         n, err = p.decodeCost(hashedSecret)
167         if err != nil {
168                 return nil, err
169         }
170         hashedSecret = hashedSecret[n:]
171
172         // The "+2" is here because we'll have to append at most 2 '=' to the salt
173         // when base64 decoding it in expensiveBlowfishSetup().
174         p.salt = make([]byte, encodedSaltSize, encodedSaltSize+2)
175         copy(p.salt, hashedSecret[:encodedSaltSize])
176
177         hashedSecret = hashedSecret[encodedSaltSize:]
178         p.hash = make([]byte, len(hashedSecret))
179         copy(p.hash, hashedSecret)
180
181         return p, nil
182 }
183
184 func bcrypt(password []byte, cost int, salt []byte) ([]byte, error) {
185         cipherData := make([]byte, len(magicCipherData))
186         copy(cipherData, magicCipherData)
187
188         c, err := expensiveBlowfishSetup(password, uint32(cost), salt)
189         if err != nil {
190                 return nil, err
191         }
192
193         for i := 0; i < 24; i += 8 {
194                 for j := 0; j < 64; j++ {
195                         c.Encrypt(cipherData[i:i+8], cipherData[i:i+8])
196                 }
197         }
198
199         // Bug compatibility with C bcrypt implementations. We only encode 23 of
200         // the 24 bytes encrypted.
201         hsh := base64Encode(cipherData[:maxCryptedHashSize])
202         return hsh, nil
203 }
204
205 func expensiveBlowfishSetup(key []byte, cost uint32, salt []byte) (*blowfish.Cipher, error) {
206
207         csalt, err := base64Decode(salt)
208         if err != nil {
209                 return nil, err
210         }
211
212         // Bug compatibility with C bcrypt implementations. They use the trailing
213         // NULL in the key string during expansion.
214         ckey := append(key, 0)
215
216         c, err := blowfish.NewSaltedCipher(ckey, csalt)
217         if err != nil {
218                 return nil, err
219         }
220
221         var i, rounds uint64
222         rounds = 1 << cost
223         for i = 0; i < rounds; i++ {
224                 blowfish.ExpandKey(ckey, c)
225                 blowfish.ExpandKey(csalt, c)
226         }
227
228         return c, nil
229 }
230
231 func (p *hashed) Hash() []byte {
232         arr := make([]byte, 60)
233         arr[0] = '$'
234         arr[1] = p.major
235         n := 2
236         if p.minor != 0 {
237                 arr[2] = p.minor
238                 n = 3
239         }
240         arr[n] = '$'
241         n += 1
242         copy(arr[n:], []byte(fmt.Sprintf("%02d", p.cost)))
243         n += 2
244         arr[n] = '$'
245         n += 1
246         copy(arr[n:], p.salt)
247         n += encodedSaltSize
248         copy(arr[n:], p.hash)
249         n += encodedHashSize
250         return arr[:n]
251 }
252
253 func (p *hashed) decodeVersion(sbytes []byte) (int, error) {
254         if sbytes[0] != '$' {
255                 return -1, InvalidHashPrefixError(sbytes[0])
256         }
257         if sbytes[1] > majorVersion {
258                 return -1, HashVersionTooNewError(sbytes[1])
259         }
260         p.major = sbytes[1]
261         n := 3
262         if sbytes[2] != '$' {
263                 p.minor = sbytes[2]
264                 n++
265         }
266         return n, nil
267 }
268
269 // sbytes should begin where decodeVersion left off.
270 func (p *hashed) decodeCost(sbytes []byte) (int, error) {
271         cost, err := strconv.Atoi(string(sbytes[0:2]))
272         if err != nil {
273                 return -1, err
274         }
275         err = checkCost(cost)
276         if err != nil {
277                 return -1, err
278         }
279         p.cost = cost
280         return 3, nil
281 }
282
283 func (p *hashed) String() string {
284         return fmt.Sprintf("&{hash: %#v, salt: %#v, cost: %d, major: %c, minor: %c}", string(p.hash), p.salt, p.cost, p.major, p.minor)
285 }
286
287 func checkCost(cost int) error {
288         if cost < MinCost || cost > MaxCost {
289                 return InvalidCostError(cost)
290         }
291         return nil
292 }