From c4b50093d9116a9651dae911021cabf9e99f7933 Mon Sep 17 00:00:00 2001 From: Tom Stellard Date: Sep 16 2019 17:08:26 +0000 Subject: Add HashVerifyPKCS1v15 function Also revert the implementation of VerifyPKCS1v15() back to the old behavior of assuming hashed inputs. This was done to support exisiting applications even though it makes the implementation no longer FIPS compliant. Setting the enviroinment variable GOLANG_STRICT_FIPS=1 will cause calls to VerifyPKCS1v15() to panic and can be used to test if an application is complying with FIPS. HashVerifyPKCS1v15 should be used instead of VerifyPKCS1v15 in order to comply with FIPS requirements. --- diff --git a/api/go1.12.txt b/api/go1.12.txt index cb65613..aad9147 100644 --- a/api/go1.12.txt +++ b/api/go1.12.txt @@ -1,6 +1,7 @@ pkg bytes, func ReplaceAll([]uint8, []uint8, []uint8) []uint8 pkg crypto/ecdsa, func HashSign(io.Reader, *PrivateKey, []uint8, crypto.Hash) (*big.Int, *big.Int, error) pkg crypto/ecdsa, func HashVerify(*PublicKey, []uint8, *big.Int, *big.Int, crypto.Hash) bool +pkg crypto/rsa, func HashVerifyPKCS1v15(*PublicKey, crypto.Hash, []uint8, []uint8) error pkg crypto/tls, const TLS_AES_128_GCM_SHA256 = 4865 pkg crypto/tls, const TLS_AES_128_GCM_SHA256 uint16 pkg crypto/tls, const TLS_AES_256_GCM_SHA384 = 4866 diff --git a/src/crypto/internal/boring/boring.go b/src/crypto/internal/boring/boring.go index 2799d9e..e46eb06 100644 --- a/src/crypto/internal/boring/boring.go +++ b/src/crypto/internal/boring/boring.go @@ -29,6 +29,13 @@ const ( // Enabled controls whether FIPS crypto is enabled. var enabled = false +// When this variable is true, the go crypto API will panic when a caller +// tries to use the API in a non-compliant manner. When this is false, the +// go crytpo API will allow existing go crypto APIs to be used even +// if they aren't FIPS compliant. However, all the unerlying crypto operations +// will still be done by OpenSSL. +var strictFIPS = false + func init() { runtime.LockOSThread() defer runtime.UnlockOSThread() @@ -110,6 +117,14 @@ func UnreachableExceptTests() { } } +func PanicIfStrictFIPS(msg string) { + if os.Getenv("GOLANG_STRICT_FIPS") == "1" || strictFIPS { + panic(msg) + } + print("Warning: Operation not allowed in FIPS mode: ") + println(msg) +} + type fail string func (e fail) Error() string { return "boringcrypto: " + string(e) + " failed" } diff --git a/src/crypto/internal/boring/notboring.go b/src/crypto/internal/boring/notboring.go index 6eb015b..2d72ae0 100644 --- a/src/crypto/internal/boring/notboring.go +++ b/src/crypto/internal/boring/notboring.go @@ -29,6 +29,9 @@ func Unreachable() { // when BoringCrypto is in use. It is a no-op without BoringCrypto. func UnreachableExceptTests() {} +// This is a noop withotu BoringCrytpo. +func PanicIfStrictFIPS(v interface{}) {} + type randReader int func (randReader) Read(b []byte) (int, error) { panic("boringcrypto: not available") } diff --git a/src/crypto/internal/boring/rsa.go b/src/crypto/internal/boring/rsa.go index 8966465..20f66ed 100644 --- a/src/crypto/internal/boring/rsa.go +++ b/src/crypto/internal/boring/rsa.go @@ -303,7 +303,7 @@ func SignRSAPKCS1v15(priv *PrivateKeyRSA, h crypto.Hash, msg []byte) ([]byte, er return out[:outLen], nil } -func VerifyRSAPKCS1v15(pub *PublicKeyRSA, h crypto.Hash, msg, sig []byte) error { +func VerifyRSAPKCS1v15(pub *PublicKeyRSA, h crypto.Hash, msg, sig []byte, msgIsHashed bool) error { size := int(C._goboringcrypto_RSA_size(pub.key)) if len(sig) < size { // BoringCrypto requires sig to be same size as RSA key, so pad with leading zeros. @@ -316,6 +316,17 @@ func VerifyRSAPKCS1v15(pub *PublicKeyRSA, h crypto.Hash, msg, sig []byte) error if md == nil { return errors.New("crypto/rsa: unsupported hash function") } + + if msgIsHashed { + PanicIfStrictFIPS("You must provide a raw unhashed message for PKCS1v15 verification and use HashVerifyPKCS1v15 instead of VerifyPKCS1v15") + nid := C._goboringcrypto_EVP_MD_type(md) + if C._goboringcrypto_RSA_verify(nid, base(msg), C.size_t(len(msg)), base(sig), C.size_t(len(sig)), pub.key) == 0 { + return fail("RSA_verify") + } + runtime.KeepAlive(pub) + return nil + } + if C._goboringcrypto_EVP_RSA_verify(md, base(msg), C.size_t(len(msg)), base(sig), C.size_t(len(sig)), pub.key) == 0 { return fail("RSA_verify") } diff --git a/src/crypto/rsa/pkcs1v15.go b/src/crypto/rsa/pkcs1v15.go index 26aac57..7db14c9 100644 --- a/src/crypto/rsa/pkcs1v15.go +++ b/src/crypto/rsa/pkcs1v15.go @@ -323,7 +323,7 @@ func VerifyPKCS1v15(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte) if err != nil { return err } - if err := boring.VerifyRSAPKCS1v15(bkey, hash, hashed, sig); err != nil { + if err := boring.VerifyRSAPKCS1v15(bkey, hash, hashed, sig, true); err != nil { return ErrVerification } return nil @@ -362,6 +362,25 @@ func VerifyPKCS1v15(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte) return nil } +func HashVerifyPKCS1v15(pub *PublicKey, hash crypto.Hash, msg []byte, sig []byte) error { + if boring.Enabled() { + bkey, err := boringPublicKey(pub) + if err != nil { + return err + } + if err := boring.VerifyRSAPKCS1v15(bkey, hash, msg, sig, false); err != nil { + return ErrVerification + } + return nil + } + + boring.UnreachableExceptTests() + h := hash.New() + h.Write(msg) + d := h.Sum(nil) + return VerifyPKCS1v15(pub, hash, d, sig) +} + func pkcs1v15HashInfo(hash crypto.Hash, inLen int) (hashLen int, prefix []byte, err error) { // Special case: crypto.Hash(0) is used to indicate that the data is // signed directly. diff --git a/src/crypto/tls/auth.go b/src/crypto/tls/auth.go index 6ada06a..22f4381 100644 --- a/src/crypto/tls/auth.go +++ b/src/crypto/tls/auth.go @@ -98,8 +98,14 @@ func verifyHandshakeSignature(sigType uint8, pubkey crypto.PublicKey, hashFunc c if !ok { return errors.New("tls: RSA signing requires a RSA public key") } - if err := rsa.VerifyPKCS1v15(pubKey, hashFunc, digest, sig); err != nil { - return err + if boring.Enabled() { + if err := rsa.HashVerifyPKCS1v15(pubKey, hashFunc, digest, sig); err != nil { + return err + } + } else { + if err := rsa.VerifyPKCS1v15(pubKey, hashFunc, digest, sig); err != nil { + return err + } } case signatureRSAPSS: pubKey, ok := pubkey.(*rsa.PublicKey) diff --git a/src/crypto/x509/x509.go b/src/crypto/x509/x509.go index ebc11a1..ea0e0f2 100644 --- a/src/crypto/x509/x509.go +++ b/src/crypto/x509/x509.go @@ -898,7 +898,7 @@ func checkSignature(algo SignatureAlgorithm, signed, signature []byte, publicKey } } else { if boring.Enabled() { - return rsa.VerifyPKCS1v15(pub, hashType, signed, signature) + return rsa.HashVerifyPKCS1v15(pub, hashType, signed, signature) } else { return rsa.VerifyPKCS1v15(pub, hashType, digest, signature) }