View Javadoc
1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.commons.crypto.cipher;
19  
20  import java.io.IOException;
21  import java.nio.ByteBuffer;
22  import java.security.GeneralSecurityException;
23  import java.security.SecureRandom;
24  import java.util.Properties;
25  import java.util.Random;
26  import javax.xml.bind.DatatypeConverter;
27  
28  import org.apache.commons.crypto.conf.ConfigurationKeys;
29  import org.apache.commons.crypto.utils.ReflectionUtils;
30  import org.apache.commons.crypto.utils.Utils;
31  import org.junit.Assert;
32  import org.junit.Before;
33  import org.junit.Test;
34  
35  public abstract class AbstractCipherTest {
36  
37    // data
38    public static final int BYTEBUFFER_SIZE = 1000;
39    public String[] cipherTests = null;
40    Properties props = null;
41    String cipherClass = null;
42    CipherTransformation[] transformations = null;
43  
44    // cipher
45    static final byte[] KEY = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
46        0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16};
47    static final byte[] IV = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
48        0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
49    Cipher enc, dec;
50  
51    @Before
52    public void setup() {
53      init();
54      Utils.checkNotNull(cipherClass);
55      Utils.checkNotNull(transformations);
56      props = new Properties();
57      props.setProperty(ConfigurationKeys.COMMONS_CRYPTO_CIPHER_CLASSES_KEY,
58          cipherClass);
59    }
60  
61    protected abstract void init() ;
62  
63    @Test
64    public void cryptoTest() throws GeneralSecurityException, IOException {
65      for (CipherTransformation tran : transformations) {
66        /** uses the small data set in {@link TestData} */
67        cipherTests = TestData.getTestData(tran);
68        for (int i = 0; i != cipherTests.length; i += 5) {
69          byte[] key = DatatypeConverter.parseHexBinary(cipherTests[i + 1]);
70          byte[] iv = DatatypeConverter.parseHexBinary(cipherTests[i + 2]);
71  
72          byte[] inputBytes = DatatypeConverter.parseHexBinary(cipherTests[i + 3]);
73          byte[] outputBytes = DatatypeConverter.parseHexBinary(cipherTests[i + 4]);
74  
75          ByteBuffer inputBuffer = ByteBuffer.allocateDirect(inputBytes.length);
76          ByteBuffer outputBuffer = ByteBuffer.allocateDirect(outputBytes.length);
77          inputBuffer.put(inputBytes);
78          inputBuffer.flip();
79          outputBuffer.put(outputBytes);
80          outputBuffer.flip();
81  
82          byteBufferTest(tran,key, iv, inputBuffer, outputBuffer);
83          byteArrayTest(tran, key, iv, inputBytes, outputBytes);
84        }
85  
86        /** uses randomly generated big data set */
87        byteArrayTest(tran, KEY, IV);
88      }
89    }
90  
91    private void byteBufferTest(CipherTransformation transformation, byte[] key,
92                                byte[] iv,
93                                  ByteBuffer input, ByteBuffer output) throws
94        GeneralSecurityException, IOException {
95      ByteBuffer decResult = ByteBuffer.allocateDirect(BYTEBUFFER_SIZE);
96      ByteBuffer encResult = ByteBuffer.allocateDirect(BYTEBUFFER_SIZE);
97      Cipher enc, dec;
98  
99      enc = getCipher(transformation);
100     dec = getCipher(transformation);
101 
102     try {
103       enc.init(Cipher.ENCRYPT_MODE, key, iv);
104     } catch (Exception e) {
105       Assert.fail("AES failed initialisation - " + e.toString());
106     }
107 
108     try {
109       dec.init(Cipher.DECRYPT_MODE, key, iv);
110     } catch (Exception e) {
111       Assert.fail("AES failed initialisation - " + e.toString());
112     }
113 
114     //
115     // encryption pass
116     //
117     enc.doFinal(input, encResult);
118     input.flip();
119     encResult.flip();
120     if (!output.equals(encResult)) {
121       byte[] b = new byte[output.remaining()];
122       output.get(b);
123       byte[] c = new byte[encResult.remaining()];
124       encResult.get(c);
125       Assert.fail("AES failed encryption - expected " + new String(
126           DatatypeConverter
127               .printHexBinary(b)) + " got " + new String(
128           DatatypeConverter.printHexBinary(c)));
129     }
130 
131     //
132     // decryption pass
133     //
134     dec.doFinal(encResult, decResult);
135     decResult.flip();
136 
137     if (!input.equals(decResult)) {
138       byte[] inArray = new byte[input.remaining()];
139       byte[] decResultArray = new byte[decResult.remaining()];
140       input.get(inArray);
141       decResult.get(decResultArray);
142       Assert.fail();
143     }
144   }
145 
146   /** test byte array whose data is planned in {@link TestData} */
147   private void byteArrayTest(CipherTransformation transformation, byte[] key,
148       byte[] iv, byte[] input, byte[] output) throws GeneralSecurityException {
149     resetCipher(transformation, key, iv);
150     int blockSize = transformation.getAlgorithmBlockSize();
151 
152     byte[] temp = new byte[input.length + blockSize];
153     int n = enc.doFinal(input, 0, input.length, temp, 0);
154     byte[] cipherText = new byte[n];
155     System.arraycopy(temp, 0, cipherText, 0, n);
156     Assert.assertArrayEquals("byte array encryption error.", output, cipherText);
157 
158     temp = new byte[cipherText.length + blockSize];
159     int m = dec.doFinal(cipherText, 0, cipherText.length, temp, 0);
160     byte[] plainText = new byte[m];
161     System.arraycopy(temp, 0, plainText, 0, m);
162     Assert.assertArrayEquals("byte array decryption error.", input, plainText);
163   }
164 
165   /** test byte array whose data is randomly generated */
166   private void byteArrayTest(CipherTransformation transformation, byte[] key,
167       byte[] iv) throws GeneralSecurityException {
168     int blockSize = transformation.getAlgorithmBlockSize();
169 
170     // AES_CBC_NOPADDING only accepts data whose size is the multiple of block size
171     int[] dataLenList = (transformation == CipherTransformation.AES_CBC_NOPADDING)
172         ? new int[] {10 * 1024} : new int[] {10 * 1024, 10 * 1024 - 3};
173     for (int dataLen : dataLenList) {
174       byte[] plainText = new byte[dataLen];
175       Random random = new SecureRandom();
176       random.nextBytes(plainText);
177       byte[] cipherText = new byte[dataLen + blockSize];
178 
179       // check update method with inputs whose sizes are the multiple of block size or not
180       int[] bufferLenList = new int[] {2 * 1024 - 128, 2 * 1024 - 125};
181       for (int bufferLen : bufferLenList) {
182         resetCipher(transformation, key, iv);
183 
184         int offset = 0;
185         // encrypt (update + doFinal) the data
186         int cipherPos = 0;
187         for (int i = 0; i < dataLen / bufferLen; i ++) {
188           cipherPos += enc.update(plainText, offset, bufferLen, cipherText, cipherPos);
189           offset += bufferLen;
190         }
191         cipherPos += enc.doFinal(plainText, offset, dataLen % bufferLen, cipherText, cipherPos);
192 
193         offset = 0;
194         // decrypt (update + doFinal) the data
195         byte[] realPlainText = new byte[cipherPos + blockSize];
196         int plainPos = 0;
197         for (int i = 0; i < cipherPos / bufferLen; i ++) {
198           plainPos += dec.update(cipherText, offset, bufferLen, realPlainText, plainPos);
199           offset += bufferLen;
200         }
201         plainPos += dec.doFinal(cipherText, offset, cipherPos % bufferLen, realPlainText, plainPos);
202 
203         // verify
204         Assert.assertEquals("random byte array length changes after transformation",
205             dataLen, plainPos);
206 
207         byte[] shrinkPlainText = new byte[plainPos];
208         System.arraycopy(realPlainText, 0, shrinkPlainText, 0, plainPos);
209         Assert.assertArrayEquals("random byte array contents changes after transformation",
210             plainText, shrinkPlainText);
211       }
212     }
213   }
214 
215   private void resetCipher(CipherTransformation transformation, byte[] key, byte[] iv) {
216     enc = getCipher(transformation);
217     dec = getCipher(transformation);
218 
219     try {
220       enc.init(Cipher.ENCRYPT_MODE, key, iv);
221     } catch (Exception e) {
222       Assert.fail("AES failed initialisation - " + e.toString());
223     }
224 
225     try {
226       dec.init(Cipher.DECRYPT_MODE, key, iv);
227     } catch (Exception e) {
228       Assert.fail("AES failed initialisation - " + e.toString());
229     }
230   }
231 
232   private Cipher getCipher(CipherTransformation transformation) {
233     try {
234       return (Cipher) ReflectionUtils
235           .newInstance(ReflectionUtils.getClassByName(cipherClass), props,
236               transformation);
237     } catch (ClassNotFoundException e) {
238       throw new RuntimeException(e);
239     }
240 
241   }
242 }