aboutsummaryrefslogtreecommitdiff
path: root/java/com/google/security/wycheproof/testcases/RsaEncryptionTest.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/com/google/security/wycheproof/testcases/RsaEncryptionTest.java')
-rw-r--r--java/com/google/security/wycheproof/testcases/RsaEncryptionTest.java227
1 files changed, 227 insertions, 0 deletions
diff --git a/java/com/google/security/wycheproof/testcases/RsaEncryptionTest.java b/java/com/google/security/wycheproof/testcases/RsaEncryptionTest.java
new file mode 100644
index 0000000..a15d45d
--- /dev/null
+++ b/java/com/google/security/wycheproof/testcases/RsaEncryptionTest.java
@@ -0,0 +1,227 @@
+/**
+ * @license
+ * Copyright 2016 Google Inc. All rights reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.security.wycheproof;
+
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.Arrays;
+import java.util.HashSet;
+import javax.crypto.Cipher;
+import javax.crypto.NoSuchPaddingException;
+import junit.framework.TestCase;
+
+/**
+ * RSA encryption tests
+ *
+ * @author bleichen@google.com (Daniel Bleichenbacher)
+ */
+// TODO(bleichen): test vectors check special cases:
+// - ciphertext too long
+// - plaintext too long
+// - ciphertext 0
+// - ciphertext == modulus timing attacks
+public class RsaEncryptionTest extends TestCase {
+
+ /**
+ * Providers that implement RSA with PKCS1Padding but not OAEP are outdated and should be avoided
+ * even if RSA is currently not used in a project. Such providers promote using an insecure
+ * cipher. There is a great danger that PKCS1Padding is used as a temporary workaround, but later
+ * stays in the project for much longer than necessary.
+ */
+ public void testOutdatedProvider() throws Exception {
+ try {
+ Cipher c = Cipher.getInstance("RSA/ECB/PKCS1Padding");
+ try {
+ Cipher.getInstance("RSA/ECB/OAEPWITHSHA-1ANDMGF1PADDING");
+ } catch (NoSuchPaddingException | NoSuchAlgorithmException ex) {
+ fail("Provider " + c.getProvider().getName() + " is outdated and should not be used.");
+ }
+ } catch (NoSuchPaddingException | NoSuchAlgorithmException ex) {
+ System.out.println("RSA/ECB/PKCS1Padding is not implemented");
+ }
+ }
+
+ /**
+ * Tries decrypting random messages with a given algorithm. Counts the number of distinct error
+ * messages and expects this number to be 1.
+ *
+ * <p><b>References:</b>
+ *
+ * <ul>
+ * <li>Bleichenbacher, "Chosen ciphertext attacks against protocols based on the RSA encryption
+ * standard PKCS# 1" Crypto 98
+ * <li>Manger, "A chosen ciphertext attack on RSA optimal asymmetric encryption padding (OAEP)
+ * as standardized in PKCS# 1 v2.0", Crypto 2001 This paper shows that OAEP is susceptible
+ * to a chosen ciphertext attack if error messages distinguish between different failure
+ * condidtions.
+ * <li>Bardou, Focardi, Kawamoto, Simionato, Steel, Tsay "Efficient Padding Oracle Attacks on
+ * Cryptographic Hardware", Crypto 2012 The paper shows that small differences on what
+ * information an attacker recieves can make a big difference on the number of chosen
+ * message necessary for an attack.
+ * <li>Smart, "Errors matter: Breaking RSA-based PIN encryption with thirty ciphertext validity
+ * queries" RSA conference, 2010 This paper shows that padding oracle attacks can be
+ * successful with even a small number of queries.
+ * </ul>
+ *
+ * <p><b>Some recent bugs:</b> CVE-2012-5081: Java JSSE provider leaked information through
+ * exceptions and timing. Both the PKCS #1 padding and the OAEP padding were broken:
+ * http://www-brs.ub.ruhr-uni-bochum.de/netahtml/HSS/Diss/MeyerChristopher/diss.pdf
+ *
+ * <p><b>What this test does not (yet) cover:</b>
+ *
+ * <ul>
+ * <li> A previous version of one of the provider leaked the block type. (when was this fixed?)
+ * <li> Some attacks require a large number of ciphertexts to be detected if random ciphertexts
+ * are used. Such problems require specifically crafted ciphertexts to run in a unit test.
+ * E.g. "Attacking RSA-based Sessions in SSL/TLS" by V. Klima, O. Pokorny, and T. Rosa:
+ * https://eprint.iacr.org/2003/052/
+ * <li> Timing leakages because of differences in parsing the padding (e.g. CVE-2015-7827) Such
+ * differences are too small to be reliably detectable in unit tests.
+ * </ul>
+ */
+ @SuppressWarnings("InsecureCryptoUsage")
+ public void testExceptions(String algorithm) throws Exception {
+ KeyPairGenerator keygen = KeyPairGenerator.getInstance("RSA");
+ keygen.initialize(1024);
+ KeyPair keypair = keygen.genKeyPair();
+ SecureRandom rand = new SecureRandom();
+ Cipher c = Cipher.getInstance(algorithm);
+ byte[] ciphertext = new byte[1024 / 8];
+ HashSet<String> exceptions = new HashSet<String>();
+ final int samples = 1000;
+ for (int i = 0; i < samples; i++) {
+ rand.nextBytes(ciphertext);
+ ciphertext[0] &= (byte) 0x7f;
+ try {
+ c.init(Cipher.DECRYPT_MODE, keypair.getPrivate());
+ c.doFinal(ciphertext);
+ } catch (Exception ex) {
+ exceptions.add(ex.toString());
+ }
+ }
+ Cipher enc = Cipher.getInstance("RSA/ECB/NOPADDING");
+ enc.init(Cipher.ENCRYPT_MODE, keypair.getPublic());
+ c.init(Cipher.DECRYPT_MODE, keypair.getPrivate());
+ byte[][] paddedKeys = generatePkcs1Vectors(1024 / 8);
+ for (int i = 0; i < paddedKeys.length; i++) {
+ ciphertext = enc.doFinal(paddedKeys[i]);
+ try {
+ c.doFinal(ciphertext);
+ } catch (Exception ex) {
+ exceptions.add(ex.toString());
+ }
+ }
+ if (exceptions.size() > 1) {
+ System.out.println("Exceptions for " + algorithm);
+ for (String s : exceptions) {
+ System.out.println(s);
+ }
+ fail("Exceptions leak information about the padding for " + algorithm);
+ }
+ }
+
+ /**
+ * Tests the exceptions for RSA decryption with PKCS1Padding. PKCS1Padding is susceptible to
+ * chosen message attacks. Nonetheless, to minimize the damage of such an attack an implementation
+ * should minimize the information about the failure in the padding.
+ */
+ public void testExceptionsPKCS1() throws Exception {
+ testExceptions("RSA/ECB/PKCS1PADDING");
+ }
+
+ public void testGetExceptionsOAEP() throws Exception {
+ testExceptions("RSA/ECB/OAEPWITHSHA-1ANDMGF1PADDING");
+ }
+
+ /**
+ * Generates PKCS#1 invalid vectors
+ * @param rsaKeyLength
+ * @return
+ */
+ private byte[][] generatePkcs1Vectors(int rsaKeyLength) {
+ // create plain padded keys
+ byte[][] plainPaddedKeys = new byte[13][];
+ // no 0x00 byte to deliver a symmetric key
+ plainPaddedKeys[0] = getEK_NoNullByte(rsaKeyLength);
+ // 0x00 too early in the padding
+ plainPaddedKeys[1] = getEK_NullByteInPadding(rsaKeyLength);
+ // 0x00 too early in the PKCS#1 padding
+ plainPaddedKeys[2] = getEK_NullByteInPkcsPadding(rsaKeyLength);
+ // decrypted ciphertext starting with 0x17 0x02
+ plainPaddedKeys[3] = getEK_WrongFirstByte(rsaKeyLength);
+ // decrypted ciphertext starting with 0x00 0x17
+ plainPaddedKeys[4] = getEK_WrongSecondByte(rsaKeyLength);
+ // different lengths of the decrypted unpadded key
+ plainPaddedKeys[5] = getPaddedKey(rsaKeyLength, 0);
+ plainPaddedKeys[6] = getPaddedKey(rsaKeyLength, 1);
+ plainPaddedKeys[7] = getPaddedKey(rsaKeyLength, 8);
+ plainPaddedKeys[8] = getPaddedKey(rsaKeyLength, 16);
+ plainPaddedKeys[9] = getPaddedKey(rsaKeyLength, 96);
+ // the decrypted padded plaintext is shorter than RSA key
+ plainPaddedKeys[10] = getPaddedKey(rsaKeyLength - 1, 16);
+ plainPaddedKeys[11] = getPaddedKey(rsaKeyLength - 2, 16);
+ // just 0x00 bytes
+ plainPaddedKeys[12] = new byte[rsaKeyLength];
+ return plainPaddedKeys;
+ }
+
+ private byte[] getPaddedKey(int rsaKeyLength, int symmetricKeyLength) {
+ byte[] key = new byte[rsaKeyLength];
+ // fill all the bytes with non-zero values
+ Arrays.fill(key, (byte) 42);
+ // set the first byte to 0x00
+ key[0] = 0x00;
+ // set the second byte to 0x02
+ key[1] = 0x02;
+ // set the separating byte
+ if(symmetricKeyLength != -1) {
+ key[rsaKeyLength - symmetricKeyLength - 1] = 0x00;
+ }
+ return key;
+ }
+
+ private byte[] getEK_WrongFirstByte(int rsaKeyLength) {
+ byte[] key = getPaddedKey(rsaKeyLength, 16);
+ key[0] = 23;
+ return key;
+ }
+
+ private byte[] getEK_WrongSecondByte(int rsaKeyLength) {
+ byte[] key = getPaddedKey(rsaKeyLength, 16);
+ key[1] = 23;
+ return key;
+ }
+
+ private byte[] getEK_NoNullByte(int rsaKeyLength) {
+ byte[] key = getPaddedKey(rsaKeyLength, -1);
+ return key;
+ }
+
+ private byte[] getEK_NullByteInPkcsPadding(int rsaKeyLength) {
+ byte[] key = getPaddedKey(rsaKeyLength, 16);
+ key[3] = 0x00;
+ return key;
+ }
+
+ private byte[] getEK_NullByteInPadding(int rsaKeyLength) {
+ byte[] key = getPaddedKey(rsaKeyLength, 16);
+ key[11] = 0x00;
+ return key;
+ }
+}