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
This commit is contained in:
Daniel Bleichenbacher
2018-08-06 02:41:25 -07:00
committed by Tink Team
parent c313761979
commit 8e7e87adb8
2 changed files with 104 additions and 11 deletions
-8
View File
@@ -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 |
@@ -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);
}
}