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