package cryptutil

import (
	"crypto/hmac"
	"crypto/sha512"

	"golang.org/x/crypto/bcrypt"
	"google.golang.org/protobuf/proto"
)

// Hash generates a hash of data using HMAC-SHA-512/256. The tag is intended to
// be a natural-language string describing the purpose of the hash, such as
// "hash file for lookup key" or "master secret to client secret".  It serves
// as an HMAC "key" and ensures that different purposes will have different
// hash output. This function is NOT suitable for hashing passwords.
func Hash(tag string, data []byte) []byte {
	h := hmac.New(sha512.New512_256, []byte(tag))
	h.Write(data)
	return h.Sum(nil)
}

// HashPassword generates a bcrypt hash of the password using work factor 14.
func HashPassword(password []byte) ([]byte, error) {
	return bcrypt.GenerateFromPassword(password, 14)
}

// CheckPasswordHash securely compares a bcrypt hashed password with its possible
// plaintext equivalent.  Returns nil on success, or an error on failure.
func CheckPasswordHash(hash, password []byte) error {
	return bcrypt.CompareHashAndPassword(hash, password)
}

// HashProto hashes a protobuf message. It sets `Deterministic` to true to ensure
// the encoded message is always the same. (ie map order is lexographic)
func HashProto(msg proto.Message) []byte {
	opts := proto.MarshalOptions{
		AllowPartial:  true,
		Deterministic: true,
	}
	bs, _ := opts.Marshal(msg)
	return Hash("proto", bs)
}