• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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