github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/security/password.go (about)

     1  // Copyright 2015 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  package security
    12  
    13  import (
    14  	"crypto/sha256"
    15  	"fmt"
    16  	"os"
    17  
    18  	"github.com/cockroachdb/errors"
    19  	"golang.org/x/crypto/bcrypt"
    20  	"golang.org/x/crypto/ssh/terminal"
    21  )
    22  
    23  // BcryptCost is the cost to use when hashing passwords. It is exposed for
    24  // testing.
    25  //
    26  // BcryptCost should increase along with computation power.
    27  // For estimates, see: http://security.stackexchange.com/questions/17207/recommended-of-rounds-for-bcrypt
    28  // For now, we use the library's default cost.
    29  var BcryptCost = bcrypt.DefaultCost
    30  
    31  // ErrEmptyPassword indicates that an empty password was attempted to be set.
    32  var ErrEmptyPassword = errors.New("empty passwords are not permitted")
    33  
    34  var sha256NewSum = sha256.New().Sum(nil)
    35  
    36  // TODO(mjibson): properly apply SHA-256 to the password. The current code
    37  // erroneously appends the SHA-256 of the empty hash to the unhashed password
    38  // instead of actually hashing the password. Fixing this requires a somewhat
    39  // complicated backwards compatibility dance. This is not a security issue
    40  // because the round of SHA-256 was only intended to achieve a fixed-length
    41  // input to bcrypt; it is bcrypt that provides the cryptographic security, and
    42  // bcrypt is correctly applied.
    43  func appendEmptySha256(password string) []byte {
    44  	// In the past we incorrectly called the hash.Hash.Sum method. That
    45  	// method uses its argument as a place to put the current hash:
    46  	// it does not add its argument to the current hash. Thus, using
    47  	// h.Sum([]byte(password))) is the equivalent to the below append.
    48  	return append([]byte(password), sha256NewSum...)
    49  }
    50  
    51  // CompareHashAndPassword tests that the provided bytes are equivalent to the
    52  // hash of the supplied password. If they are not equivalent, returns an
    53  // error.
    54  func CompareHashAndPassword(hashedPassword []byte, password string) error {
    55  	return bcrypt.CompareHashAndPassword(hashedPassword, appendEmptySha256(password))
    56  }
    57  
    58  // HashPassword takes a raw password and returns a bcrypt hashed password.
    59  func HashPassword(password string) ([]byte, error) {
    60  	return bcrypt.GenerateFromPassword(appendEmptySha256(password), BcryptCost)
    61  }
    62  
    63  // PromptForPassword prompts for a password.
    64  // This is meant to be used when using a password.
    65  func PromptForPassword() (string, error) {
    66  	fmt.Print("Enter password: ")
    67  	password, err := terminal.ReadPassword(int(os.Stdin.Fd()))
    68  	if err != nil {
    69  		return "", err
    70  	}
    71  	// Make sure stdout moves on to the next line.
    72  	fmt.Print("\n")
    73  
    74  	return string(password), nil
    75  }