crypto: return an error if a hash function is not available

Calling New otherwise panics, which is unnecessary, especially in
crypto.SignMessage and especially if we add a new not-implemented hash
value for ML-DSA external Mu.

Change-Id: I9f8d29d01e126838d7d2585133e562536a6a6964
Reviewed-on: https://go-review.googlesource.com/c/go/+/745660
Reviewed-by: Roland Shoemaker <roland@golang.org>
Reviewed-by: Daniel McCarney <daniel@binaryparadox.net>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Filippo Valsorda <filippo@golang.org>
This commit is contained in:
Filippo Valsorda 2026-02-12 11:43:15 +01:00 committed by Gopher Robot
parent 3825609217
commit 6de59e2070
7 changed files with 46 additions and 6 deletions

View file

@ -246,14 +246,23 @@ func SignMessage(signer Signer, rand io.Reader, msg []byte, opts SignerOpts) (si
if ms, ok := signer.(MessageSigner); ok {
return ms.SignMessage(rand, msg, opts)
}
if opts.HashFunc() != 0 {
h := opts.HashFunc().New()
if hash := opts.HashFunc(); hash != 0 {
if !hash.Available() {
return nil, hashUnavailableError(hash)
}
h := hash.New()
h.Write(msg)
msg = h.Sum(nil)
}
return signer.Sign(rand, msg, opts)
}
type hashUnavailableError Hash
func (h hashUnavailableError) Error() string {
return "crypto: requested hash function unavailable: " + Hash(h).String()
}
// Decapsulator is an interface for an opaque private KEM key that can be used for
// decapsulation operations. For example, an ML-KEM key kept in a hardware module.
//

View file

@ -450,6 +450,9 @@ func signFIPSDeterministic[P ecdsa.Point[P]](c *ecdsa.Curve[P], hashFunc crypto.
if err != nil {
return nil, err
}
if !hashFunc.Available() {
return nil, errors.New("ecdsa: requested hash function unavailable: " + hashFunc.String())
}
h := fips140hash.UnwrapNew(hashFunc.New)
if fips140only.Enforced() && !fips140only.ApprovedHash(h()) {
return nil, errors.New("crypto/ecdsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode")

View file

@ -247,10 +247,12 @@ bXVL8iKLrG91IYQByUHZIn3WVAd2bfi4MfKagRt0ggd4
expectErr(t, errRet2(rsa.SignPKCS1v15(rand.Reader, rsaKey, crypto.SHA1, make([]byte, 20))))
// rand is always ignored for PKCS1v15 signing
expectNoErr(t, errRet2(rsa.SignPKCS1v15(readerWrap{rand.Reader}, rsaKey, crypto.SHA256, make([]byte, 32))))
expectErr(t, errRet2(rsa.SignPKCS1v15(rand.Reader, rsaKey, crypto.Hash(0), make([]byte, 32))))
expectNoErr(t, rsa.VerifyPKCS1v15(&rsaKey.PublicKey, crypto.SHA256, make([]byte, 32), sigPKCS1v15))
expectErr(t, rsa.VerifyPKCS1v15(&smallKey.PublicKey, crypto.SHA256, make([]byte, 32), sigPKCS1v15))
expectErr(t, rsa.VerifyPKCS1v15(&rsaKey.PublicKey, crypto.SHA1, make([]byte, 20), sigPKCS1v15))
expectErr(t, rsa.VerifyPKCS1v15(&rsaKey.PublicKey, crypto.Hash(0), make([]byte, 32), sigPKCS1v15))
sigPSS, err := rsa.SignPSS(rand.Reader, rsaKey, crypto.SHA256, make([]byte, 32), nil)
expectNoErr(t, err)

View file

@ -80,6 +80,9 @@ func SignPSS(random io.Reader, priv *PrivateKey, hash crypto.Hash, digest []byte
}
boring.UnreachableExceptTests()
if !hash.Available() {
return nil, errors.New("crypto/rsa: requested hash function unavailable: " + hash.String())
}
h := fips140hash.Unwrap(hash.New())
if err := checkFIPS140OnlyPrivateKey(priv); err != nil {
@ -145,6 +148,9 @@ func VerifyPSS(pub *PublicKey, hash crypto.Hash, digest []byte, sig []byte, opts
return nil
}
if !hash.Available() {
return errors.New("crypto/rsa: requested hash function unavailable: " + hash.String())
}
h := fips140hash.Unwrap(hash.New())
if err := checkFIPS140OnlyPublicKey(pub); err != nil {
@ -203,6 +209,12 @@ func EncryptOAEP(hash hash.Hash, random io.Reader, pub *PublicKey, msg []byte, l
//
// See [EncryptOAEP] for additional details.
func EncryptOAEPWithOptions(random io.Reader, pub *PublicKey, msg []byte, opts *OAEPOptions) ([]byte, error) {
if !opts.Hash.Available() {
return nil, errors.New("crypto/rsa: requested hash function unavailable: " + opts.Hash.String())
}
if opts.MGFHash != 0 && !opts.MGFHash.Available() {
return nil, errors.New("crypto/rsa: requested hash function unavailable: " + opts.MGFHash.String())
}
if opts.MGFHash == 0 {
return encryptOAEP(opts.Hash.New(), opts.Hash.New(), random, pub, msg, opts.Label)
}
@ -342,8 +354,10 @@ func SignPKCS1v15(random io.Reader, priv *PrivateKey, hash crypto.Hash, hashed [
if err := checkFIPS140OnlyPrivateKey(priv); err != nil {
return nil, err
}
if fips140only.Enforced() && !fips140only.ApprovedHash(fips140hash.Unwrap(hash.New())) {
return nil, errors.New("crypto/rsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode")
if fips140only.Enforced() {
if !hash.Available() || !fips140only.ApprovedHash(fips140hash.Unwrap(hash.New())) {
return nil, errors.New("crypto/rsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode")
}
}
k, err := fipsPrivateKey(priv)
@ -388,8 +402,10 @@ func VerifyPKCS1v15(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte)
if err := checkFIPS140OnlyPublicKey(pub); err != nil {
return err
}
if fips140only.Enforced() && !fips140only.ApprovedHash(fips140hash.Unwrap(hash.New())) {
return errors.New("crypto/rsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode")
if fips140only.Enforced() {
if !hash.Available() || !fips140only.ApprovedHash(fips140hash.Unwrap(hash.New())) {
return errors.New("crypto/rsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode")
}
}
k, err := fipsPublicKey(pub)

View file

@ -176,6 +176,12 @@ func (priv *PrivateKey) Decrypt(rand io.Reader, ciphertext []byte, opts crypto.D
switch opts := opts.(type) {
case *OAEPOptions:
if !opts.Hash.Available() {
return nil, errors.New("rsa: requested hash function unavailable: " + opts.Hash.String())
}
if opts.MGFHash != 0 && !opts.MGFHash.Available() {
return nil, errors.New("rsa: requested hash function unavailable: " + opts.MGFHash.String())
}
if opts.MGFHash == 0 {
return decryptOAEP(opts.Hash.New(), opts.Hash.New(), priv, ciphertext, opts.Label)
} else {

View file

@ -21,6 +21,9 @@ import (
// verifyHandshakeSignature verifies a signature against unhashed handshake contents.
func verifyHandshakeSignature(sigType uint8, pubkey crypto.PublicKey, hashFunc crypto.Hash, signed, sig []byte) error {
if hashFunc != directSigning {
if !hashFunc.Available() {
return fmt.Errorf("hash function unavailable: %v", hashFunc)
}
h := hashFunc.New()
h.Write(signed)
signed = h.Sum(nil)

View file

@ -16,6 +16,7 @@ import (
"crypto/rc4"
"crypto/sha1"
"crypto/sha256"
_ "crypto/sha512" // for crypto.SHA384
"fmt"
"hash"
"internal/cpu"