github.com/mdaxf/iac@v0.0.0-20240519030858-58a061660378/vendor_skip/golang.org/x/crypto/bcrypt/bcrypt.go (about)

     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
     7  package bcrypt // import "golang.org/x/crypto/bcrypt"
     8  
     9  // The code is a port of Provos and Mazières's C implementation.
    10  import (
    11  	"crypto/rand"
    12  	"crypto/subtle"
    13  	"errors"
    14  	"fmt"
    15  	"io"
    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), MinCost, MaxCost)
    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  // ErrPasswordTooLong is returned when the password passed to
    86  // GenerateFromPassword is too long (i.e. > 72 bytes).
    87  var ErrPasswordTooLong = errors.New("bcrypt: password length exceeds 72 bytes")
    88  
    89  // GenerateFromPassword returns the bcrypt hash of the password at the given
    90  // cost. If the cost given is less than MinCost, the cost will be set to
    91  // DefaultCost, instead. Use CompareHashAndPassword, as defined in this package,
    92  // to compare the returned hashed password with its cleartext version.
    93  // GenerateFromPassword does not accept passwords longer than 72 bytes, which
    94  // is the longest password bcrypt will operate on.
    95  func GenerateFromPassword(password []byte, cost int) ([]byte, error) {
    96  	if len(password) > 72 {
    97  		return nil, ErrPasswordTooLong
    98  	}
    99  	p, err := newFromPassword(password, cost)
   100  	if err != nil {
   101  		return nil, err
   102  	}
   103  	return p.Hash(), nil
   104  }
   105  
   106  // CompareHashAndPassword compares a bcrypt hashed password with its possible
   107  // plaintext equivalent. Returns nil on success, or an error on failure.
   108  func CompareHashAndPassword(hashedPassword, password []byte) error {
   109  	p, err := newFromHash(hashedPassword)
   110  	if err != nil {
   111  		return err
   112  	}
   113  
   114  	otherHash, err := bcrypt(password, p.cost, p.salt)
   115  	if err != nil {
   116  		return err
   117  	}
   118  
   119  	otherP := &hashed{otherHash, p.salt, p.cost, p.major, p.minor}
   120  	if subtle.ConstantTimeCompare(p.Hash(), otherP.Hash()) == 1 {
   121  		return nil
   122  	}
   123  
   124  	return ErrMismatchedHashAndPassword
   125  }
   126  
   127  // Cost returns the hashing cost used to create the given hashed
   128  // password. When, in the future, the hashing cost of a password system needs
   129  // to be increased in order to adjust for greater computational power, this
   130  // function allows one to establish which passwords need to be updated.
   131  func Cost(hashedPassword []byte) (int, error) {
   132  	p, err := newFromHash(hashedPassword)
   133  	if err != nil {
   134  		return 0, err
   135  	}
   136  	return p.cost, nil
   137  }
   138  
   139  func newFromPassword(password []byte, cost int) (*hashed, error) {
   140  	if cost < MinCost {
   141  		cost = DefaultCost
   142  	}
   143  	p := new(hashed)
   144  	p.major = majorVersion
   145  	p.minor = minorVersion
   146  
   147  	err := checkCost(cost)
   148  	if err != nil {
   149  		return nil, err
   150  	}
   151  	p.cost = cost
   152  
   153  	unencodedSalt := make([]byte, maxSaltSize)
   154  	_, err = io.ReadFull(rand.Reader, unencodedSalt)
   155  	if err != nil {
   156  		return nil, err
   157  	}
   158  
   159  	p.salt = base64Encode(unencodedSalt)
   160  	hash, err := bcrypt(password, p.cost, p.salt)
   161  	if err != nil {
   162  		return nil, err
   163  	}
   164  	p.hash = hash
   165  	return p, err
   166  }
   167  
   168  func newFromHash(hashedSecret []byte) (*hashed, error) {
   169  	if len(hashedSecret) < minHashSize {
   170  		return nil, ErrHashTooShort
   171  	}
   172  	p := new(hashed)
   173  	n, err := p.decodeVersion(hashedSecret)
   174  	if err != nil {
   175  		return nil, err
   176  	}
   177  	hashedSecret = hashedSecret[n:]
   178  	n, err = p.decodeCost(hashedSecret)
   179  	if err != nil {
   180  		return nil, err
   181  	}
   182  	hashedSecret = hashedSecret[n:]
   183  
   184  	// The "+2" is here because we'll have to append at most 2 '=' to the salt
   185  	// when base64 decoding it in expensiveBlowfishSetup().
   186  	p.salt = make([]byte, encodedSaltSize, encodedSaltSize+2)
   187  	copy(p.salt, hashedSecret[:encodedSaltSize])
   188  
   189  	hashedSecret = hashedSecret[encodedSaltSize:]
   190  	p.hash = make([]byte, len(hashedSecret))
   191  	copy(p.hash, hashedSecret)
   192  
   193  	return p, nil
   194  }
   195  
   196  func bcrypt(password []byte, cost int, salt []byte) ([]byte, error) {
   197  	cipherData := make([]byte, len(magicCipherData))
   198  	copy(cipherData, magicCipherData)
   199  
   200  	c, err := expensiveBlowfishSetup(password, uint32(cost), salt)
   201  	if err != nil {
   202  		return nil, err
   203  	}
   204  
   205  	for i := 0; i < 24; i += 8 {
   206  		for j := 0; j < 64; j++ {
   207  			c.Encrypt(cipherData[i:i+8], cipherData[i:i+8])
   208  		}
   209  	}
   210  
   211  	// Bug compatibility with C bcrypt implementations. We only encode 23 of
   212  	// the 24 bytes encrypted.
   213  	hsh := base64Encode(cipherData[:maxCryptedHashSize])
   214  	return hsh, nil
   215  }
   216  
   217  func expensiveBlowfishSetup(key []byte, cost uint32, salt []byte) (*blowfish.Cipher, error) {
   218  	csalt, err := base64Decode(salt)
   219  	if err != nil {
   220  		return nil, err
   221  	}
   222  
   223  	// Bug compatibility with C bcrypt implementations. They use the trailing
   224  	// NULL in the key string during expansion.
   225  	// We copy the key to prevent changing the underlying array.
   226  	ckey := append(key[:len(key):len(key)], 0)
   227  
   228  	c, err := blowfish.NewSaltedCipher(ckey, csalt)
   229  	if err != nil {
   230  		return nil, err
   231  	}
   232  
   233  	var i, rounds uint64
   234  	rounds = 1 << cost
   235  	for i = 0; i < rounds; i++ {
   236  		blowfish.ExpandKey(ckey, c)
   237  		blowfish.ExpandKey(csalt, c)
   238  	}
   239  
   240  	return c, nil
   241  }
   242  
   243  func (p *hashed) Hash() []byte {
   244  	arr := make([]byte, 60)
   245  	arr[0] = '$'
   246  	arr[1] = p.major
   247  	n := 2
   248  	if p.minor != 0 {
   249  		arr[2] = p.minor
   250  		n = 3
   251  	}
   252  	arr[n] = '$'
   253  	n++
   254  	copy(arr[n:], []byte(fmt.Sprintf("%02d", p.cost)))
   255  	n += 2
   256  	arr[n] = '$'
   257  	n++
   258  	copy(arr[n:], p.salt)
   259  	n += encodedSaltSize
   260  	copy(arr[n:], p.hash)
   261  	n += encodedHashSize
   262  	return arr[:n]
   263  }
   264  
   265  func (p *hashed) decodeVersion(sbytes []byte) (int, error) {
   266  	if sbytes[0] != '$' {
   267  		return -1, InvalidHashPrefixError(sbytes[0])
   268  	}
   269  	if sbytes[1] > majorVersion {
   270  		return -1, HashVersionTooNewError(sbytes[1])
   271  	}
   272  	p.major = sbytes[1]
   273  	n := 3
   274  	if sbytes[2] != '$' {
   275  		p.minor = sbytes[2]
   276  		n++
   277  	}
   278  	return n, nil
   279  }
   280  
   281  // sbytes should begin where decodeVersion left off.
   282  func (p *hashed) decodeCost(sbytes []byte) (int, error) {
   283  	cost, err := strconv.Atoi(string(sbytes[0:2]))
   284  	if err != nil {
   285  		return -1, err
   286  	}
   287  	err = checkCost(cost)
   288  	if err != nil {
   289  		return -1, err
   290  	}
   291  	p.cost = cost
   292  	return 3, nil
   293  }
   294  
   295  func (p *hashed) String() string {
   296  	return fmt.Sprintf("&{hash: %#v, salt: %#v, cost: %d, major: %c, minor: %c}", string(p.hash), p.salt, p.cost, p.major, p.minor)
   297  }
   298  
   299  func checkCost(cost int) error {
   300  	if cost < MinCost || cost > MaxCost {
   301  		return InvalidCostError(cost)
   302  	}
   303  	return nil
   304  }