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