bcrypt.go 8.1 KB

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