func Compare(password, phc []byte) (bool, error) {
	id, version, params, salt, hash, err := phcDecode(string(phc))
	if err != nil {
		return false, errors.Wrap(err, "error decoding hash")
	}
	var hashedPass []byte
	switch id {
	case bcryptHash:
		return (bcrypt.CompareHashAndPassword(hash, password) == nil), nil
	case scryptHash:
		p, err := newScryptParams(params)
		if err != nil {
			return false, err
		}
		hashedPass, err = scrypt.Key(password, salt, p.N, p.r, p.p, len(hash))
		if err != nil {
			return false, errors.Wrap(err, "error deriving input")
		}
	case argon2iHash:
		p, err := newArgon2Params(params)
		if err != nil {
			return false, err
		}
		if version != 0 && version != argon2.Version {
			return false, errors.Errorf("unsupported argon2 version '%d'", version)
		}
		hashedPass = argon2.Key(password, salt, p.t, p.m, p.p, uint32(len(hash)))
	case argon2idHash:
		p, err := newArgon2Params(params)
		if err != nil {
			return false, err
		}
		if version != 0 && version != argon2.Version {
			return false, errors.Errorf("unsupported argon2 version '%d'", version)
		}
		hashedPass = argon2.IDKey(password, salt, p.t, p.m, p.p, uint32(len(hash)))
	default:
		return false, errors.Errorf("invalid or unsupported hash method with id '%s'", id)
	}
	return (subtle.ConstantTimeCompare(hash, hashedPass) == 1), nil
}
Compare compares the password with the given PHC encoded hash, returns true
if they match. The time taken is a function of the length of the slices and
is independent of the contents.
package kdf
import (
	"crypto/subtle"
	"strconv"
	"github.com/pkg/errors"
	"github.com/smallstep/cli/crypto/randutil"
	"golang.org/x/crypto/argon2"
	"golang.org/x/crypto/bcrypt"
	"golang.org/x/crypto/scrypt"
)
type KDF func(password []byte) (string, error)
func Scrypt(password []byte) (string, error) {
	salt, err := randutil.Salt(16)
	if err != nil {
		return "", err
	}
	p := scryptParams[scryptHash32768]
	hash, err := scrypt.Key(password, salt, p.N, p.r, p.p, p.kl)
	if err != nil {
		return "", errors.Wrap(err, "error deriving password")
	}
	return phcEncode("scrypt", p.getParams(), salt, hash), nil
}
func Bcrypt(password []byte) (string, error) {
	hash, err := bcrypt.GenerateFromPassword(password, bcrypt.DefaultCost)
	if err != nil {
		return "", errors.Wrap(err, "error deriving password")
	}
	return string(hash), nil
}
func Argon2i(password []byte) (string, error) {
	salt, err := randutil.Salt(16)
	if err != nil {
		return "", err
	}
	p := argon2Params[argon2iHash]
	hash := argon2.Key(password, salt, p.t, p.m, p.p, p.kl)
	identifier := "argon2i$v=" + strconv.Itoa(argon2.Version)
	return phcEncode(identifier, p.getParams(), salt, hash), nil
}
func Argon2id(password []byte) (string, error) {
	salt, err := randutil.Salt(16)
	if err != nil {
		return "", err
	}
	p := argon2Params[argon2idHash]
	hash := argon2.IDKey(password, salt, p.t, p.m, p.p, p.kl)
	identifier := "argon2id$v=" + strconv.Itoa(argon2.Version)
	return phcEncode(identifier, p.getParams(), salt, hash), nil
}