blob: adaab57ab5ea1db61f9a08d31168a11d8041d87e [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2005-2007 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
20 * CA 95054 USA or visit www.sun.com if you need additional information or
21 * have any questions.
22 */
23
24/*
25 * @test
26 * @bug 5083253
27 * @run main CTSMode
28 * @summary Verify that CTR mode works as expected
29 * @author Valerie Peng
30 */
31
32import java.util.*;
33import java.security.AlgorithmParameters;
34import javax.crypto.*;
35import javax.crypto.spec.*;
36
37public class CTSMode {
38
39 private final static byte[] toByteArray(String s) {
40 char[] c = s.toCharArray();
41 byte[] t = new byte[c.length / 2];
42 int n = 0;
43 int d1 = -1;
44 int d2 = -1;
45 for (int i = 0; i < c.length; i++) {
46 char ch = c[i];
47 if (d1 == -1) {
48 d1 = Character.digit(ch, 16);
49 } else {
50 d2 = Character.digit(ch, 16);
51 if (d2 != -1) {
52 t[n++] = (byte)((d1 << 4) | d2);
53 d1 = -1;
54 d2 = -1;
55 }
56 }
57 }
58 if (d1 != -1) {
59 throw new RuntimeException();
60 }
61 if (n == t.length) {
62 return t;
63 }
64 byte[] b = new byte[n];
65 System.arraycopy(t, 0, b, 0, n);
66 return b;
67 }
68
69 private final static SecretKey KEY1 =
70 new SecretKeySpec(toByteArray("636869636b656e207465726979616b69"),
71 "AES");
72
73 private final static IvParameterSpec IV1 =
74 new IvParameterSpec(new byte[16]);
75
76 /*
77 * Use test vectors, i.e. PLAIN1 and CIPHER1, from the appendix B
78 * of RFC 3962 "Advanced Encryption Standard (AES) Encryption for
79 * Kerberos 5".
80 */
81 private final static byte[][] PLAIN1 = {
82 toByteArray("4920776f756c64206c696b652074686520"),
83 toByteArray("4920776f756c64206c696b6520746865" +
84 "2047656e6572616c20476175277320"),
85 toByteArray("4920776f756c64206c696b6520746865" +
86 "2047656e6572616c2047617527732043"),
87 toByteArray("4920776f756c64206c696b6520746865" +
88 "2047656e6572616c2047617527732043" +
89 "6869636b656e2c20706c656173652c"),
90 toByteArray("4920776f756c64206c696b6520746865" +
91 "2047656e6572616c2047617527732043" +
92 "6869636b656e2c20706c656173652c20"),
93 toByteArray("4920776f756c64206c696b6520746865" +
94 "2047656e6572616c2047617527732043" +
95 "6869636b656e2c20706c656173652c20" +
96 "616e6420776f6e746f6e20736f75702e")
97 };
98 private final static byte[][] CIPHER1 = {
99 toByteArray("c6353568f2bf8cb4d8a580362da7ff7f97"),
100 toByteArray("fc00783e0efdb2c1d445d4c8eff7ed22" +
101 "97687268d6ecccc0c07b25e25ecfe5"),
102 toByteArray("39312523a78662d5be7fcbcc98ebf5a8" +
103 "97687268d6ecccc0c07b25e25ecfe584"),
104 toByteArray("97687268d6ecccc0c07b25e25ecfe584" +
105 "b3fffd940c16a18c1b5549d2f838029e" +
106 "39312523a78662d5be7fcbcc98ebf5"),
107 toByteArray("97687268d6ecccc0c07b25e25ecfe584" +
108 "9dad8bbb96c4cdc03bc103e1a194bbd8" +
109 "39312523a78662d5be7fcbcc98ebf5a8"),
110 toByteArray("97687268d6ecccc0c07b25e25ecfe584" +
111 "39312523a78662d5be7fcbcc98ebf5a8" +
112 "4807efe836ee89a526730dbc2f7bc840" +
113 "9dad8bbb96c4cdc03bc103e1a194bbd8"),
114 };
115
116 private final static byte[] IV2_SRC =
117 toByteArray("11223344556677880011223344556677");
118
119 private final static String[] ALGORITHMS2 = {
120 "DES", "DESede", "Blowfish", "AES"
121 };
122 private final static int[] KEYSIZES2 = {
123 8, 24, 16, 16
124 };
125
126 private static String toString(byte[] b) {
127 StringBuffer sb = new StringBuffer(b.length * 3);
128 for (int i = 0; i < b.length; i++) {
129 int k = b[i] & 0xff;
130 if (i != 0) {
131 sb.append(':');
132 }
133 sb.append(Character.forDigit(k >> 4, 16));
134 sb.append(Character.forDigit(k & 0xf, 16));
135 }
136 return sb.toString();
137 }
138
139 public static void main(String[] args) throws Exception {
140 test1();
141 test2();
142 test3();
143 }
144
145 /**
146 * Test with the test vectors and see if results match.
147 */
148 private static void test1() throws Exception {
149 for (int i = 0; i < PLAIN1.length; i++) {
150 String algo = KEY1.getAlgorithm();
151 int MAX_KEYSIZE = Cipher.getMaxAllowedKeyLength(algo);
152 if (KEY1.getEncoded().length > MAX_KEYSIZE) {
153 // skip tests using keys whose length exceeds
154 // what's configured in jce jurisdiction policy files.
155 continue;
156 }
157 System.out.println("Running test1_" + i + " (" + algo + ")");
158 Cipher cipher = Cipher.getInstance(algo+ "/CTS/NoPadding",
159 "SunJCE");
160 byte[] plainText = PLAIN1[i];
161 byte[] cipherText = CIPHER1[i];
162 cipher.init(Cipher.ENCRYPT_MODE, KEY1, IV1);
163 byte[] enc = cipher.doFinal(plainText);
164 if (Arrays.equals(cipherText, enc) == false) {
165 System.out.println("plain: " + toString(plainText));
166 System.out.println("cipher: " + toString(cipherText));
167 System.out.println("actual: " + toString(enc));
168 throw new RuntimeException("Encryption failure for test " + i);
169 }
170 cipher.init(Cipher.DECRYPT_MODE, KEY1, IV1);
171 byte[] dec = cipher.doFinal(cipherText);
172 if (Arrays.equals(plainText, dec) == false) {
173 System.out.println("cipher: " + toString(cipherText));
174 System.out.println("plain: " + toString(plainText));
175 System.out.println("actual: " + toString(enc));
176 throw new RuntimeException("Decryption failure for test " + i);
177 }
178 }
179 }
180
181 /**
182 * Test with a combination of update/doFinal calls and make
183 * sure that same data is recovered after decryption.
184 */
185 private static void test2() throws Exception {
186 for (int i = 0; i < ALGORITHMS2.length; i++) {
187 String algo = ALGORITHMS2[i];
188 System.out.println("Running test2_" + i + " (" + algo + ")");
189 int keySize = KEYSIZES2[i];
190 int MAX_KEYSIZE = Cipher.getMaxAllowedKeyLength(algo);
191 if (keySize > MAX_KEYSIZE) {
192 // skip tests using keys whose length exceeds
193 // what's configured in jce jurisdiction policy files.
194 continue;
195 }
196 Cipher cipher =
197 Cipher.getInstance(algo+"/CTS/NoPadding", "SunJCE");
198 int blockSize = cipher.getBlockSize();
199 SecretKeySpec key = new SecretKeySpec(new byte[keySize], algo);
200 // Make sure encryption works for inputs with valid length
201 byte[] plainText = PLAIN1[3];
202 cipher.init(Cipher.ENCRYPT_MODE, key);
203 byte[] cipherText = new byte[plainText.length];
204 int firstPartLen = blockSize + 1;
205 int processed1 = cipher.update(plainText, 0, firstPartLen,
206 cipherText, 0);
207 int processed2 = cipher.doFinal(plainText, firstPartLen,
208 plainText.length-firstPartLen,
209 cipherText, processed1);
210 AlgorithmParameters params = cipher.getParameters();
211 if ((processed1 + processed2) != plainText.length) {
212 System.out.println("processed1 = " + processed1);
213 System.out.println("processed2 = " + processed2);
214 System.out.println("total length = " + plainText.length);
215 throw new RuntimeException("Encryption failure for test " + i);
216 }
217 // Make sure IllegalBlockSizeException is thrown for inputs
218 // with less-than-a-block length
219 try {
220 cipher.doFinal(new byte[blockSize-1]);
221 throw new RuntimeException("Expected IBSE is not thrown");
222 } catch (IllegalBlockSizeException ibse) {
223 }
224 // Make sure data is encrypted as in CBC mode for inputs
225 // which is exactly one block long
226 IvParameterSpec iv2 = new IvParameterSpec(IV2_SRC, 0, blockSize);
227 cipher.init(Cipher.ENCRYPT_MODE, key, iv2);
228 Cipher cipher2 =
229 Cipher.getInstance(algo+"/CBC/NoPadding", "SunJCE");
230 cipher2.init(Cipher.ENCRYPT_MODE, key, iv2);
231
232 byte[] eout = cipher.doFinal(IV2_SRC, 0, blockSize);
233 byte[] eout2 = cipher2.doFinal(IV2_SRC, 0, blockSize);
234 if (!Arrays.equals(eout, eout2)) {
235 throw new RuntimeException("Different encryption output " +
236 "for CBC and CTS");
237 }
238 // Make sure decryption works for inputs with valid length
239 cipher.init(Cipher.DECRYPT_MODE, key, params);
240 byte[] recoveredText =
241 new byte[cipher.getOutputSize(cipherText.length)];
242 processed1 = cipher.update(cipherText, 0, firstPartLen,
243 recoveredText, 0);
244 processed2 = cipher.update(cipherText, firstPartLen,
245 cipherText.length-firstPartLen,
246 recoveredText, processed1);
247 int processed3 =
248 cipher.doFinal(recoveredText, processed1+processed2);
249 if ((processed1 + processed2 + processed3) != plainText.length) {
250 System.out.println("processed1 = " + processed1);
251 System.out.println("processed2 = " + processed2);
252 System.out.println("processed3 = " + processed3);
253 System.out.println("total length = " + plainText.length);
254 throw new RuntimeException("Decryption failure for test " + i);
255 }
256 if (Arrays.equals(plainText, recoveredText) == false) {
257 System.out.println("plain: " + toString(plainText));
258 System.out.println("recovered: " + toString(recoveredText));
259 throw new RuntimeException("Decryption failure for test " + i);
260 }
261 // Make sure IllegalBlockSizeException is thrown for inputs
262 // with less-than-a-block length
263 try {
264 cipher.doFinal(new byte[blockSize-1]);
265 throw new RuntimeException("Expected IBSE is not thrown");
266 } catch (IllegalBlockSizeException ibse) {
267 }
268 // Make sure data is decrypted as in CBC mode for inputs
269 // which is exactly one block long
270 cipher.init(Cipher.DECRYPT_MODE, key, iv2);
271 cipher2.init(Cipher.DECRYPT_MODE, key, iv2);
272 byte[] dout = cipher.doFinal(eout);
273 byte[] dout2 = cipher2.doFinal(eout2);
274 if (!Arrays.equals(dout, dout2)) {
275 throw new RuntimeException("Different decryption output " +
276 "for CBC and CTS");
277 }
278 }
279 }
280
281 /**
282 * Test with a shortBuffer and see if encryption/decryption
283 * still works correctly afterwards.
284 */
285 private static void test3() throws Exception {
286 // Skip PLAIN1[0, 1, 2] due to their lengths
287 for (int i = 3; i < PLAIN1.length; i++) {
288 String algo = KEY1.getAlgorithm();
289 System.out.println("Running test3_" + i + " (" + algo + ")");
290 int MAX_KEYSIZE = Cipher.getMaxAllowedKeyLength(algo);
291 if (KEY1.getEncoded().length > MAX_KEYSIZE) {
292 // skip tests using keys whose length exceeds
293 // what's configured in jce jurisdiction policy files.
294 continue;
295 }
296 Cipher cipher =
297 Cipher.getInstance(algo+ "/CTS/NoPadding", "SunJCE");
298 byte[] plainText = PLAIN1[i];
299 byte[] cipherText = CIPHER1[i];
300 byte[] enc = new byte[plainText.length];
301 cipher.init(Cipher.ENCRYPT_MODE, KEY1, IV1);
302 int halfInput = plainText.length/2;
303 int processed1 = cipher.update(plainText, 0, halfInput,
304 enc, 0);
305 try {
306 cipher.doFinal(plainText, halfInput,
307 plainText.length-halfInput,
308 new byte[1], 0);
309 throw new RuntimeException("Expected Exception is not thrown");
310 } catch (ShortBufferException sbe) {
311 // expected exception thrown; retry
312 int processed2 =
313 cipher.doFinal(plainText, halfInput,
314 plainText.length-halfInput,
315 enc, processed1);
316 if ((processed1 + processed2) != enc.length) {
317 System.out.println("processed1 = " + processed1);
318 System.out.println("processed2 = " + processed2);
319 System.out.println("total length = " + enc.length);
320 throw new RuntimeException("Encryption length check " +
321 "failed");
322 }
323 }
324 if (Arrays.equals(cipherText, enc) == false) {
325 System.out.println("plain: " + toString(plainText));
326 System.out.println("cipher: " + toString(cipherText));
327 System.out.println("actual: " + toString(enc));
328 throw new RuntimeException("Encryption failure for test " + i);
329 }
330 cipher.init(Cipher.DECRYPT_MODE, KEY1, IV1);
331 byte[] dec =
332 new byte[cipher.getOutputSize(cipherText.length)];
333 processed1 = cipher.update(cipherText, 0, halfInput,
334 dec, 0);
335 try {
336 cipher.update(cipherText, halfInput,
337 cipherText.length-halfInput,
338 new byte[1], 0);
339 throw new RuntimeException("Expected Exception is not thrown");
340 } catch (ShortBufferException sbe) {
341 // expected exception thrown; retry
342 int processed2 = cipher.update(cipherText, halfInput,
343 cipherText.length-halfInput,
344 dec, processed1);
345 int processed3 =
346 cipher.doFinal(dec, processed1+processed2);
347 if ((processed1 + processed2 + processed3) != dec.length) {
348 System.out.println("processed1 = " + processed1);
349 System.out.println("processed2 = " + processed2);
350 System.out.println("processed3 = " + processed3);
351 System.out.println("total length = " + dec.length);
352 throw new RuntimeException("Decryption length check " +
353 "failed");
354 }
355 }
356 if (Arrays.equals(plainText, dec) == false) {
357 System.out.println("cipher: " + toString(cipherText));
358 System.out.println("plain: " + toString(plainText));
359 System.out.println("actualD: " + toString(dec));
360 throw new RuntimeException("Decryption failure for test " + i);
361 }
362 }
363 }
364}