Home | History | Annotate | Download | only in testcases
      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.io.ByteArrayOutputStream;
     20 import java.io.IOException;
     21 import java.security.NoSuchAlgorithmException;
     22 import java.security.SecureRandom;
     23 import java.security.spec.AlgorithmParameterSpec;
     24 import java.util.ArrayList;
     25 import java.util.Arrays;
     26 import javax.crypto.Cipher;
     27 import javax.crypto.CipherOutputStream;
     28 import javax.crypto.spec.GCMParameterSpec;
     29 import javax.crypto.spec.SecretKeySpec;
     30 import junit.framework.TestCase;
     31 
     32 /** CipherOutputStream tests */
     33 public class CipherOutputStreamTest extends TestCase {
     34   static final SecureRandom rand = new SecureRandom();
     35 
     36   static byte[] randomBytes(int size) {
     37     byte[] bytes = new byte[size];
     38     rand.nextBytes(bytes);
     39     return bytes;
     40   }
     41 
     42   static SecretKeySpec randomKey(String algorithm, int keySizeInBytes) {
     43     return new SecretKeySpec(randomBytes(keySizeInBytes), "AES");
     44   }
     45 
     46   static AlgorithmParameterSpec randomParameters(
     47       String algorithm, int ivSizeInBytes, int tagSizeInBytes) {
     48     if ("AES/GCM/NoPadding".equals(algorithm) || "AES/EAX/NoPadding".equals(algorithm)) {
     49       return new GCMParameterSpec(8 * tagSizeInBytes, randomBytes(ivSizeInBytes));
     50     }
     51     return null;
     52   }
     53 
     54   /** Test vectors */
     55   @SuppressWarnings("InsecureCryptoUsage")
     56   public static class TestVector {
     57     public String algorithm;
     58     public SecretKeySpec key;
     59     public AlgorithmParameterSpec params;
     60     public byte[] pt;
     61     public byte[] aad;
     62     public byte[] ct;
     63 
     64     public TestVector(
     65         String algorithm, int keySize, int ivSize, int tagSize, int ptSize, int aadSize)
     66         throws Exception {
     67       this.algorithm = algorithm;
     68       this.key = randomKey(algorithm, keySize);
     69       this.params = randomParameters(algorithm, ivSize, tagSize);
     70       this.pt = randomBytes(ptSize);
     71       this.aad = randomBytes(aadSize);
     72       Cipher cipher = Cipher.getInstance(algorithm);
     73       cipher.init(Cipher.ENCRYPT_MODE, this.key, this.params);
     74       cipher.updateAAD(aad);
     75       this.ct = cipher.doFinal(pt);
     76     }
     77   }
     78 
     79   Iterable<TestVector> getTestVectors(
     80       String algorithm,
     81       int[] keySizes,
     82       int[] ivSizes,
     83       int[] tagSizes,
     84       int[] ptSizes,
     85       int[] aadSizes)
     86       throws Exception {
     87     ArrayList<TestVector> result = new ArrayList<TestVector>();
     88     for (int keySize : keySizes) {
     89       for (int ivSize : ivSizes) {
     90         for (int tagSize : tagSizes) {
     91           for (int ptSize : ptSizes) {
     92             for (int aadSize : aadSizes) {
     93               result.add(new TestVector(algorithm, keySize, ivSize, tagSize, ptSize, aadSize));
     94             }
     95           }
     96         }
     97       }
     98     }
     99     return result;
    100   }
    101 
    102   @SuppressWarnings("InsecureCryptoUsage")
    103   public void testEncrypt(Iterable<TestVector> tests) throws Exception {
    104     for (TestVector t : tests) {
    105       Cipher cipher = Cipher.getInstance(t.algorithm);
    106       cipher.init(Cipher.ENCRYPT_MODE, t.key, t.params);
    107       cipher.updateAAD(t.aad);
    108       ByteArrayOutputStream os = new ByteArrayOutputStream();
    109       CipherOutputStream cos = new CipherOutputStream(os, cipher);
    110       cos.write(t.pt);
    111       cos.close();
    112       assertEquals(TestUtil.bytesToHex(t.ct), TestUtil.bytesToHex(os.toByteArray()));
    113     }
    114   }
    115 
    116   @SuppressWarnings("InsecureCryptoUsage")
    117   public void testDecrypt(Iterable<TestVector> tests) throws Exception {
    118     for (TestVector t : tests) {
    119       Cipher cipher = Cipher.getInstance(t.algorithm);
    120       cipher.init(Cipher.DECRYPT_MODE, t.key, t.params);
    121       cipher.updateAAD(t.aad);
    122       ByteArrayOutputStream os = new ByteArrayOutputStream();
    123       CipherOutputStream cos = new CipherOutputStream(os, cipher);
    124       cos.write(t.ct);
    125       cos.close();
    126       assertEquals(TestUtil.bytesToHex(t.pt), TestUtil.bytesToHex(os.toByteArray()));
    127     }
    128   }
    129 
    130   @SuppressWarnings("InsecureCryptoUsage")
    131   public void testCorruptDecrypt(Iterable<TestVector> tests) throws Exception {
    132     for (TestVector t : tests) {
    133       Cipher cipher = Cipher.getInstance(t.algorithm);
    134       cipher.init(Cipher.DECRYPT_MODE, t.key, t.params);
    135       cipher.updateAAD(t.aad);
    136       byte[] ct = Arrays.copyOf(t.ct, t.ct.length);
    137       ct[ct.length - 1] ^= (byte) 1;
    138       ByteArrayOutputStream os = new ByteArrayOutputStream();
    139       CipherOutputStream cos = new CipherOutputStream(os, cipher);
    140       cos.write(ct);
    141       try {
    142         // cos.close() should call cipher.doFinal().
    143         cos.close();
    144         byte[] decrypted = os.toByteArray();
    145         // Unfortunately Oracle thinks that returning an empty array is valid behaviour.
    146         // We accept empty results here, but flag them in the next test, so that we can distinguish
    147         // between beheviour considered acceptable by Oracle and more serious flaws.
    148         if (decrypted.length > 0) {
    149           fail(
    150               "this should fail; decrypted:"
    151                   + TestUtil.bytesToHex(decrypted)
    152                   + " pt: "
    153                   + TestUtil.bytesToHex(t.pt));
    154         }
    155       } catch (IOException ex) {
    156         // expected
    157       }
    158     }
    159   }
    160 
    161   @SuppressWarnings("InsecureCryptoUsage")
    162   public void testCorruptDecryptEmpty(Iterable<TestVector> tests) throws Exception {
    163     for (TestVector t : tests) {
    164       Cipher cipher = Cipher.getInstance(t.algorithm);
    165       cipher.init(Cipher.DECRYPT_MODE, t.key, t.params);
    166       cipher.updateAAD(t.aad);
    167       byte[] ct = Arrays.copyOf(t.ct, t.ct.length);
    168       ct[ct.length - 1] ^= (byte) 1;
    169       ByteArrayOutputStream os = new ByteArrayOutputStream();
    170       CipherOutputStream cos = new CipherOutputStream(os, cipher);
    171       cos.write(ct);
    172       try {
    173         // cos.close() should call cipher.doFinal().
    174         cos.close();
    175         byte[] decrypted = os.toByteArray();
    176         fail(
    177             "this should fail; decrypted:"
    178                 + TestUtil.bytesToHex(decrypted)
    179                 + " pt: "
    180                 + TestUtil.bytesToHex(t.pt));
    181       } catch (IOException ex) {
    182         // expected
    183       }
    184     }
    185   }
    186 
    187   public void testAesGcm() throws Exception {
    188     final int[] keySizes = {16, 32};
    189     final int[] ivSizes = {12};
    190     final int[] tagSizes = {12, 16};
    191     final int[] ptSizes = {8, 16, 65, 8100};
    192     final int[] aadSizes = {0, 8, 24};
    193     Iterable<TestVector> v =
    194         getTestVectors("AES/GCM/NoPadding", keySizes, ivSizes, tagSizes, ptSizes, aadSizes);
    195     testEncrypt(v);
    196     testDecrypt(v);
    197     testCorruptDecrypt(v);
    198   }
    199 
    200   /**
    201    * Unfortunately Oracle thinks that returning an empty array is valid behaviour for corrupt
    202    * ciphertexts. Because of this we test empty plaintext separately to distinguish behaviour
    203    * considered acceptable by Oracle from other behaviour.
    204    */
    205   public void testEmptyPlaintext() throws Exception {
    206     final int[] keySizes = {16, 32};
    207     final int[] ivSizes = {12};
    208     final int[] tagSizes = {12, 16};
    209     final int[] ptSizes = {0};
    210     final int[] aadSizes = {0, 8, 24};
    211     Iterable<TestVector> v =
    212         getTestVectors("AES/GCM/NoPadding", keySizes, ivSizes, tagSizes, ptSizes, aadSizes);
    213     testEncrypt(v);
    214     testDecrypt(v);
    215     testCorruptDecryptEmpty(v);
    216   }
    217 
    218   /** Tests CipherOutputStream with AES-EAX if AES-EAS is supported by the provider. */
    219   @SuppressWarnings("InsecureCryptoUsage")
    220   public void testAesEax() throws Exception {
    221     final String algorithm = "AES/EAX/NoPadding";
    222     final int[] keySizes = {16, 32};
    223     final int[] ivSizes = {12, 16};
    224     final int[] tagSizes = {12, 16};
    225     final int[] ptSizes = {8, 16, 65, 8100};
    226     final int[] aadSizes = {0, 8, 24};
    227     try {
    228       Cipher.getInstance(algorithm);
    229     } catch (NoSuchAlgorithmException ex) {
    230       System.out.println("Skipping testAesEax");
    231       return;
    232     }
    233     Iterable<TestVector> v =
    234         getTestVectors(algorithm, keySizes, ivSizes, tagSizes, ptSizes, aadSizes);
    235     testEncrypt(v);
    236     testDecrypt(v);
    237     testCorruptDecrypt(v);
    238   }
    239 }
    240