1 /** 2 * @license 3 * Copyright 2016 Google Inc. All rights reserved. 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.security.wycheproof; 18 19 import java.security.KeyPair; 20 import java.security.KeyPairGenerator; 21 import java.security.NoSuchAlgorithmException; 22 import java.security.SecureRandom; 23 import java.util.Arrays; 24 import java.util.HashSet; 25 import javax.crypto.Cipher; 26 import javax.crypto.NoSuchPaddingException; 27 import junit.framework.TestCase; 28 29 /** 30 * RSA encryption tests 31 * 32 * @author bleichen@google.com (Daniel Bleichenbacher) 33 */ 34 // TODO(bleichen): test vectors check special cases: 35 // - ciphertext too long 36 // - plaintext too long 37 // - ciphertext 0 38 // - ciphertext == modulus timing attacks 39 public class RsaEncryptionTest extends TestCase { 40 41 /** 42 * Providers that implement RSA with PKCS1Padding but not OAEP are outdated and should be avoided 43 * even if RSA is currently not used in a project. Such providers promote using an insecure 44 * cipher. There is a great danger that PKCS1Padding is used as a temporary workaround, but later 45 * stays in the project for much longer than necessary. 46 */ testOutdatedProvider()47 public void testOutdatedProvider() throws Exception { 48 try { 49 Cipher c = Cipher.getInstance("RSA/ECB/PKCS1Padding"); 50 try { 51 Cipher.getInstance("RSA/ECB/OAEPWITHSHA-1ANDMGF1PADDING"); 52 } catch (NoSuchPaddingException | NoSuchAlgorithmException ex) { 53 fail("Provider " + c.getProvider().getName() + " is outdated and should not be used."); 54 } 55 } catch (NoSuchPaddingException | NoSuchAlgorithmException ex) { 56 System.out.println("RSA/ECB/PKCS1Padding is not implemented"); 57 } 58 } 59 60 /** 61 * Tries decrypting random messages with a given algorithm. Counts the number of distinct error 62 * messages and expects this number to be 1. 63 * 64 * <p><b>References:</b> 65 * 66 * <ul> 67 * <li>Bleichenbacher, "Chosen ciphertext attacks against protocols based on the RSA encryption 68 * standard PKCS# 1" Crypto 98 69 * <li>Manger, "A chosen ciphertext attack on RSA optimal asymmetric encryption padding (OAEP) 70 * as standardized in PKCS# 1 v2.0", Crypto 2001 This paper shows that OAEP is susceptible 71 * to a chosen ciphertext attack if error messages distinguish between different failure 72 * condidtions. 73 * <li>Bardou, Focardi, Kawamoto, Simionato, Steel, Tsay "Efficient Padding Oracle Attacks on 74 * Cryptographic Hardware", Crypto 2012 The paper shows that small differences on what 75 * information an attacker recieves can make a big difference on the number of chosen 76 * message necessary for an attack. 77 * <li>Smart, "Errors matter: Breaking RSA-based PIN encryption with thirty ciphertext validity 78 * queries" RSA conference, 2010 This paper shows that padding oracle attacks can be 79 * successful with even a small number of queries. 80 * </ul> 81 * 82 * <p><b>Some recent bugs:</b> CVE-2012-5081: Java JSSE provider leaked information through 83 * exceptions and timing. Both the PKCS #1 padding and the OAEP padding were broken: 84 * http://www-brs.ub.ruhr-uni-bochum.de/netahtml/HSS/Diss/MeyerChristopher/diss.pdf 85 * 86 * <p><b>What this test does not (yet) cover:</b> 87 * 88 * <ul> 89 * <li> A previous version of one of the provider leaked the block type. (when was this fixed?) 90 * <li> Some attacks require a large number of ciphertexts to be detected if random ciphertexts 91 * are used. Such problems require specifically crafted ciphertexts to run in a unit test. 92 * E.g. "Attacking RSA-based Sessions in SSL/TLS" by V. Klima, O. Pokorny, and T. Rosa: 93 * https://eprint.iacr.org/2003/052/ 94 * <li> Timing leakages because of differences in parsing the padding (e.g. CVE-2015-7827) Such 95 * differences are too small to be reliably detectable in unit tests. 96 * </ul> 97 */ 98 @SuppressWarnings("InsecureCryptoUsage") testExceptions(String algorithm)99 public void testExceptions(String algorithm) throws Exception { 100 KeyPairGenerator keygen = KeyPairGenerator.getInstance("RSA"); 101 keygen.initialize(1024); 102 KeyPair keypair = keygen.genKeyPair(); 103 SecureRandom rand = new SecureRandom(); 104 Cipher c = Cipher.getInstance(algorithm); 105 byte[] ciphertext = new byte[1024 / 8]; 106 HashSet<String> exceptions = new HashSet<String>(); 107 final int samples = 1000; 108 for (int i = 0; i < samples; i++) { 109 rand.nextBytes(ciphertext); 110 ciphertext[0] &= (byte) 0x7f; 111 try { 112 c.init(Cipher.DECRYPT_MODE, keypair.getPrivate()); 113 c.doFinal(ciphertext); 114 } catch (Exception ex) { 115 exceptions.add(ex.toString()); 116 } 117 } 118 Cipher enc = Cipher.getInstance("RSA/ECB/NOPADDING"); 119 enc.init(Cipher.ENCRYPT_MODE, keypair.getPublic()); 120 c.init(Cipher.DECRYPT_MODE, keypair.getPrivate()); 121 byte[][] paddedKeys = generatePkcs1Vectors(1024 / 8); 122 for (int i = 0; i < paddedKeys.length; i++) { 123 ciphertext = enc.doFinal(paddedKeys[i]); 124 try { 125 c.doFinal(ciphertext); 126 } catch (Exception ex) { 127 exceptions.add(ex.toString()); 128 } 129 } 130 if (exceptions.size() > 1) { 131 System.out.println("Exceptions for " + algorithm); 132 for (String s : exceptions) { 133 System.out.println(s); 134 } 135 fail("Exceptions leak information about the padding for " + algorithm); 136 } 137 } 138 139 /** 140 * Tests the exceptions for RSA decryption with PKCS1Padding. PKCS1Padding is susceptible to 141 * chosen message attacks. Nonetheless, to minimize the damage of such an attack an implementation 142 * should minimize the information about the failure in the padding. 143 */ testExceptionsPKCS1()144 public void testExceptionsPKCS1() throws Exception { 145 testExceptions("RSA/ECB/PKCS1PADDING"); 146 } 147 testGetExceptionsOAEP()148 public void testGetExceptionsOAEP() throws Exception { 149 testExceptions("RSA/ECB/OAEPWITHSHA-1ANDMGF1PADDING"); 150 } 151 152 /** 153 * Generates PKCS#1 invalid vectors 154 * @param rsaKeyLength 155 * @return 156 */ generatePkcs1Vectors(int rsaKeyLength)157 private byte[][] generatePkcs1Vectors(int rsaKeyLength) { 158 // create plain padded keys 159 byte[][] plainPaddedKeys = new byte[13][]; 160 // no 0x00 byte to deliver a symmetric key 161 plainPaddedKeys[0] = getEK_NoNullByte(rsaKeyLength); 162 // 0x00 too early in the padding 163 plainPaddedKeys[1] = getEK_NullByteInPadding(rsaKeyLength); 164 // 0x00 too early in the PKCS#1 padding 165 plainPaddedKeys[2] = getEK_NullByteInPkcsPadding(rsaKeyLength); 166 // decrypted ciphertext starting with 0x17 0x02 167 plainPaddedKeys[3] = getEK_WrongFirstByte(rsaKeyLength); 168 // decrypted ciphertext starting with 0x00 0x17 169 plainPaddedKeys[4] = getEK_WrongSecondByte(rsaKeyLength); 170 // different lengths of the decrypted unpadded key 171 plainPaddedKeys[5] = getPaddedKey(rsaKeyLength, 0); 172 plainPaddedKeys[6] = getPaddedKey(rsaKeyLength, 1); 173 plainPaddedKeys[7] = getPaddedKey(rsaKeyLength, 8); 174 plainPaddedKeys[8] = getPaddedKey(rsaKeyLength, 16); 175 plainPaddedKeys[9] = getPaddedKey(rsaKeyLength, 96); 176 // the decrypted padded plaintext is shorter than RSA key 177 plainPaddedKeys[10] = getPaddedKey(rsaKeyLength - 1, 16); 178 plainPaddedKeys[11] = getPaddedKey(rsaKeyLength - 2, 16); 179 // just 0x00 bytes 180 plainPaddedKeys[12] = new byte[rsaKeyLength]; 181 return plainPaddedKeys; 182 } 183 getPaddedKey(int rsaKeyLength, int symmetricKeyLength)184 private byte[] getPaddedKey(int rsaKeyLength, int symmetricKeyLength) { 185 byte[] key = new byte[rsaKeyLength]; 186 // fill all the bytes with non-zero values 187 Arrays.fill(key, (byte) 42); 188 // set the first byte to 0x00 189 key[0] = 0x00; 190 // set the second byte to 0x02 191 key[1] = 0x02; 192 // set the separating byte 193 if(symmetricKeyLength != -1) { 194 key[rsaKeyLength - symmetricKeyLength - 1] = 0x00; 195 } 196 return key; 197 } 198 getEK_WrongFirstByte(int rsaKeyLength)199 private byte[] getEK_WrongFirstByte(int rsaKeyLength) { 200 byte[] key = getPaddedKey(rsaKeyLength, 16); 201 key[0] = 23; 202 return key; 203 } 204 getEK_WrongSecondByte(int rsaKeyLength)205 private byte[] getEK_WrongSecondByte(int rsaKeyLength) { 206 byte[] key = getPaddedKey(rsaKeyLength, 16); 207 key[1] = 23; 208 return key; 209 } 210 getEK_NoNullByte(int rsaKeyLength)211 private byte[] getEK_NoNullByte(int rsaKeyLength) { 212 byte[] key = getPaddedKey(rsaKeyLength, -1); 213 return key; 214 } 215 getEK_NullByteInPkcsPadding(int rsaKeyLength)216 private byte[] getEK_NullByteInPkcsPadding(int rsaKeyLength) { 217 byte[] key = getPaddedKey(rsaKeyLength, 16); 218 key[3] = 0x00; 219 return key; 220 } 221 getEK_NullByteInPadding(int rsaKeyLength)222 private byte[] getEK_NullByteInPadding(int rsaKeyLength) { 223 byte[] key = getPaddedKey(rsaKeyLength, 16); 224 key[11] = 0x00; 225 return key; 226 } 227 } 228