From 8e7e87adb816988af42352b54cbdab69c8b2e08b Mon Sep 17 00:00:00 2001 From: Daniel Bleichenbacher Date: Mon, 6 Aug 2018 02:41:25 -0700 Subject: [PATCH] Adding a test for faulty RSA signature generation. Instead of inducing a fault (I don't know how to do this), the test uses faulty CRT parameters. Results: All Java providers pass. OpenJdk: Generating PKCS#1 signature with faulty key throws:java.security.SignatureException: Could not sign data BouncyCastle: Generating PKCS#1 signature with faulty key throws:java.security.SignatureException: java.lang.IllegalStateException: RSA engine faulty decryption/signing detected Conscrypt: Generating PKCS#1 signature with faulty key throws:java.security.SignatureException: java.lang.RuntimeException: error:04000044:RSA routines:OPENSSL_internal:internal error PiperOrigin-RevId: 207514657 GitOrigin-RevId: ced32b741745a87fb13672eeb2e0ef17ab778d78 --- doc/bugs.md | 8 -- .../testcases/RsaSignatureTest.java | 107 +++++++++++++++++- 2 files changed, 104 insertions(+), 11 deletions(-) diff --git a/doc/bugs.md b/doc/bugs.md index 1416b6e..6d9e2c8 100644 --- a/doc/bugs.md +++ b/doc/bugs.md @@ -62,12 +62,4 @@ See [list of issues](issues.md) for details. | CBC-HMAC is vulnerable to padding oracle attack | Quan Nguyen | N/A | https://goo.gl/ACZQeI | To be released | CBC-HMAC integer overflow, HMAC bypass | Quan Nguyen | N/A | https://goo.gl/ACZQeI | To be released -## Package OpenSSL -| Summary | Credits | CVE | Upstream Acknowledgement | Tests | -|:---------------------------: |:--------------------------:|:-----------------:|:--------------------------------------: |:---------------------------------------: -| X25519 incorrect carry handling | Alex Gaynor and Paul Kehrer | N/A | https://github.com/openssl/openssl/issues/6687 | -## Package LibreSSL -| Summary | Credits | CVE | Upstream Acknowledgement | Tests | -|:---------------------------: |:--------------------------:|:-----------------:|:--------------------------------------: |:---------------------------------------: -| Overly lax RSA PKCS1v1.5 parsing | Alex Gaynor and Paul Kehrer | N/A | https://github.com/openbsd/src/commit/4698a0ba0d5547fce37134cb00f204c68f429885#diff-8c6377c3026df41da690063739326043 | diff --git a/java/com/google/security/wycheproof/testcases/RsaSignatureTest.java b/java/com/google/security/wycheproof/testcases/RsaSignatureTest.java index 9e3a8ba..32ea493 100644 --- a/java/com/google/security/wycheproof/testcases/RsaSignatureTest.java +++ b/java/com/google/security/wycheproof/testcases/RsaSignatureTest.java @@ -15,26 +15,28 @@ package com.google.security.wycheproof; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import java.math.BigInteger; import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.MessageDigest; +import java.security.PrivateKey; import java.security.PublicKey; import java.security.Signature; import java.security.SignatureException; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.RSAPrivateCrtKeySpec; import java.security.spec.RSAPublicKeySpec; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; -/** Tests RSA signature schemes. */ +/** Tests PKCS #1 v 1.5 signatures */ // TODO(bleichen): -// - maybe add tests with invalid keys. -// - So far only PKCS #1 signatures are tested. But RSA-PSS becomes more popular. // - document stuff // - Join other RSA tests @RunWith(JUnit4.class) @@ -1219,4 +1221,103 @@ public class RsaSignatureTest { } } } + + /** + * Faults during the generation of a signature can leak the information about the private key. + * A. K. Lenstra showed in "Memo on RSA signature generation in the presence of faults", + * (https://infoscience.epfl.ch/record/164524/files/nscan20.PDF) that PKCS #1 signatures are + * especially susceptible to faults when the Chinese Remainder Theorem is used to compute the + * signature: one single faulty signature is sufficient to leak the private key. + * + * One countermeasure that is often used in libraries is to blind the RSA computation and + * verify the signature before returning it. Nowadays, libraries are expected to have at least + * some countermeasures against faulty computations. In some cases (e.g. OpenSSL) the library + * tries to fix a faulty computation by generating a correct signature without using Chinese + * remaindering. + * + * The test here does not induce a fault. Instead it tries to sign with a faulty private key. + * The expected outcome of the test is that underlying provider either detects that the fault + * or generates a valid signature by ignoring the faulty CRT parameter. + * + * Since the test only simulates a fault, but does not actually induce a fault it is somewhat + * incomplete. It does not detect all vulnerable implementations. The test should nonetheless + * detect implementations that include no verification at all. + */ + @Test + public void testFaultySigner() throws Exception { + BigInteger e = new BigInteger("65537"); + BigInteger d = new BigInteger( + "1491581187972832788084570222215155297353839087630599492610691218" + + "6098027383804966741416365668088258821394558334495197493887270311" + + "7558637148793177374456685063919969705672268324029058661801838398" + + "1099187046803818325657704350675941092582695993374867459573741707" + + "2513551423973482044545986645893321692393572214394692273248819124" + + "5866638922766330300631727125395012955305761836925591665625409882" + + "5987442083465656021724458811783361811914866856391248003733867121" + + "5531501554906114868306919889638573670925006068497222709802245970" + + "0014474779292382225845722344584808716054088377124806520166137504" + + "58797849822813881641713404303944154638273"); + BigInteger q = new BigInteger( + "1327930250247153291239240833779228146841620599139480980326615632" + + "6868823273498280322301518048955331731683358443542450740927959439" + + "3056349447047388914345605165927201322192706870545643991584573901" + + "9099563807204264522234257863225478717589651408831271029849307682" + + "13198832542217762257092135384802889866043941823057701"); + BigInteger p = new BigInteger( + "1546732137638443281784728718025150988901748595222448633054370906" + + "7724307988669542799529278238746541544956234718616481585427107180" + + "6134464028933334724614223213582911567222033332353858049787180486" + + "8311341830570208335451999930773903649599388066890163502238099141" + + "76306676019969635213034585825883528127235874684082417"); + + BigInteger n = p.multiply(q); + BigInteger dp = d.mod(p.subtract(BigInteger.ONE)); + BigInteger dq = d.mod(q.subtract(BigInteger.ONE)); + BigInteger crt = q.modInverse(p); + RSAPrivateCrtKeySpec validKey = new RSAPrivateCrtKeySpec(n, e, d, p, q, dp, dq, crt); + RSAPrivateCrtKeySpec invalidKey = + new RSAPrivateCrtKeySpec(n, e, d, p, q, dp.add(BigInteger.valueOf(2)), dq, crt); + byte[] message = "Test".getBytes("UTF-8"); + KeyFactory kf = KeyFactory.getInstance("RSA"); + PrivateKey validPrivKey = kf.generatePrivate(validKey); + Signature signer = Signature.getInstance("SHA256WithRSA"); + signer.initSign(validPrivKey); + signer.update(message); + byte[] signature = signer.sign(); + PrivateKey invalidPrivKey = null; + try { + invalidPrivKey = kf.generatePrivate(invalidKey); + } catch (InvalidKeySpecException ex) { + // The provider checks the private key and notices a mismatch. + // This is a good sign, though of course in this case it means that we can't + // check for faults. + System.out.println("Provider catches invalid RSA key:" + ex); + return; + } + byte[] invalidSignature = null; + try { + signer.initSign(invalidPrivKey); + signer.update(message); + invalidSignature = signer.sign(); + } catch (Exception ex) { + // We do not necessarily expect a checked exception here, since generating + // an invalid signature typically indicates a programming error. + // Though RuntimeExceptions are fine here. + System.out.println("Generating PKCS#1 signature with faulty key throws:" + ex); + return; + } + String signatureHex = TestUtil.bytesToHex(signature); + String invalidSignatureHex = TestUtil.bytesToHex(invalidSignature); + if (signatureHex.equals(invalidSignatureHex)) { + // The provider generated a correct signature. This can for example happen if the provider + // does not use the CRT parameters. + System.out.println("Signature generation did not use faulty parameter"); + return; + } + fail("Generated faulty PKCS #1 signature with faulty parameters" + + " valid signature:" + + signatureHex + + " invalid signature:" + + invalidSignatureHex); + } }