• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 Google Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 ////////////////////////////////////////////////////////////////////////////////
16 
17 package com.google.crypto.tink.subtle;
18 
19 import static org.junit.Assert.assertArrayEquals;
20 import static org.junit.Assert.assertEquals;
21 import static org.junit.Assert.assertFalse;
22 import static org.junit.Assert.assertThrows;
23 
24 import com.google.crypto.tink.config.TinkFips;
25 import com.google.crypto.tink.config.internal.TinkFipsUtil;
26 import java.security.GeneralSecurityException;
27 import java.security.Security;
28 import java.util.Arrays;
29 import org.conscrypt.Conscrypt;
30 import org.junit.Assume;
31 import org.junit.Before;
32 import org.junit.Test;
33 import org.junit.runner.RunWith;
34 import org.junit.runners.JUnit4;
35 
36 /**
37  * Unit tests for AesCtrJceCipher.
38  */
39 @RunWith(JUnit4.class)
40 public class AesCtrJceCipherTest {
41 
42   // NIST SP 800-38A pp 55.
43   private static final String NIST_KEY = "2b7e151628aed2a6abf7158809cf4f3c";
44   private static final String NIST_PLAINTEXT =
45       "6bc1bee22e409f96e93d7e117393172a"
46           + "ae2d8a571e03ac9c9eb76fac45af8e51"
47           + "30c81c46a35ce411e5fbc1191a0a52ef"
48           + "f69f2445df4f9b17ad2b417be66c3710";
49   private static final String NIST_CIPHERTEXT =
50       "874d6191b620e3261bef6864990db6ce"
51           + "9806f66b7970fdff8617187bb9fffdff"
52           + "5ae4df3edbd5d35e5b4f09020db03eab"
53           + "1e031dda2fbe03d1792170a0f3009cee";
54   private static final String NIST_IV = "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff";
55 
56   private static final String PLAINTEXT =
57       "I'm counter mode and I'm not vulnerable to padding oracle attack like CBC mode";
58 
59   private byte[] msg;
60 
61   @Before
setUp()62   public void setUp() {
63     try {
64       msg = PLAINTEXT.getBytes("UTF-8");
65     } catch (Exception ignored) {
66       // Ignored
67     }
68   }
69 
70   @Before
useConscrypt()71   public void useConscrypt() throws Exception {
72     // If Tink is build in FIPS-only mode, then we register Conscrypt for the tests.
73     if (TinkFips.useOnlyFips()) {
74       try {
75         Conscrypt.checkAvailability();
76         Security.addProvider(Conscrypt.newProvider());
77       } catch (Throwable cause) {
78         throw new IllegalStateException(
79             "Cannot test AesCtr in FIPS-mode without Conscrypt Provider", cause);
80       }
81     }
82   }
83 
84   @Test
testNistVector()85   public void testNistVector() throws Exception {
86     Assume.assumeTrue(!TinkFips.useOnlyFips() || TinkFipsUtil.fipsModuleAvailable());
87 
88     byte[] rawCiphertext = Hex.decode(NIST_CIPHERTEXT);
89     byte[] iv = Hex.decode(NIST_IV);
90     byte[] ciphertext = new byte[iv.length + rawCiphertext.length];
91     System.arraycopy(iv, 0, ciphertext, 0, iv.length);
92     System.arraycopy(rawCiphertext, 0, ciphertext, iv.length, rawCiphertext.length);
93     AesCtrJceCipher cipher = new AesCtrJceCipher(Hex.decode(NIST_KEY), iv.length);
94     assertArrayEquals(Hex.decode(NIST_PLAINTEXT), cipher.decrypt(ciphertext));
95   }
96 
97   @Test
testMultipleEncrypts()98   public void testMultipleEncrypts() throws Exception {
99     Assume.assumeTrue(!TinkFips.useOnlyFips() || TinkFipsUtil.fipsModuleAvailable());
100 
101     // Checks whether multiple encryptions result in different ciphertexts.
102     byte[] key = Random.randBytes(16);
103     int ivSize = 16;
104     AesCtrJceCipher cipher = new AesCtrJceCipher(key, ivSize);
105     byte[] c1 = cipher.encrypt(msg);
106     byte[] c2 = cipher.encrypt(msg);
107     assertEquals(c1.length, c2.length);
108     assertFalse(Arrays.equals(c1, c2));
109   }
110 
111   @Test
testCtrProperty()112   public void testCtrProperty() throws Exception {
113     Assume.assumeTrue(!TinkFips.useOnlyFips() || TinkFipsUtil.fipsModuleAvailable());
114 
115     // Counter mode is malleable, i.e., if we flip the ciphertext, the plaintext is flipped.
116     byte[] key = Random.randBytes(16);
117     int ivSize = 16;
118     AesCtrJceCipher cipher = new AesCtrJceCipher(key, ivSize);
119     byte[] c1 = cipher.encrypt(msg);
120     for (int i = 0; i < msg.length; i++) {
121       for (int j = 0; j < 8; j++) {
122         byte[] p1 = Arrays.copyOf(msg, msg.length);
123         byte[] c2 = Arrays.copyOf(c1, c1.length);
124         p1[i] = (byte) (p1[i] ^ (1 << j));
125         c2[i + ivSize] = (byte) (c2[i + ivSize] ^ (1 << j));
126         byte[] p2 = cipher.decrypt(c2);
127         assertArrayEquals(p1, p2);
128         assertFalse(Arrays.equals(p2, msg));
129       }
130     }
131   }
132 
133   @Test
testEncryptDecrypt()134   public void testEncryptDecrypt() throws Exception {
135     Assume.assumeTrue(!TinkFips.useOnlyFips() || TinkFipsUtil.fipsModuleAvailable());
136 
137     byte[] key = Random.randBytes(16);
138     int ivSize = 16;
139     AesCtrJceCipher c = new AesCtrJceCipher(key, ivSize);
140     byte[] ciphertext = c.encrypt(msg);
141     assertArrayEquals(msg, c.decrypt(ciphertext));
142   }
143 
144   @Test
testFailIfFipsModuleNotAvailable()145   public void testFailIfFipsModuleNotAvailable() throws Exception {
146     Assume.assumeTrue(TinkFips.useOnlyFips() && !TinkFipsUtil.fipsModuleAvailable());
147 
148     byte[] key = Random.randBytes(16);
149     int ivSize = 16;
150     assertThrows(GeneralSecurityException.class, () -> new AesCtrJceCipher(key, ivSize));
151   }
152 }
153