1 /* 2 * Copyright 2015 The Android Open Source Project 3 * 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 android.keystore.cts; 18 19 import android.security.keystore.KeyProperties; 20 import android.security.keystore.KeyProtection; 21 import android.test.AndroidTestCase; 22 23 import java.io.BufferedReader; 24 import java.io.ByteArrayInputStream; 25 import java.io.EOFException; 26 import java.io.IOException; 27 import java.io.InputStream; 28 import java.io.InputStreamReader; 29 import java.security.KeyStore; 30 import java.util.Arrays; 31 import java.util.zip.ZipEntry; 32 import java.util.zip.ZipInputStream; 33 34 import javax.crypto.Cipher; 35 import javax.crypto.SecretKey; 36 import javax.crypto.spec.IvParameterSpec; 37 import javax.crypto.spec.SecretKeySpec; 38 39 public class AESCipherNistCavpKatTest extends AndroidTestCase { 40 testECBVarKey128()41 public void testECBVarKey128() throws Exception { 42 runTestsForKatFile("ECBVarKey128.rsp"); 43 } 44 testECBVarKey192()45 public void testECBVarKey192() throws Exception { 46 runTestsForKatFile("ECBVarKey192.rsp"); 47 } testECBVarKey256()48 public void testECBVarKey256() throws Exception { 49 runTestsForKatFile("ECBVarKey256.rsp"); 50 } 51 testECBVarTxt128()52 public void testECBVarTxt128() throws Exception { 53 runTestsForKatFile("ECBVarTxt128.rsp"); 54 } 55 testECBVarTxt192()56 public void testECBVarTxt192() throws Exception { 57 runTestsForKatFile("ECBVarTxt192.rsp"); 58 } 59 testECBVarTxt256()60 public void testECBVarTxt256() throws Exception { 61 runTestsForKatFile("ECBVarTxt256.rsp"); 62 } 63 testECBGFSbox128()64 public void testECBGFSbox128() throws Exception { 65 runTestsForKatFile("ECBGFSbox128.rsp"); 66 } 67 testECBGFSbox192()68 public void testECBGFSbox192() throws Exception { 69 runTestsForKatFile("ECBGFSbox192.rsp"); 70 } 71 testECBGFSbox256()72 public void testECBGFSbox256() throws Exception { 73 runTestsForKatFile("ECBGFSbox256.rsp"); 74 } 75 testECBKeySbox128()76 public void testECBKeySbox128() throws Exception { 77 runTestsForKatFile("ECBKeySbox128.rsp"); 78 } 79 testECBKeySbox192()80 public void testECBKeySbox192() throws Exception { 81 runTestsForKatFile("ECBKeySbox192.rsp"); 82 } 83 testECBKeySbox256()84 public void testECBKeySbox256() throws Exception { 85 runTestsForKatFile("ECBKeySbox256.rsp"); 86 } 87 testCBCVarKey128()88 public void testCBCVarKey128() throws Exception { 89 runTestsForKatFile("CBCVarKey128.rsp"); 90 } 91 testCBCVarKey192()92 public void testCBCVarKey192() throws Exception { 93 runTestsForKatFile("CBCVarKey192.rsp"); 94 } testCBCVarKey256()95 public void testCBCVarKey256() throws Exception { 96 runTestsForKatFile("CBCVarKey256.rsp"); 97 } 98 testCBCVarTxt128()99 public void testCBCVarTxt128() throws Exception { 100 runTestsForKatFile("CBCVarTxt128.rsp"); 101 } 102 testCBCVarTxt192()103 public void testCBCVarTxt192() throws Exception { 104 runTestsForKatFile("CBCVarTxt192.rsp"); 105 } 106 testCBCVarTxt256()107 public void testCBCVarTxt256() throws Exception { 108 runTestsForKatFile("CBCVarTxt256.rsp"); 109 } 110 testCBCGFSbox128()111 public void testCBCGFSbox128() throws Exception { 112 runTestsForKatFile("CBCGFSbox128.rsp"); 113 } 114 testCBCGFSbox192()115 public void testCBCGFSbox192() throws Exception { 116 runTestsForKatFile("CBCGFSbox192.rsp"); 117 } 118 testCBCGFSbox256()119 public void testCBCGFSbox256() throws Exception { 120 runTestsForKatFile("CBCGFSbox256.rsp"); 121 } 122 testCBCKeySbox128()123 public void testCBCKeySbox128() throws Exception { 124 runTestsForKatFile("CBCKeySbox128.rsp"); 125 } 126 testCBCKeySbox192()127 public void testCBCKeySbox192() throws Exception { 128 runTestsForKatFile("CBCKeySbox192.rsp"); 129 } 130 testCBCKeySbox256()131 public void testCBCKeySbox256() throws Exception { 132 runTestsForKatFile("CBCKeySbox256.rsp"); 133 } 134 runTestsForKatFile(String fileName)135 private void runTestsForKatFile(String fileName) throws Exception { 136 try (ZipInputStream zipIn = new ZipInputStream( 137 getContext().getResources().getAssets().open("nist_cavp_aes_kat.zip"))) { 138 ZipEntry zipEntry; 139 byte[] entryContents = null; 140 while ((zipEntry = zipIn.getNextEntry()) != null) { 141 String entryName = zipEntry.getName(); 142 143 // We have to read the contents of all entries because there's no way to skip an 144 // entry without reading its contents. 145 entryContents = new byte[(int) zipEntry.getSize()]; 146 readFully(zipIn, entryContents); 147 148 if (fileName.equals(entryName)) { 149 break; 150 } 151 } 152 153 if (entryContents == null) { 154 fail(fileName + " not found"); 155 return; 156 } 157 158 String blockMode = fileName.substring(0, 3); 159 if ("CFB".equals(blockMode)) { 160 blockMode = fileName.substring(0, 4); 161 } 162 runTestsForKatFile(blockMode, entryContents); 163 } 164 } 165 runTestsForKatFile(String blockMode, byte[] fileContents)166 private void runTestsForKatFile(String blockMode, byte[] fileContents) throws Exception { 167 BufferedReader in = null; 168 int testNumber = 0; 169 try { 170 in = new BufferedReader(new InputStreamReader( 171 new ByteArrayInputStream(fileContents), "ISO-8859-1")); 172 String line; 173 int lineNumber = 0; 174 String section = null; // ENCRYPT or DECRYPT 175 176 boolean insideTestDefinition = false; 177 TestVector testVector = null; 178 179 while ((line = in.readLine()) != null) { 180 lineNumber++; 181 line = line.trim(); 182 if (line.startsWith("#")) { 183 // Ignore comment lines 184 continue; 185 } 186 187 if (!insideTestDefinition) { 188 // Outside of a test definition 189 if (line.length() == 0) { 190 // Ignore empty lines 191 continue; 192 } 193 if ((line.startsWith("[")) && (line.endsWith("]"))) { 194 section = line.substring(1, line.length() - 1); 195 if ((!"DECRYPT".equals(section)) && (!"ENCRYPT".equals(section))) { 196 throw new IOException(lineNumber + ": Unexpected section: " + section); 197 } 198 continue; 199 } 200 201 // Check whether this is a NAME = VALUE line 202 int delimiterIndex = line.indexOf('='); 203 if (delimiterIndex == -1) { 204 throw new IOException(lineNumber + ": Unexpected line outside of test" 205 + " definition: " + line); 206 } 207 String name = line.substring(0, delimiterIndex).trim(); 208 String value = line.substring(delimiterIndex + 1).trim(); 209 210 if ("COUNT".equals(name)) { 211 testNumber = Integer.parseInt(value); 212 insideTestDefinition = true; 213 testVector = new TestVector(); 214 } else { 215 throw new IOException(lineNumber + ": Unexpected line outside of test" 216 + " definition: " + line); 217 } 218 } else { 219 // Inside of a test definition 220 if (line.length() == 0) { 221 // End of test definition 222 boolean encrypt; 223 if ("ENCRYPT".equals(section)) { 224 encrypt = true; 225 } else if ("DECRYPT".equals(section)) { 226 encrypt = false; 227 } else { 228 throw new IOException("Unexpected test operation: " + section); 229 } 230 runKatTest(blockMode, encrypt, testVector); 231 insideTestDefinition = false; 232 testVector = null; 233 } else { 234 // Check whether this is a NAME = VALUE line 235 int delimiterIndex = line.indexOf('='); 236 if (delimiterIndex == -1) { 237 throw new IOException(lineNumber + ": Unexpected line inside test" 238 + " definition: " + line); 239 } 240 String name = line.substring(0, delimiterIndex).trim(); 241 String value = line.substring(delimiterIndex + 1).trim(); 242 243 if ("KEY".equals(name)) { 244 testVector.key = HexEncoding.decode(value); 245 } else if ("IV".equals(name)) { 246 testVector.iv = HexEncoding.decode(value); 247 } else if ("PLAINTEXT".equals(name)) { 248 testVector.plaintext = HexEncoding.decode(value); 249 } else if ("CIPHERTEXT".equals(name)) { 250 testVector.ciphertext = HexEncoding.decode(value); 251 } else { 252 throw new IOException(lineNumber + ": Unexpected line inside test" 253 + " definition: " + line); 254 } 255 } 256 } 257 } 258 } catch (Throwable e) { 259 throw new RuntimeException("Test #" + testNumber + " failed", e); 260 } finally { 261 if (in != null) { 262 try { 263 in.close(); 264 } catch (Exception ignored) {} 265 } 266 } 267 } 268 runKatTest(String mode, boolean encrypt, TestVector testVector)269 private void runKatTest(String mode, boolean encrypt, TestVector testVector) throws Exception { 270 String keyAlias = AESCipherNistCavpKatTest.class.getName(); 271 KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); 272 keyStore.load(null); 273 keyStore.setEntry(keyAlias, 274 new KeyStore.SecretKeyEntry(new SecretKeySpec(testVector.key, "AES")), 275 new KeyProtection.Builder( 276 KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) 277 .setBlockModes(mode) 278 .setEncryptionPaddings("NoPadding") 279 .setRandomizedEncryptionRequired(false) 280 .build()); 281 try { 282 SecretKey key = (SecretKey) keyStore.getKey(keyAlias, null); 283 assertNotNull(key); 284 Cipher cipher = Cipher.getInstance("AES/" + mode + "/NoPadding"); 285 286 int opmode = (encrypt) ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE; 287 if (testVector.iv != null) { 288 cipher.init(opmode, key, new IvParameterSpec(testVector.iv)); 289 } else { 290 cipher.init(opmode, key); 291 } 292 293 byte[] input = (encrypt) ? testVector.plaintext : testVector.ciphertext; 294 byte[] actualOutput = cipher.doFinal(input); 295 byte[] expectedOutput = (encrypt) ? testVector.ciphertext : testVector.plaintext; 296 if (!Arrays.equals(expectedOutput, actualOutput)) { 297 fail("Expected: " + HexEncoding.encode(expectedOutput) 298 + ", actual: " + HexEncoding.encode(actualOutput)); 299 } 300 } finally { 301 keyStore.deleteEntry(keyAlias); 302 } 303 } 304 readFully(InputStream in, byte[] buf)305 private static void readFully(InputStream in, byte[] buf) throws IOException { 306 int offset = 0; 307 int remaining = buf.length; 308 while (remaining > 0) { 309 int chunkSize = in.read(buf, offset, remaining); 310 if (chunkSize == -1) { 311 throw new EOFException("Premature EOF. Remaining: " + remaining); 312 } 313 offset += chunkSize; 314 remaining -= chunkSize; 315 } 316 } 317 318 private static class TestVector { 319 public byte[] key; 320 public byte[] iv; 321 public byte[] plaintext; 322 public byte[] ciphertext; 323 } 324 } 325