1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.crypto.cipher;
19
20
21 import static org.junit.jupiter.api.Assertions.assertArrayEquals;
22 import static org.junit.jupiter.api.Assertions.assertEquals;
23 import static org.junit.jupiter.api.Assertions.assertNotNull;
24 import static org.junit.jupiter.api.Assertions.assertThrows;
25 import static org.junit.jupiter.api.Assertions.fail;
26
27 import java.nio.ByteBuffer;
28 import java.security.InvalidAlgorithmParameterException;
29 import java.security.InvalidKeyException;
30 import java.security.SecureRandom;
31 import java.util.Properties;
32 import java.util.Random;
33
34 import javax.crypto.Cipher;
35 import javax.crypto.spec.GCMParameterSpec;
36 import javax.crypto.spec.IvParameterSpec;
37 import javax.crypto.spec.SecretKeySpec;
38 import javax.xml.bind.DatatypeConverter;
39
40 import org.apache.commons.crypto.utils.AES;
41 import org.apache.commons.crypto.utils.ReflectionUtils;
42 import org.junit.jupiter.api.BeforeEach;
43 import org.junit.jupiter.api.Test;
44
45 public abstract class AbstractCipherTest {
46
47 public static final String OPENSSL_CIPHER_CLASSNAME = OpenSslCipher.class.getName();
48
49 public static final String JCE_CIPHER_CLASSNAME = JceCipher.class.getName();
50
51
52 public static final int BYTEBUFFER_SIZE = 1000;
53
54
55 static final byte[] KEY = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14,
56 0x15, 0x16, 0x17, 0x18, 0x19, 0x20, 0x21, 0x22, 0x23, 0x24 };
57 static final byte[] IV = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
58 0x08 };
59 public String[] cipherTests;
60 private Properties props;
61
62 protected String cipherClass;
63 protected String[] transformations;
64 private CryptoCipher enc, dec;
65
66
67 private void byteArrayTest(final String transformation, final byte[] key, final byte[] iv) throws Exception {
68 final int blockSize = enc.getBlockSize();
69
70
71
72 final int[] dataLenList = transformation.equals(AES.CBC_NO_PADDING) ? new int[] { 10 * 1024 }
73 : new int[] { 10 * 1024, 10 * 1024 - 3 };
74 for (final int dataLen : dataLenList) {
75 final byte[] plainText = new byte[dataLen];
76 final Random random = new SecureRandom();
77 random.nextBytes(plainText);
78 final byte[] cipherText = new byte[dataLen + blockSize];
79
80
81
82 final int[] bufferLenList = { 2 * 1024 - 128, 2 * 1024 - 125 };
83 for (final int bufferLen : bufferLenList) {
84 resetCipher(transformation, key, iv);
85
86 int offset = 0;
87
88 int cipherPos = 0;
89 for (int i = 0; i < dataLen / bufferLen; i++) {
90 cipherPos += enc.update(plainText, offset, bufferLen, cipherText, cipherPos);
91 offset += bufferLen;
92 }
93 cipherPos += enc.doFinal(plainText, offset, dataLen % bufferLen, cipherText, cipherPos);
94
95 offset = 0;
96
97 final byte[] realPlainText = new byte[cipherPos + blockSize];
98 int plainPos = 0;
99 for (int i = 0; i < cipherPos / bufferLen; i++) {
100 plainPos += dec.update(cipherText, offset, bufferLen, realPlainText, plainPos);
101 offset += bufferLen;
102 }
103 plainPos += dec.doFinal(cipherText, offset, cipherPos % bufferLen, realPlainText, plainPos);
104
105
106 assertEquals(dataLen, plainPos, "random byte array length changes after transformation");
107
108 final byte[] shrinkPlainText = new byte[plainPos];
109 System.arraycopy(realPlainText, 0, shrinkPlainText, 0, plainPos);
110 assertArrayEquals(plainText, shrinkPlainText, "random byte array contents changes after transformation");
111 }
112 }
113 }
114
115
116 private void byteArrayTest(final String transformation, final byte[] key, final byte[] iv, final byte[] input,
117 final byte[] output) throws Exception {
118 resetCipher(transformation, key, iv);
119 final int blockSize = enc.getBlockSize();
120
121 byte[] temp = new byte[input.length + blockSize];
122 final int n = enc.doFinal(input, 0, input.length, temp, 0);
123 final byte[] cipherText = new byte[n];
124 System.arraycopy(temp, 0, cipherText, 0, n);
125 assertArrayEquals(output, cipherText, "byte array encryption error.");
126
127 temp = new byte[cipherText.length + blockSize];
128 final int m = dec.doFinal(cipherText, 0, cipherText.length, temp, 0);
129 final byte[] plainText = new byte[m];
130 System.arraycopy(temp, 0, plainText, 0, m);
131 assertArrayEquals(input, plainText, "byte array decryption error.");
132 }
133
134 private void byteBufferTest(final String transformation, final byte[] key, final byte[] iv, final ByteBuffer input,
135 final ByteBuffer output) throws Exception {
136 final ByteBuffer decResult = ByteBuffer.allocateDirect(BYTEBUFFER_SIZE);
137 final ByteBuffer encResult = ByteBuffer.allocateDirect(BYTEBUFFER_SIZE);
138
139 try (final CryptoCipher enc = getCipher(transformation); final CryptoCipher dec = getCipher(transformation)) {
140
141 enc.init(Cipher.ENCRYPT_MODE, AES.newSecretKeySpec(key), new IvParameterSpec(iv));
142 dec.init(Cipher.DECRYPT_MODE, AES.newSecretKeySpec(key), new IvParameterSpec(iv));
143
144
145
146
147 enc.doFinal(input, encResult);
148 input.flip();
149 encResult.flip();
150 if (!output.equals(encResult)) {
151 final byte[] b = new byte[output.remaining()];
152 output.get(b);
153 final byte[] c = new byte[encResult.remaining()];
154 encResult.get(c);
155 fail("AES failed encryption - expected " + DatatypeConverter.printHexBinary(b)
156 + " got " + DatatypeConverter.printHexBinary(c));
157 }
158
159
160
161
162 dec.doFinal(encResult, decResult);
163 decResult.flip();
164
165 if (!input.equals(decResult)) {
166 final byte[] inArray = new byte[input.remaining()];
167 final byte[] decResultArray = new byte[decResult.remaining()];
168 input.get(inArray);
169 decResult.get(decResultArray);
170 fail();
171 }
172 }
173 }
174
175 @Test
176 public void closeTestAfterInit() throws Exception {
177
178
179 try (final CryptoCipher enc = getCipher(transformations[0])) {
180 enc.init(Cipher.ENCRYPT_MODE, newSecretKeySpec(), new IvParameterSpec(IV));
181 }
182 }
183
184 @Test
185 public void closeTestNoInit() throws Exception {
186
187
188 try (final CryptoCipher enc = getCipher(transformations[0])) {
189
190 }
191 }
192
193 @Test
194 public void closeTestRepeat() throws Exception {
195
196
197 try (final CryptoCipher enc = getCipher(transformations[0])) {
198 enc.close();
199 enc.close();
200 }
201 }
202
203 @Test
204 public void cryptoTest() throws Exception {
205 for (final String tran : transformations) {
206
207 cipherTests = TestData.getTestData(tran);
208 assertNotNull(cipherTests, tran);
209 for (int i = 0; i != cipherTests.length; i += 5) {
210 final byte[] key = DatatypeConverter.parseHexBinary(cipherTests[i + 1]);
211 final byte[] iv = DatatypeConverter.parseHexBinary(cipherTests[i + 2]);
212
213 final byte[] inputBytes = DatatypeConverter.parseHexBinary(cipherTests[i + 3]);
214 final byte[] outputBytes = DatatypeConverter.parseHexBinary(cipherTests[i + 4]);
215
216 final ByteBuffer inputBuffer = ByteBuffer.allocateDirect(inputBytes.length);
217 final ByteBuffer outputBuffer = ByteBuffer.allocateDirect(outputBytes.length);
218 inputBuffer.put(inputBytes);
219 inputBuffer.flip();
220 outputBuffer.put(outputBytes);
221 outputBuffer.flip();
222
223 byteBufferTest(tran, key, iv, inputBuffer, outputBuffer);
224 byteArrayTest(tran, key, iv, inputBytes, outputBytes);
225 }
226
227
228 byteArrayTest(tran, KEY, IV);
229 }
230 }
231
232 private CryptoCipher getCipher(final String transformation) throws ClassNotFoundException {
233 return (CryptoCipher) ReflectionUtils.newInstance(ReflectionUtils.getClassByName(cipherClass), props,
234 transformation);
235 }
236
237 protected abstract void init();
238
239 SecretKeySpec newSecretKeySpec() {
240 return AES.newSecretKeySpec(KEY);
241 }
242
243 @Test
244 public void reInitAfterClose() throws Exception {
245
246
247 try (final CryptoCipher enc = getCipher(transformations[0])) {
248 enc.init(Cipher.ENCRYPT_MODE, newSecretKeySpec(), new IvParameterSpec(IV));
249 enc.close();
250 enc.init(Cipher.DECRYPT_MODE, newSecretKeySpec(), new IvParameterSpec(IV));
251 }
252 }
253
254 @Test
255 public void reInitTest() throws Exception {
256
257
258 try (final CryptoCipher enc = getCipher(transformations[0])) {
259 enc.init(Cipher.ENCRYPT_MODE, newSecretKeySpec(), new IvParameterSpec(IV));
260 enc.init(Cipher.DECRYPT_MODE, newSecretKeySpec(), new IvParameterSpec(IV));
261 enc.init(Cipher.ENCRYPT_MODE, newSecretKeySpec(), new IvParameterSpec(IV));
262 }
263 }
264
265 private void resetCipher(final String transformation, final byte[] key, final byte[] iv)
266 throws ClassNotFoundException, InvalidKeyException, InvalidAlgorithmParameterException {
267 enc = getCipher(transformation);
268 dec = getCipher(transformation);
269
270 enc.init(Cipher.ENCRYPT_MODE, AES.newSecretKeySpec(key), new IvParameterSpec(iv));
271
272 dec.init(Cipher.DECRYPT_MODE, AES.newSecretKeySpec(key), new IvParameterSpec(iv));
273 }
274
275 @BeforeEach
276 public void setup() {
277 init();
278 assertNotNull(cipherClass, "cipherClass");
279 assertNotNull(transformations, "transformations");
280 props = new Properties();
281 props.setProperty(CryptoCipherFactory.CLASSES_KEY, cipherClass);
282 }
283
284 @Test
285 public void testInvalidIV() throws Exception {
286 for (final String transform : transformations) {
287 try (final CryptoCipher cipher = getCipher(transform)) {
288 assertNotNull(cipher);
289 final byte[] invalidIV = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
290 0x0d, 0x0e, 0x0f, 0x11 };
291 assertThrows(InvalidAlgorithmParameterException.class, () -> cipher.init(OpenSsl.ENCRYPT_MODE, newSecretKeySpec(), new IvParameterSpec(invalidIV)));
292 }
293 }
294 }
295
296 @Test
297 public void testInvalidIVClass() throws Exception {
298 for (final String transform : transformations) {
299 try (final CryptoCipher cipher = getCipher(transform)) {
300 assertNotNull(cipher);
301 assertThrows(InvalidAlgorithmParameterException.class, () -> cipher.init(OpenSsl.ENCRYPT_MODE, newSecretKeySpec(), new GCMParameterSpec(IV.length, IV)));
302 }
303 }
304 }
305
306 @Test
307 public void testInvalidKey() throws Exception {
308 for (final String transform : transformations) {
309 try (final CryptoCipher cipher = getCipher(transform)) {
310 assertNotNull(cipher);
311
312 final byte[] invalidKey = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
313 0x0c, 0x0d, 0x0e, 0x0f, 0x11 };
314 assertThrows(InvalidKeyException.class, () -> cipher.init(OpenSsl.ENCRYPT_MODE, AES.newSecretKeySpec(invalidKey), new IvParameterSpec(IV)));
315 }
316 }
317 }
318
319 @Test
320 public void testInvalidTransform() {
321 assertThrows(IllegalArgumentException.class,
322 () -> getCipher("AES/CBR/NoPadding/garbage/garbage").close());
323 }
324
325 @Test
326 public void testNullTransform() {
327 assertThrows(IllegalArgumentException.class,
328 () -> getCipher(null).close());
329 }
330 }