• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 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 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.assertNotNull;
23 import static org.junit.Assert.assertNull;
24 import static org.junit.Assert.assertSame;
25 import static org.junit.Assert.assertTrue;
26 import static org.junit.Assert.fail;
27 
28 import android.keystore.cts.util.EmptyArray;
29 import android.keystore.cts.util.TestUtils;
30 import android.os.Build;
31 import android.os.SystemProperties;
32 import android.security.keystore.KeyProperties;
33 import android.security.keystore.KeyProtection;
34 
35 import androidx.test.runner.AndroidJUnit4;
36 
37 import junit.framework.AssertionFailedError;
38 
39 import org.junit.After;
40 import org.junit.Before;
41 import org.junit.Test;
42 import org.junit.runner.RunWith;
43 
44 import java.io.ByteArrayOutputStream;
45 import java.nio.ByteBuffer;
46 import java.security.AlgorithmParameters;
47 import java.security.InvalidAlgorithmParameterException;
48 import java.security.InvalidKeyException;
49 import java.security.Key;
50 import java.security.KeyStore;
51 import java.security.NoSuchAlgorithmException;
52 import java.security.NoSuchProviderException;
53 import java.security.Provider;
54 import java.security.SecureRandom;
55 import java.security.Security;
56 import java.security.spec.AlgorithmParameterSpec;
57 import java.security.spec.InvalidParameterSpecException;
58 import java.util.Arrays;
59 import java.util.Enumeration;
60 import java.util.Locale;
61 
62 import javax.crypto.BadPaddingException;
63 import javax.crypto.Cipher;
64 import javax.crypto.IllegalBlockSizeException;
65 import javax.crypto.NoSuchPaddingException;
66 import javax.crypto.SecretKey;
67 import javax.crypto.ShortBufferException;
68 import javax.crypto.spec.IvParameterSpec;
69 import javax.crypto.spec.SecretKeySpec;
70 
71 @RunWith(AndroidJUnit4.class)
72 abstract class BlockCipherTestBase {
73 
74     private static final String EXPECTED_PROVIDER_NAME = TestUtils.EXPECTED_CRYPTO_OP_PROVIDER_NAME;
75     private static final int LARGE_MESSAGE_SIZE = 100 * 1024;
76 
77     private KeyStore mAndroidKeyStore;
78     private int mNextKeyId;
79     private SecureRandom mRand = new SecureRandom();
80 
81     @Before
setUp()82     public void setUp() throws Exception {
83         if (isStrongbox()) {
84             TestUtils.assumeStrongBox();
85         }
86         mAndroidKeyStore = KeyStore.getInstance("AndroidKeyStore");
87         mAndroidKeyStore.load(null);
88         for (Enumeration<String> e = mAndroidKeyStore.aliases(); e.hasMoreElements();) {
89             mAndroidKeyStore.deleteEntry(e.nextElement());
90         }
91     }
92 
93     @After
tearDown()94     public void tearDown() throws Exception {
95         if (mAndroidKeyStore != null) {
96             for (Enumeration<String> e = mAndroidKeyStore.aliases(); e.hasMoreElements();) {
97                 mAndroidKeyStore.deleteEntry(e.nextElement());
98             }
99         }
100     }
101 
getTransformation()102     protected abstract String getTransformation();
getBlockSize()103     protected abstract int getBlockSize();
104 
getKatKey()105     protected abstract byte[] getKatKey();
getKatIv()106     protected abstract byte[] getKatIv();
getKatAlgorithmParameterSpec()107     protected abstract AlgorithmParameterSpec getKatAlgorithmParameterSpec();
getKatPlaintext()108     protected abstract byte[] getKatPlaintext();
getKatCiphertext()109     protected abstract byte[] getKatCiphertext();
getKatAuthenticationTagLengthBytes()110     protected abstract int getKatAuthenticationTagLengthBytes();
isStreamCipher()111     protected abstract boolean isStreamCipher();
isAuthenticatedCipher()112     protected abstract boolean isAuthenticatedCipher();
113 
getIv(AlgorithmParameters params)114     protected abstract byte[] getIv(AlgorithmParameters params)
115             throws InvalidParameterSpecException;
116 
isStrongbox()117     abstract protected boolean isStrongbox();
118 
getKatInput(int opmode)119     private byte[] getKatInput(int opmode) {
120         switch (opmode) {
121             case Cipher.ENCRYPT_MODE:
122                 return getKatPlaintext();
123             case Cipher.DECRYPT_MODE:
124                 return getKatCiphertext();
125             default:
126                 throw new IllegalArgumentException("Invalid opmode: " + opmode);
127         }
128     }
129 
getKatOutput(int opmode)130     private byte[] getKatOutput(int opmode) {
131         switch (opmode) {
132             case Cipher.ENCRYPT_MODE:
133                 return getKatCiphertext();
134             case Cipher.DECRYPT_MODE:
135                 return getKatPlaintext();
136             default:
137                 throw new IllegalArgumentException("Invalid opmode: " + opmode);
138         }
139     }
140 
141     private Cipher mCipher;
142     private int mOpmode;
143 
144     @Test
testGetAlgorithm()145     public void testGetAlgorithm() throws Exception {
146         createCipher();
147         assertEquals(getTransformation(), mCipher.getAlgorithm());
148     }
149 
150     @Test
testGetProvider()151     public void testGetProvider() throws Exception {
152         createCipher();
153         Provider expectedProvider = Security.getProvider(EXPECTED_PROVIDER_NAME);
154         assertSame(expectedProvider, mCipher.getProvider());
155     }
156 
157     @Test
testGetBlockSize()158     public void testGetBlockSize() throws Exception {
159         createCipher();
160         assertEquals(getBlockSize(), mCipher.getBlockSize());
161     }
162 
163     @Test
testGetExemptionMechanism()164     public void testGetExemptionMechanism() throws Exception {
165         createCipher();
166         assertNull(mCipher.getExemptionMechanism());
167     }
168 
169     @Test
testGetParameters()170     public void testGetParameters() throws Exception {
171         createCipher();
172         assertAlgoritmParametersIv(null);
173 
174         initKat(Cipher.ENCRYPT_MODE);
175         assertAlgoritmParametersIv(getKatIv());
176         doFinal(getKatPlaintext());
177         assertAlgoritmParametersIv(getKatIv());
178 
179         initKat(Cipher.DECRYPT_MODE);
180         assertAlgoritmParametersIv(getKatIv());
181         doFinal(getKatCiphertext());
182         assertAlgoritmParametersIv(getKatIv());
183     }
184 
assertAlgoritmParametersIv(byte[] expectedIv)185     private void assertAlgoritmParametersIv(byte[] expectedIv)
186             throws InvalidParameterSpecException {
187         AlgorithmParameters actualParameters = mCipher.getParameters();
188         if (expectedIv == null) {
189             assertNull(actualParameters);
190         } else {
191             byte[] actualIv = getIv(actualParameters);
192             assertArrayEquals(expectedIv, actualIv);
193         }
194     }
195 
196     @Test
testGetOutputSizeInEncryptionMode()197     public void testGetOutputSizeInEncryptionMode() throws Exception {
198         int blockSize = getBlockSize();
199         createCipher();
200         try {
201             mCipher.getOutputSize(blockSize);
202             fail();
203         } catch (IllegalStateException expected) {}
204 
205         initKat(Cipher.ENCRYPT_MODE);
206         if (isAuthenticatedCipher()) {
207             // Authenticated ciphers do not return any output when decrypting until doFinal where
208             // ciphertext is authenticated.
209             for (int input = 0; input <= blockSize * 2; input++) {
210                 int actualOutputSize = mCipher.getOutputSize(input);
211                 int expectedOutputSize = input + getKatAuthenticationTagLengthBytes();
212                 if (actualOutputSize < expectedOutputSize) {
213                     fail("getOutputSize(" + expectedOutputSize + ") underestimated output size"
214                             + ". min expected: <" + expectedOutputSize
215                             + ">, actual: <" + actualOutputSize + ">");
216                 }
217             }
218             return;
219         } else if (isStreamCipher()) {
220             // Unauthenticated stream ciphers do not buffer input or output.
221             for (int input = 0; input <= blockSize * 2; input++) {
222                 int actualOutputSize = mCipher.getOutputSize(input);
223                 if (actualOutputSize < input) {
224                     fail("getOutputSize(" + input + ") underestimated output size. min expected: <"
225                             + input + ">, actual: <" + actualOutputSize + ">");
226                 }
227             }
228             return;
229         }
230         // Not a stream cipher -- input may be buffered.
231 
232         for (int buffered = 0; buffered < blockSize; buffered++) {
233             // Assert that the output of getOutputSize is not lower than the minimum expected.
234             for (int input = 0; input <= blockSize * 2; input++) {
235                 int inputInclBuffered = buffered + input;
236                 // doFinal dominates the output size.
237                 // One full plaintext block results in one ciphertext block.
238                 int minExpectedOutputSize = inputInclBuffered - (inputInclBuffered % blockSize);
239                 if (isPaddingEnabled()) {
240                     // Regardless of whether there is a partial input block, an additional block of
241                     // ciphertext should be output.
242                     minExpectedOutputSize += blockSize;
243                 } else {
244                     // When no padding is enabled, any remaining partial block of plaintext will
245                     // cause an error. Thus, there's no need to account for its ciphertext.
246                 }
247                 int actualOutputSize = mCipher.getOutputSize(input);
248                 if (actualOutputSize < minExpectedOutputSize) {
249                     fail("getOutputSize(" + input + ") underestimated output size when buffered == "
250                             + buffered + ". min expected: <"
251                             + minExpectedOutputSize + ">, actual: <" + actualOutputSize + ">");
252                 }
253             }
254 
255             if (buffered == blockSize - 1) {
256                 break;
257             }
258             // Buffer one more byte of input.
259             assertNull("buffered: " + buffered, update(new byte[1]));
260         }
261     }
262 
263     @Test
testGetOutputSizeInDecryptionMode()264     public void testGetOutputSizeInDecryptionMode() throws Exception {
265         if (!TestUtils.isAttestationSupported()) {
266             return;
267         }
268 
269         int blockSize = getBlockSize();
270         createCipher();
271         try {
272             mCipher.getOutputSize(blockSize);
273             fail();
274         } catch (IllegalStateException expected) {}
275 
276         initKat(Cipher.DECRYPT_MODE);
277         if ((!isAuthenticatedCipher()) && (isStreamCipher())) {
278             // Unauthenticated stream ciphers do not buffer input or output.
279             for (int input = 0; input <= blockSize * 2; input++) {
280                 int actualOutputSize = mCipher.getOutputSize(input);
281                 int expectedOutputSize = input;
282                 if (actualOutputSize < expectedOutputSize) {
283                     fail("getOutputSize(" + expectedOutputSize + ") underestimated output size"
284                             + ". min expected: <" + expectedOutputSize
285                             + ">, actual: <" + actualOutputSize + ">");
286                 }
287             }
288             return;
289         }
290         // Input may be buffered.
291 
292         for (int buffered = 0; buffered < blockSize; buffered++) {
293             // Assert that the output of getOutputSize is not lower than the minimum expected.
294             for (int input = 0; input <= blockSize * 2; input++) {
295                 int inputInclBuffered = buffered + input;
296                 // doFinal dominates the output size.
297                 int minExpectedOutputSize;
298                 if (isAuthenticatedCipher()) {
299                     // Non-stream authenticated ciphers not supported
300                     assertTrue(isStreamCipher());
301 
302                     // Authenticated stream cipher
303                     minExpectedOutputSize =
304                             inputInclBuffered - getKatAuthenticationTagLengthBytes();
305                 } else {
306                     // Unauthenticated block cipher.
307 
308                     // One full ciphertext block results in one ciphertext block.
309                     minExpectedOutputSize = inputInclBuffered - (inputInclBuffered % blockSize);
310                     if (isPaddingEnabled()) {
311                         if ((inputInclBuffered % blockSize) == 0) {
312                             // No more ciphertext remaining. Thus, the last plaintext block is at
313                             // most blockSize - 1 bytes long.
314                             minExpectedOutputSize--;
315                         } else {
316                             // Partial ciphertext block cannot be decrypted. Thus, the last
317                             // plaintext block would not have been output.
318                             minExpectedOutputSize -= blockSize;
319                         }
320                     } else {
321                         // When no padding is enabled, any remaining ciphertext will cause a error
322                         // because only full blocks can be decrypted. Thus, there's no need to
323                         // account for its plaintext.
324                     }
325                 }
326                 if (minExpectedOutputSize < 0) {
327                     minExpectedOutputSize = 0;
328                 }
329                 int actualOutputSize = mCipher.getOutputSize(input);
330                 if (actualOutputSize < minExpectedOutputSize) {
331                     fail("getOutputSize(" + input + ") underestimated output size when buffered == "
332                             + buffered + ". min expected: <"
333                             + minExpectedOutputSize + ">, actual: <" + actualOutputSize + ">");
334                 }
335             }
336 
337             if (buffered == blockSize - 1) {
338                 break;
339             }
340             // Buffer one more byte of input.
341             assertNull("buffered: " + buffered, update(new byte[1]));
342         }
343     }
344 
345     @Test
testInitRequiresIvInDecryptMode()346     public void testInitRequiresIvInDecryptMode() throws Exception {
347         if (getKatIv() == null) {
348             // IV not used in this transformation.
349             return;
350         }
351 
352         createCipher();
353         try {
354             init(Cipher.DECRYPT_MODE, getKey());
355             fail();
356         } catch (InvalidKeyException expected) {}
357 
358         createCipher();
359         try {
360             init(Cipher.DECRYPT_MODE, getKey(), (SecureRandom) null);
361             fail();
362         } catch (InvalidKeyException expected) {}
363 
364         createCipher();
365         try {
366             init(Cipher.DECRYPT_MODE, getKey(), (AlgorithmParameterSpec) null, null);
367             fail();
368         } catch (InvalidAlgorithmParameterException expected) {}
369 
370         createCipher();
371         try {
372             init(Cipher.DECRYPT_MODE, getKey(), (AlgorithmParameterSpec) null, null);
373             fail();
374         } catch (InvalidAlgorithmParameterException expected) {}
375 
376         createCipher();
377         try {
378             init(Cipher.DECRYPT_MODE, getKey(), (AlgorithmParameters) null, null);
379             fail();
380         } catch (InvalidAlgorithmParameterException expected) {}
381 
382         createCipher();
383         try {
384             init(Cipher.DECRYPT_MODE, getKey(), (AlgorithmParameters) null, null);
385             fail();
386         } catch (InvalidAlgorithmParameterException expected) {}
387     }
388 
389     @Test
testGetIV()390     public void testGetIV() throws Exception {
391         createCipher();
392         assertNull(mCipher.getIV());
393 
394         initKat(Cipher.ENCRYPT_MODE);
395         assertArrayEquals(getKatIv(), mCipher.getIV());
396 
397         byte[] ciphertext = doFinal(new byte[getBlockSize()]);
398         assertArrayEquals(getKatIv(), mCipher.getIV());
399 
400         createCipher();
401         initKat(Cipher.DECRYPT_MODE);
402         assertArrayEquals(getKatIv(), mCipher.getIV());
403 
404         doFinal(ciphertext);
405         assertArrayEquals(getKatIv(), mCipher.getIV());
406     }
407 
408     @Test
testIvGeneratedAndUsedWhenEncryptingWithoutExplicitIv()409     public void testIvGeneratedAndUsedWhenEncryptingWithoutExplicitIv() throws Exception {
410         createCipher();
411         SecretKey key = getKey();
412         init(Cipher.ENCRYPT_MODE, key);
413         byte[] generatedIv = mCipher.getIV();
414         AlgorithmParameters generatedParams = mCipher.getParameters();
415         if (getKatIv() == null) {
416             // IV not needed by this transformation -- shouldn't have been generated by Cipher.init
417             assertNull(generatedIv);
418             assertNull(generatedParams);
419         } else {
420             // IV is needed by this transformation -- should've been generated by Cipher.init
421             assertNotNull(generatedIv);
422             assertEquals(getKatIv().length, generatedIv.length);
423             assertNotNull(generatedParams);
424             assertArrayEquals(generatedIv, getIv(generatedParams));
425         }
426 
427         // Assert that encrypting then decrypting using the above IV (or null) results in the
428         // original plaintext.
429         byte[] plaintext = new byte[getBlockSize()];
430         byte[] ciphertext = doFinal(plaintext);
431         createCipher();
432         init(Cipher.DECRYPT_MODE, key, generatedParams);
433         byte[] decryptedPlaintext = mCipher.doFinal(ciphertext);
434         assertArrayEquals(plaintext, decryptedPlaintext);
435     }
436 
437     @Test
testGeneratedIvSurvivesReset()438     public void testGeneratedIvSurvivesReset() throws Exception {
439         if (getKatIv() == null) {
440             // This transformation does not use an IV
441             return;
442         }
443 
444         createCipher();
445         init(Cipher.ENCRYPT_MODE, getKey());
446         byte[] iv = mCipher.getIV();
447         AlgorithmParameters generatedParams = mCipher.getParameters();
448         byte[] ciphertext = mCipher.doFinal(getKatPlaintext());
449         // Assert that the IV is still there
450         assertArrayEquals(iv, mCipher.getIV());
451         assertAlgoritmParametersIv(iv);
452 
453         if (getKatIv() != null) {
454             // We try to prevent IV reuse by not letting the Cipher be reused.
455             return;
456         }
457 
458         // Assert that encrypting the same input after the above reset produces the same ciphertext.
459         assertArrayEquals(ciphertext, mCipher.doFinal(getKatPlaintext()));
460 
461         assertArrayEquals(iv, mCipher.getIV());
462         assertAlgoritmParametersIv(iv);
463 
464         // Just in case, test with a new instance of Cipher with the same parameters
465         createCipher();
466         init(Cipher.ENCRYPT_MODE, getKey(), generatedParams);
467         assertArrayEquals(ciphertext, mCipher.doFinal(getKatPlaintext()));
468     }
469 
470     @Test
testGeneratedIvDoesNotSurviveReinitialization()471     public void testGeneratedIvDoesNotSurviveReinitialization() throws Exception {
472         if (getKatIv() == null) {
473             // This transformation does not use an IV
474             return;
475         }
476 
477         createCipher();
478         init(Cipher.ENCRYPT_MODE, getKey());
479         byte[] ivBeforeReinitialization = mCipher.getIV();
480 
481         init(Cipher.ENCRYPT_MODE, getKey());
482         // A new IV should've been generated
483         if (Arrays.equals(ivBeforeReinitialization, mCipher.getIV())) {
484             fail("Same auto-generated IV after Cipher reinitialized."
485                     + " Broken implementation or you're very unlucky (p: 2^{-"
486                     + (ivBeforeReinitialization.length * 8) + "})");
487         }
488     }
489 
490     @Test
testExplicitlySetIvDoesNotSurviveReinitialization()491     public void testExplicitlySetIvDoesNotSurviveReinitialization() throws Exception {
492         if (getKatIv() == null) {
493             // This transformation does not use an IV
494             return;
495         }
496 
497         createCipher();
498         initKat(Cipher.ENCRYPT_MODE);
499         init(Cipher.ENCRYPT_MODE, getKey());
500         // A new IV should've been generated
501         if (Arrays.equals(getKatIv(), mCipher.getIV())) {
502             fail("Auto-generated IV after Cipher reinitialized is the same as previous IV."
503                     + " Broken implementation or you're very unlucky (p: 2^{-"
504                     + (getKatIv().length * 8) + "})");
505         }
506     }
507 
508     @Test
testReinitializingInDecryptModeDoesNotUsePreviouslyUsedIv()509     public void testReinitializingInDecryptModeDoesNotUsePreviouslyUsedIv() throws Exception {
510         if (getKatIv() == null) {
511             // This transformation does not use an IV
512             return;
513         }
514 
515         createCipher();
516         // Initialize with explicitly provided IV
517         init(Cipher.ENCRYPT_MODE, getKey(), getKatAlgorithmParameterSpec());
518         // Make sure the IV has been used, just in case it's set/cached lazily.
519         mCipher.update(new byte[getBlockSize() * 2]);
520 
521         // IV required but not provided
522         try {
523             init(Cipher.DECRYPT_MODE, getKey());
524             fail();
525         } catch (InvalidKeyException expected) {}
526 
527         createCipher();
528         // Initialize with a generated IV
529         init(Cipher.ENCRYPT_MODE, getKey());
530         mCipher.doFinal(getKatPlaintext());
531 
532         // IV required but not provided
533         try {
534             init(Cipher.DECRYPT_MODE, getKey());
535             fail();
536         } catch (InvalidKeyException expected) {}
537 
538         // IV required but not provided
539         try {
540             init(Cipher.DECRYPT_MODE, getKey(), (SecureRandom) null);
541             fail();
542         } catch (InvalidKeyException expected) {}
543 
544         // IV required but not provided
545         try {
546             init(Cipher.DECRYPT_MODE, getKey(), (AlgorithmParameterSpec) null);
547             fail();
548         } catch (InvalidAlgorithmParameterException expected) {}
549 
550         // IV required but not provided
551         try {
552             init(Cipher.DECRYPT_MODE, getKey(), (AlgorithmParameterSpec) null, null);
553             fail();
554         } catch (InvalidAlgorithmParameterException expected) {}
555 
556         // IV required but not provided
557         try {
558             init(Cipher.DECRYPT_MODE, getKey(), (AlgorithmParameters) null);
559             fail();
560         } catch (InvalidAlgorithmParameterException expected) {}
561 
562         // IV required but not provided
563         try {
564             init(Cipher.DECRYPT_MODE, getKey(), (AlgorithmParameters) null, null);
565             fail();
566         } catch (InvalidAlgorithmParameterException expected) {}
567     }
568 
569     @Test
testKeyDoesNotSurviveReinitialization()570     public void testKeyDoesNotSurviveReinitialization() throws Exception {
571         assertKeyDoesNotSurviveReinitialization(Cipher.ENCRYPT_MODE);
572         assertKeyDoesNotSurviveReinitialization(Cipher.DECRYPT_MODE);
573     }
574 
assertKeyDoesNotSurviveReinitialization(int opmode)575     private void assertKeyDoesNotSurviveReinitialization(int opmode) throws Exception {
576         byte[] input = getKatInput(opmode);
577         createCipher();
578         byte[] katKeyBytes = getKatKey();
579         SecretKey key1 = importKey(katKeyBytes);
580         init(opmode, key1, getKatAlgorithmParameterSpec());
581         byte[] output1 = doFinal(input);
582 
583         // Create a different key by flipping a bit in the KAT key.
584         katKeyBytes[0] ^= 0b1000; // Flip a bit that _does_ affect 3DES
585         SecretKey key2 = importKey(katKeyBytes);
586 
587         init(opmode, key2, getKatAlgorithmParameterSpec());
588         byte[] output2 = null;
589         try {
590             output2 = doFinal(input);
591         } catch (BadPaddingException expected) {
592             // Padding doesn't decode probably because the new key is being used. This can only
593             // occur if padding is used.
594             return;
595         } catch (IllegalBlockSizeException notExpected) {
596             if (isStrongbox()) {
597                 fail("Should throw BadPaddingException (b/194126736)");
598             } else {
599                 throw notExpected;
600             }
601         }
602 
603         // Either padding wasn't used or the old key was used.
604         if (Arrays.equals(output1, output2)) {
605             fail("Same output when reinitialized with a different key. opmode: " + opmode);
606         }
607     }
608 
609     @Test
testDoFinalResets()610     public void testDoFinalResets() throws Exception {
611         if (!TestUtils.isAttestationSupported()) {
612             return;
613         }
614 
615         assertDoFinalResetsCipher(Cipher.DECRYPT_MODE);
616         assertDoFinalResetsCipher(Cipher.ENCRYPT_MODE);
617     }
618 
assertDoFinalResetsCipher(int opmode)619     private void assertDoFinalResetsCipher(int opmode) throws Exception {
620         byte[] input = getKatInput(opmode);
621         byte[] expectedOutput = getKatOutput(opmode);
622 
623         createCipher();
624         initKat(opmode);
625         assertArrayEquals(expectedOutput, doFinal(input));
626 
627         if ((opmode == Cipher.ENCRYPT_MODE) && (getKatIv() != null)) {
628             // Assert that this cipher cannot be reused (thus making IV reuse harder)
629             try {
630                 doFinal(input);
631                 fail();
632             } catch (IllegalStateException expected) {}
633             return;
634         }
635 
636         // Assert that the same output is produced after the above reset
637         assertArrayEquals(expectedOutput, doFinal(input));
638 
639         // Assert that the same output is produced after the above reset. This time, make update()
640         // buffer half a block of input.
641         if (input.length < getBlockSize() * 2) {
642             fail("This test requires an input which is at least two blocks long");
643         }
644         assertArrayEquals(expectedOutput, concat(
645                 update(subarray(input, 0, getBlockSize() * 3 / 2)),
646                 doFinal(subarray(input, getBlockSize() * 3 / 2, input.length))));
647 
648         // Assert that the same output is produced after the above reset, despite half of the block
649         // having been buffered prior to the reset. This is in case the implementation does not
650         // empty that buffer when resetting.
651         assertArrayEquals(expectedOutput, doFinal(input));
652 
653         // Assert that the IV with which the cipher was initialized is still there after the resets.
654         assertArrayEquals(getKatIv(), mCipher.getIV());
655         assertAlgoritmParametersIv(getKatIv());
656     }
657 
658     @Test
testUpdateWithEmptyInputReturnsCorrectValue()659     public void testUpdateWithEmptyInputReturnsCorrectValue() throws Exception {
660         // Test encryption
661         createCipher();
662         initKat(Cipher.ENCRYPT_MODE);
663         assertUpdateWithEmptyInputReturnsNull();
664 
665         // Test decryption
666         createCipher();
667         initKat(Cipher.DECRYPT_MODE);
668         assertUpdateWithEmptyInputReturnsNull();
669     }
670 
assertUpdateWithEmptyInputReturnsNull()671     private void assertUpdateWithEmptyInputReturnsNull() {
672         assertEquals(null, update(new byte[0]));
673         assertEquals(null, update(new byte[getBlockSize() * 2], getBlockSize(), 0));
674         assertEquals(null, update(new byte[getBlockSize()], 0, 0));
675 
676         // Feed two blocks through the Cipher, so that it's in a state where a block of input
677         // produces a block of output.
678         // Two blocks are used instead of one because when decrypting with padding enabled, output
679         // lags behind input by a block because the Cipher doesn't know whether the most recent
680         // input block was supposed to contain padding.
681         update(new byte[getBlockSize() * 2]);
682 
683         assertEquals(null, update(new byte[0]));
684         assertEquals(null, update(new byte[getBlockSize() * 2], getBlockSize(), 0));
685         assertEquals(null, update(new byte[getBlockSize()], 0, 0));
686     }
687 
688     @Test
testUpdateDoesNotProduceOutputWhenInsufficientInput()689     public void testUpdateDoesNotProduceOutputWhenInsufficientInput() throws Exception {
690         if (!TestUtils.isAttestationSupported()) {
691             return;
692         }
693 
694         if (isStreamCipher()) {
695             // Stream ciphers always produce output for non-empty input.
696             return;
697         }
698 
699         // Test encryption
700         createCipher();
701         initKat(Cipher.ENCRYPT_MODE);
702         assertUpdateDoesNotProduceOutputWhenInsufficientInput();
703 
704         // Test decryption
705         createCipher();
706         initKat(Cipher.DECRYPT_MODE);
707         assertUpdateDoesNotProduceOutputWhenInsufficientInput();
708     }
709 
assertUpdateDoesNotProduceOutputWhenInsufficientInput()710     private void assertUpdateDoesNotProduceOutputWhenInsufficientInput() throws Exception {
711         if (getBlockSize() < 8) {
712             fail("This test isn't designed for small block size: " + getBlockSize());
713         }
714 
715         assertEquals(null, update(new byte[1]));
716         assertEquals(null, update(new byte[1], 0, 1));
717         assertEquals(0, update(new byte[1], 0, 1, new byte[getBlockSize()]));
718         assertEquals(0, update(new byte[1], 0, 1, new byte[getBlockSize()], 0));
719         assertEquals(0, update(ByteBuffer.allocate(1), ByteBuffer.allocate(getBlockSize())));
720 
721         // Complete the current block. There are blockSize - 4 bytes left to fill.
722         byte[] output = update(new byte[getBlockSize() - 4]);
723 
724         try {
725             assertEquals(getBlockSize(), output.length);
726         } catch (NullPointerException e) {
727             if (isStrongbox() && output == null) {
728                 if (Build.VERSION_CODES.TIRAMISU
729                         > SystemProperties.getInt("ro.vendor.api_level", 0)) {
730                     // Known broken on some older vendor implementations.
731                     return;
732                 }
733                 fail("b/194134359");
734             }
735             throw e;
736         }
737 
738         assertEquals(null, update(new byte[1]));
739         assertEquals(null, update(new byte[1], 0, 1));
740         assertEquals(0, update(new byte[1], 0, 1, new byte[getBlockSize()]));
741         assertEquals(0, update(new byte[1], 0, 1, new byte[getBlockSize()], 0));
742         assertEquals(0, update(ByteBuffer.allocate(1), ByteBuffer.allocate(getBlockSize())));
743     }
744 
745     @Test
testKatOneShotEncryptUsingDoFinal()746     public void testKatOneShotEncryptUsingDoFinal() throws Exception {
747         createCipher();
748         assertKatOneShotTransformUsingDoFinal(
749                 Cipher.ENCRYPT_MODE, getKatPlaintext(), getKatCiphertext());
750     }
751 
752     @Test
testKatOneShotDecryptUsingDoFinal()753     public void testKatOneShotDecryptUsingDoFinal() throws Exception {
754         createCipher();
755         assertKatOneShotTransformUsingDoFinal(
756                 Cipher.DECRYPT_MODE, getKatCiphertext(), getKatPlaintext());
757     }
758 
assertKatOneShotTransformUsingDoFinal( int opmode, byte[] input, byte[] expectedOutput)759     private void assertKatOneShotTransformUsingDoFinal(
760             int opmode, byte[] input, byte[] expectedOutput) throws Exception {
761         int bufferWithInputInTheMiddleCleartextOffset = 5;
762         byte[] bufferWithInputInTheMiddle = concat(
763                 new byte[bufferWithInputInTheMiddleCleartextOffset],
764                 input,
765                 new byte[4]);
766 
767         initKat(opmode);
768         assertArrayEquals(expectedOutput, doFinal(input));
769         initKat(opmode);
770         assertArrayEquals(expectedOutput, doFinal(input, 0, input.length));
771         initKat(opmode);
772         assertArrayEquals(expectedOutput,
773                 doFinal(bufferWithInputInTheMiddle,
774                         bufferWithInputInTheMiddleCleartextOffset,
775                         input.length));
776 
777         ByteBuffer inputBuffer = ByteBuffer.wrap(
778                 bufferWithInputInTheMiddle,
779                 bufferWithInputInTheMiddleCleartextOffset,
780                 input.length);
781         ByteBuffer actualOutputBuffer = ByteBuffer.allocate(expectedOutput.length);
782         initKat(opmode);
783         assertEquals(expectedOutput.length, doFinal(inputBuffer, actualOutputBuffer));
784         assertEquals(0, inputBuffer.remaining());
785         assertByteBufferEquals(
786                 (ByteBuffer) ByteBuffer.wrap(expectedOutput).position(expectedOutput.length),
787                 actualOutputBuffer);
788     }
789 
790     @Test
testKatEncryptOneByteAtATime()791     public void testKatEncryptOneByteAtATime() throws Exception {
792         if (!TestUtils.isAttestationSupported()) {
793             return;
794         }
795 
796         createCipher();
797         initKat(Cipher.ENCRYPT_MODE);
798         byte[] plaintext = getKatPlaintext();
799         byte[] expectedCiphertext = getKatCiphertext();
800         int blockSize = getBlockSize();
801         if (isStreamCipher()) {
802             ByteArrayOutputStream actualCiphertext = new ByteArrayOutputStream();
803             // Stream cipher -- one byte in, one byte out
804             for (int plaintextIndex = 0; plaintextIndex < plaintext.length; plaintextIndex++) {
805                 byte[] output = update(new byte[] {plaintext[plaintextIndex]});
806                 if (output != null) {
807                     actualCiphertext.write(output);
808                 }
809                 // Some StrongBox implementations cannot support 1:1 input:output lengths, so
810                 // we relax this API restriction for them.
811                 if (!isStrongbox()) {
812                     assertEquals("plaintext index: " + plaintextIndex, 1, output.length);
813                     assertEquals("plaintext index: " + plaintextIndex,
814                             expectedCiphertext[plaintextIndex], output[0]);
815                 }
816             }
817             byte[] finalOutput = doFinal();
818             if (!isStrongbox()) {
819                 byte[] expectedFinalOutput;
820                 if (isAuthenticatedCipher()) {
821                     expectedFinalOutput =
822                             subarray(expectedCiphertext, plaintext.length,
823                                     expectedCiphertext.length);
824                 } else {
825                     expectedFinalOutput = EmptyArray.BYTE;
826                 }
827                 assertArrayEquals(expectedFinalOutput, finalOutput);
828             }
829 
830             // StrongBox doesn't require 1:1 in:out, so just compare the full ciphertext. We perform
831             // this check on non-StrongBox implementations as well to ensure the test logic is
832             // exercised on non-StrongBox platforms.
833             if (finalOutput != null) {
834                 actualCiphertext.write(finalOutput);
835             }
836             assertArrayEquals(expectedCiphertext, actualCiphertext.toByteArray());
837         } else {
838             // Not a stream cipher -- operates on full blocks only.
839 
840             // Assert that a block of output is produced once a full block of input is provided.
841             // Every input block produces an output block.
842             int ciphertextIndex = 0;
843             for (int plaintextIndex = 0; plaintextIndex < plaintext.length; plaintextIndex++) {
844                 byte[] output = update(new byte[] {plaintext[plaintextIndex]});
845                 String additionalInformation = "";
846                 boolean compareOutput = true;
847                 if (isStrongbox()) {
848                     // This is known to be broken on older vendor implementations.
849                     if (Build.VERSION_CODES.TIRAMISU
850                             > SystemProperties.getInt("ro.vendor.api_level", 0)) {
851                         compareOutput = false;
852                     } else {
853                         additionalInformation = " (b/194134359)";
854                     }
855                 }
856                 if (compareOutput) {
857                     if ((plaintextIndex % blockSize) == blockSize - 1) {
858                         // Cipher.update is expected to have output a new block
859                         assertArrayEquals(
860                                 "plaintext index: " + plaintextIndex + additionalInformation,
861                                 subarray(
862                                         expectedCiphertext,
863                                         ciphertextIndex,
864                                         ciphertextIndex + blockSize),
865                                 output);
866                     } else {
867                         // Cipher.update is expected to have produced no output
868                         assertArrayEquals(
869                                 "plaintext index: " + plaintextIndex + additionalInformation,
870                                 null, output);
871                     }
872                 }
873                 if (output != null) {
874                     ciphertextIndex += output.length;
875                 }
876             }
877 
878             byte[] actualFinalOutput = doFinal();
879             byte[] expectedFinalOutput =
880                     subarray(expectedCiphertext, ciphertextIndex, expectedCiphertext.length);
881             assertArrayEquals(expectedFinalOutput, actualFinalOutput);
882         }
883     }
884 
885     @Test
testKatDecryptOneByteAtATime()886     public void testKatDecryptOneByteAtATime() throws Exception {
887         if (!TestUtils.isAttestationSupported()) {
888             return;
889         }
890 
891         createCipher();
892         initKat(Cipher.DECRYPT_MODE);
893         byte[] ciphertext = getKatCiphertext();
894         int plaintextIndex = 0;
895         int blockSize = getBlockSize();
896         byte[] expectedPlaintext = getKatPlaintext();
897         boolean paddingEnabled = isPaddingEnabled();
898         if (isAuthenticatedCipher()) {
899             // Authenticated cipher -- no output until doFinal where ciphertext is authenticated.
900             for (int ciphertextIndex = 0; ciphertextIndex < ciphertext.length; ciphertextIndex++) {
901                 byte[] output = update(new byte[] {ciphertext[ciphertextIndex]});
902                 assertEquals("ciphertext index: " + ciphertextIndex,
903                         0, (output != null) ? output.length : 0);
904             }
905             byte[] finalOutput = doFinal();
906             assertArrayEquals(expectedPlaintext, finalOutput);
907         } else if (isStreamCipher()) {
908             ByteArrayOutputStream actualPlaintext = new ByteArrayOutputStream();
909             // Unauthenticated stream cipher -- one byte in, one byte out
910             for (int ciphertextIndex = 0; ciphertextIndex < ciphertext.length; ciphertextIndex++) {
911                 byte[] output = update(new byte[] {ciphertext[ciphertextIndex]});
912                 if (output != null) {
913                     actualPlaintext.write(output);
914                 }
915                 // Some StrongBox implementations cannot support 1:1 input:output lengths, so
916                 // we relax this API restriction for them.
917                 if (!isStrongbox()) {
918                     assertEquals("ciphertext index: " + ciphertextIndex, 1, output.length);
919                     assertEquals("ciphertext index: " + ciphertextIndex,
920                             expectedPlaintext[ciphertextIndex], output[0]);
921                 }
922             }
923             byte[] finalOutput = doFinal();
924             if (!isStrongbox()) {
925                 assertEquals(0, finalOutput.length);
926             }
927 
928             // StrongBox doesn't require 1:1 in:out, so just compare the full ciphertext. We perform
929             // this check on non-StrongBox implementations as well to ensure the test logic is
930             // exercised on non-StrongBox platforms.
931             if (finalOutput != null) {
932                 actualPlaintext.write(finalOutput);
933             }
934             assertArrayEquals(expectedPlaintext, actualPlaintext.toByteArray());
935         } else {
936             // Unauthenticated block cipher -- operates in full blocks only
937 
938             // Assert that a block of output is produced once a full block of input is provided.
939             // When padding is used, output is produced one input byte later: once the first byte of the
940             // next input block is provided.
941             for (int ciphertextIndex = 0; ciphertextIndex < ciphertext.length; ciphertextIndex++) {
942                 byte[] output = update(new byte[] {ciphertext[ciphertextIndex]});
943                 boolean outputExpected =
944                         ((paddingEnabled)
945                                 && (ciphertextIndex > 0) && ((ciphertextIndex % blockSize) == 0))
946                         || ((!paddingEnabled) && ((ciphertextIndex % blockSize) == blockSize - 1));
947 
948                 String additionalInformation = "";
949                 boolean compareOutput = true;
950                 if (isStrongbox()) {
951                     // This is known to be broken on older vendor implementations.
952                     if (Build.VERSION_CODES.TIRAMISU
953                             > SystemProperties.getInt("ro.vendor.api_level", 0)) {
954                         compareOutput = false;
955                     } else {
956                         additionalInformation = " (b/194134040)";
957                     }
958                 }
959                 if (compareOutput) {
960                     if (outputExpected) {
961                         assertArrayEquals(
962                                 "ciphertext index: " + ciphertextIndex + additionalInformation,
963                                 subarray(expectedPlaintext, plaintextIndex,
964                                     plaintextIndex + blockSize),
965                                 output);
966                     } else {
967                         assertEquals("ciphertext index: " + ciphertextIndex + additionalInformation,
968                                 null, output);
969                     }
970                 }
971 
972                 if (output != null) {
973                     plaintextIndex += output.length;
974                 }
975             }
976 
977             byte[] actualFinalOutput = doFinal();
978             byte[] expectedFinalOutput =
979                     subarray(expectedPlaintext, plaintextIndex, expectedPlaintext.length);
980             assertArrayEquals(expectedFinalOutput, actualFinalOutput);
981         }
982     }
983 
984     @Test
testUpdateAADNotSupported()985     public void testUpdateAADNotSupported() throws Exception {
986         if (isAuthenticatedCipher()) {
987             // Not applicable to authenticated ciphers where updateAAD is supported.
988             return;
989         }
990 
991         createCipher();
992         initKat(Cipher.ENCRYPT_MODE);
993         assertUpdateAADNotSupported();
994 
995         createCipher();
996         initKat(Cipher.DECRYPT_MODE);
997         assertUpdateAADNotSupported();
998     }
999 
1000     @Test
testUpdateAADSupported()1001     public void testUpdateAADSupported() throws Exception {
1002         if (!isAuthenticatedCipher()) {
1003             // Not applicable to unauthenticated ciphers where updateAAD is not supported.
1004             return;
1005         }
1006 
1007         createCipher();
1008         initKat(Cipher.ENCRYPT_MODE);
1009         assertUpdateAADSupported();
1010 
1011         createCipher();
1012         initKat(Cipher.DECRYPT_MODE);
1013         assertUpdateAADSupported();
1014     }
1015 
assertUpdateAADNotSupported()1016     private void assertUpdateAADNotSupported() throws Exception {
1017         try {
1018             mCipher.updateAAD(new byte[getBlockSize()]);
1019             fail();
1020         } catch (UnsupportedOperationException expected) {
1021         } catch (IllegalStateException expected) {}
1022 
1023         try {
1024             mCipher.updateAAD(new byte[getBlockSize()], 0, getBlockSize());
1025             fail();
1026         } catch (UnsupportedOperationException expected) {
1027         } catch (IllegalStateException expected) {}
1028 
1029         try {
1030             mCipher.updateAAD(ByteBuffer.allocate(getBlockSize()));
1031             fail();
1032         } catch (UnsupportedOperationException expected) {
1033         } catch (IllegalStateException expected) {}
1034     }
1035 
assertUpdateAADSupported()1036     private void assertUpdateAADSupported() throws Exception {
1037         mCipher.updateAAD(new byte[getBlockSize()]);
1038         mCipher.updateAAD(new byte[getBlockSize()], 0, getBlockSize());
1039         mCipher.updateAAD(ByteBuffer.allocate(getBlockSize()));
1040     }
1041 
1042     // TODO: Add tests for WRAP and UNWRAP
1043 
1044     @Test
testUpdateAndDoFinalNotSupportedInWrapAndUnwrapModes()1045     public void testUpdateAndDoFinalNotSupportedInWrapAndUnwrapModes() throws Exception {
1046         createCipher();
1047         assertUpdateAndDoFinalThrowIllegalStateExceprtion(
1048                 Cipher.WRAP_MODE, getKey(), getKatAlgorithmParameterSpec());
1049 
1050         createCipher();
1051         assertUpdateAndDoFinalThrowIllegalStateExceprtion(
1052                 Cipher.UNWRAP_MODE, getKey(), getKatAlgorithmParameterSpec());
1053     }
1054 
assertUpdateAndDoFinalThrowIllegalStateExceprtion( int opmode, SecretKey key, AlgorithmParameterSpec paramSpec)1055     private void assertUpdateAndDoFinalThrowIllegalStateExceprtion(
1056             int opmode, SecretKey key, AlgorithmParameterSpec paramSpec)
1057             throws Exception {
1058         try {
1059             init(opmode, key, paramSpec);
1060         } catch (UnsupportedOperationException e) {
1061             // Skip this test because wrap/unwrap is not supported by this Cipher
1062             return;
1063         }
1064 
1065         try {
1066             update(new byte[getBlockSize()]);
1067             fail();
1068         } catch (IllegalStateException expected) {}
1069 
1070         init(opmode, key, paramSpec);
1071         try {
1072             update(new byte[getBlockSize()], 0, getBlockSize());
1073             fail();
1074         } catch (IllegalStateException expected) {}
1075 
1076         init(opmode, key, paramSpec);
1077         try {
1078             update(new byte[getBlockSize()], 0, getBlockSize(), new byte[getBlockSize() * 2]);
1079             fail();
1080         } catch (IllegalStateException expected) {}
1081 
1082         init(opmode, key, paramSpec);
1083         try {
1084             update(new byte[getBlockSize()], 0, getBlockSize(), new byte[getBlockSize() * 2], 0);
1085             fail();
1086         } catch (IllegalStateException expected) {}
1087 
1088         init(opmode, key, paramSpec);
1089         try {
1090             update(ByteBuffer.allocate(getBlockSize()), ByteBuffer.allocate(getBlockSize() * 2));
1091             fail();
1092         } catch (IllegalStateException expected) {}
1093 
1094         init(opmode, key, paramSpec);
1095         try {
1096             doFinal();
1097             fail();
1098         } catch (IllegalStateException expected) {}
1099 
1100         init(opmode, key, paramSpec);
1101         try {
1102             doFinal(new byte[getBlockSize()]);
1103             fail();
1104         } catch (IllegalStateException expected) {}
1105 
1106         init(opmode, key, paramSpec);
1107         try {
1108             doFinal(new byte[getBlockSize()], 0, getBlockSize());
1109             fail();
1110         } catch (IllegalStateException expected) {}
1111 
1112         init(opmode, key, paramSpec);
1113         try {
1114             doFinal(new byte[getBlockSize() * 2], 0);
1115             fail();
1116         } catch (IllegalStateException expected) {}
1117 
1118         init(opmode, key, paramSpec);
1119         try {
1120             doFinal(new byte[getBlockSize()], 0, getBlockSize(), new byte[getBlockSize() * 2]);
1121             fail();
1122         } catch (IllegalStateException expected) {}
1123 
1124         init(opmode, key, paramSpec);
1125         try {
1126             doFinal(new byte[getBlockSize()], 0, getBlockSize(), new byte[getBlockSize() * 2], 0);
1127             fail();
1128         } catch (IllegalStateException expected) {}
1129 
1130         init(opmode, key, paramSpec);
1131         try {
1132             doFinal(ByteBuffer.allocate(getBlockSize()), ByteBuffer.allocate(getBlockSize() * 2));
1133             fail();
1134         } catch (IllegalStateException expected) {}
1135     }
1136 
1137     @Test
testGeneratedPadding()1138     public void testGeneratedPadding() throws Exception {
1139         // Assert that the Cipher under test correctly handles plaintexts of various lengths.
1140         if (isStreamCipher()) {
1141             // Not applicable to stream ciphers
1142             return;
1143         }
1144 
1145         // Encryption of basePlaintext and additional data should result in baseCiphertext and some
1146         // data (some of which may be padding).
1147         int blockSize = getBlockSize();
1148         byte[] basePlaintext = subarray(getKatPlaintext(), 0, blockSize);
1149         byte[] baseCiphertext = subarray(getKatCiphertext(), 0, blockSize);
1150         boolean paddingEnabled = isPaddingEnabled();
1151 
1152         for (int lastInputBlockUnusedByteCount = 0;
1153                 lastInputBlockUnusedByteCount < blockSize;
1154                 lastInputBlockUnusedByteCount++) {
1155             byte[] plaintext = concat(basePlaintext, new byte[lastInputBlockUnusedByteCount]);
1156             createCipher();
1157             initKat(Cipher.ENCRYPT_MODE);
1158 
1159             if ((!paddingEnabled) && ((lastInputBlockUnusedByteCount % blockSize) != 0)) {
1160                 // Without padding, plaintext which does not end with a full block should be
1161                 // rejected.
1162                 try {
1163                     doFinal(plaintext);
1164                     fail();
1165                 } catch (IllegalBlockSizeException expected) {}
1166                 continue;
1167             }
1168             byte[] ciphertext = doFinal(plaintext);
1169 
1170             assertArrayEquals(
1171                     "lastInputBlockUnusedByteCount: " + lastInputBlockUnusedByteCount,
1172                     baseCiphertext,
1173                     subarray(ciphertext, 0, baseCiphertext.length));
1174 
1175             int expectedCiphertextLength = getExpectedCiphertextLength(plaintext.length);
1176             int expectedDecryptedPlaintextLength =
1177                     (paddingEnabled) ? plaintext.length : expectedCiphertextLength;
1178             assertEquals(
1179                     "lastInputBlockUnusedByteCount: " + lastInputBlockUnusedByteCount,
1180                     expectedCiphertextLength,
1181                     ciphertext.length);
1182             initKat(Cipher.DECRYPT_MODE);
1183             byte[] decryptedPlaintext = doFinal(ciphertext);
1184             assertEquals(
1185                     "lastInputBlockUnusedByteCount: " + lastInputBlockUnusedByteCount,
1186                     expectedDecryptedPlaintextLength,
1187                     decryptedPlaintext.length);
1188             assertArrayEquals(
1189                     "lastInputBlockUnusedByteCount: " + lastInputBlockUnusedByteCount,
1190                     basePlaintext,
1191                     subarray(decryptedPlaintext, 0, basePlaintext.length));
1192         }
1193     }
1194 
1195     @Test
testDecryptWithMangledPadding()1196     public void testDecryptWithMangledPadding() throws Exception {
1197         if (!isPaddingEnabled()) {
1198             // Test not applicable when padding not in use
1199             return;
1200         }
1201 
1202         createCipher();
1203         initKat(Cipher.DECRYPT_MODE);
1204         byte[] ciphertext = getKatCiphertext();
1205         // Flip a bit in the last byte of ciphertext -- this should result in the last plaintext
1206         // block getting mangled. In turn, this should result in bad padding.
1207         ciphertext[ciphertext.length - 1] ^= 1;
1208         try {
1209             doFinal(ciphertext);
1210             fail();
1211         } catch (BadPaddingException expected) {}
1212         catch (IllegalBlockSizeException e) {
1213             if (isStrongbox()) {
1214                 fail("Should throw BadPaddingException (b/194126736)");
1215             } else {
1216                 fail();
1217             }
1218         }
1219     }
1220 
1221     @Test
testDecryptWithMissingPadding()1222     public void testDecryptWithMissingPadding() throws Exception {
1223         if (!isPaddingEnabled()) {
1224             // Test not applicable when padding not in use
1225             return;
1226         }
1227 
1228         createCipher();
1229         initKat(Cipher.DECRYPT_MODE);
1230         byte[] ciphertext = subarray(getKatCiphertext(), 0, getBlockSize());
1231         try {
1232             doFinal(ciphertext);
1233             fail();
1234         } catch (BadPaddingException expected) {}
1235         catch (IllegalBlockSizeException e) {
1236             if (isStrongbox()) {
1237                 fail("Should throw BadPaddingException (b/194126736)");
1238             } else {
1239                 fail();
1240             }
1241         }
1242     }
1243 
1244     @Test
testUpdateCopySafe()1245     public void testUpdateCopySafe() throws Exception {
1246         // Assert that when input and output buffers passed to Cipher.update reference the same
1247         // byte array, then no input data is overwritten before it's consumed.
1248         assertUpdateCopySafe(Cipher.ENCRYPT_MODE, 0, 0);
1249         assertUpdateCopySafe(Cipher.ENCRYPT_MODE, 0, 1);
1250         assertUpdateCopySafe(Cipher.ENCRYPT_MODE, 1, 0);
1251         assertUpdateCopySafe(Cipher.ENCRYPT_MODE, 0, getBlockSize() - 1);
1252         assertUpdateCopySafe(Cipher.ENCRYPT_MODE, 0, getBlockSize());
1253         assertUpdateCopySafe(Cipher.ENCRYPT_MODE, 0, getBlockSize() + 1);
1254         assertUpdateCopySafe(Cipher.ENCRYPT_MODE, getBlockSize() * 2 - 1, 0);
1255         assertUpdateCopySafe(Cipher.ENCRYPT_MODE, getBlockSize() * 2, 0);
1256         assertUpdateCopySafe(Cipher.ENCRYPT_MODE, getBlockSize() * 2 + 1, 0);
1257 
1258         assertUpdateCopySafe(Cipher.DECRYPT_MODE, 0, 0);
1259         assertUpdateCopySafe(Cipher.DECRYPT_MODE, 0, 1);
1260         assertUpdateCopySafe(Cipher.DECRYPT_MODE, 1, 0);
1261         assertUpdateCopySafe(Cipher.DECRYPT_MODE, 0, getBlockSize() - 1);
1262         assertUpdateCopySafe(Cipher.DECRYPT_MODE, 0, getBlockSize());
1263         assertUpdateCopySafe(Cipher.DECRYPT_MODE, 0, getBlockSize() + 1);
1264         assertUpdateCopySafe(Cipher.DECRYPT_MODE, getBlockSize() * 2 - 1, 0);
1265         assertUpdateCopySafe(Cipher.DECRYPT_MODE, getBlockSize() * 2, 0);
1266         assertUpdateCopySafe(Cipher.DECRYPT_MODE, getBlockSize() * 2 + 1, 0);
1267     }
1268 
assertUpdateCopySafe( int opmode, int inputOffsetInBuffer, int outputOffsetInBuffer)1269     private void assertUpdateCopySafe(
1270             int opmode, int inputOffsetInBuffer, int outputOffsetInBuffer)
1271             throws Exception {
1272         int blockSize = getBlockSize();
1273         byte[] input;
1274         byte[] expectedOutput;
1275         switch (opmode) {
1276             case Cipher.ENCRYPT_MODE:
1277                 input = getKatPlaintext();
1278                 if (isStreamCipher()) {
1279                     if (isAuthenticatedCipher()) {
1280                         expectedOutput = subarray(getKatCiphertext(), 0, input.length);
1281                     } else {
1282                         expectedOutput = getKatCiphertext();
1283                     }
1284                 } else {
1285                     // Update outputs exactly one block of ciphertext for one block of plaintext,
1286                     // excluding padding.
1287                     expectedOutput = subarray(
1288                             getKatCiphertext(), 0, (input.length / blockSize) * blockSize);
1289                 }
1290                 break;
1291             case Cipher.DECRYPT_MODE:
1292                 input = getKatCiphertext();
1293                 if (isAuthenticatedCipher()) {
1294                     expectedOutput = EmptyArray.BYTE;
1295                 } else if (isStreamCipher()) {
1296                     expectedOutput = getKatPlaintext();
1297                 } else {
1298                     expectedOutput = getKatPlaintext();
1299                     if (isPaddingEnabled()) {
1300                         // When padding is enabled, update will not output the last block of
1301                         // plaintext because it doesn't know whether more ciphertext will be
1302                         // provided.
1303                         expectedOutput = subarray(
1304                                 expectedOutput, 0, ((input.length / blockSize) - 1) * blockSize);
1305                     } else {
1306                         // When no padding is used, one block of ciphertext results in one block of
1307                         // plaintext.
1308                         expectedOutput = subarray(
1309                                 expectedOutput, 0, (input.length - (input.length % blockSize)));
1310                     }
1311                 }
1312                 break;
1313             default:
1314                 throw new AssertionFailedError("Unsupported opmode: " + opmode);
1315         }
1316 
1317         int inputEndIndexInBuffer = inputOffsetInBuffer + input.length;
1318         int outputEndIndexInBuffer = outputOffsetInBuffer + expectedOutput.length;
1319 
1320         assertTrue("StrongBox output assumptions below need input to be at least a block.",
1321                 input.length >= blockSize);
1322 
1323         // Test the update(byte[], int, int, byte[], int) variant
1324         byte[] buffer = new byte[Math.max(inputEndIndexInBuffer, outputEndIndexInBuffer)];
1325         System.arraycopy(input, 0, buffer, inputOffsetInBuffer, input.length);
1326         createCipher();
1327         initKat(opmode);
1328         int outputLength = update(buffer, inputOffsetInBuffer, input.length,
1329                 buffer, outputOffsetInBuffer);
1330         if (isStrongbox()) {
1331             // StrongBox does not have to support one byte of output per byte of input.
1332             assertTrue("output length: " + outputLength,
1333                     outputLength >= blockSize || (expectedOutput.length == 0 && outputLength == 0));
1334             outputEndIndexInBuffer = outputOffsetInBuffer + outputLength;
1335         } else {
1336             assertEquals(expectedOutput.length, outputLength);
1337         }
1338         assertArrayEquals(subarray(expectedOutput, 0, outputLength),
1339                 subarray(buffer, outputOffsetInBuffer, outputEndIndexInBuffer));
1340 
1341         if (outputOffsetInBuffer == 0) {
1342             // We can use the update variant which assumes that output offset is 0.
1343             Arrays.fill(buffer, (byte)0);
1344             System.arraycopy(input, 0, buffer, inputOffsetInBuffer, input.length);
1345             createCipher();
1346             initKat(opmode);
1347             outputLength = update(buffer, inputOffsetInBuffer, input.length, buffer, outputOffsetInBuffer);
1348             if (isStrongbox()) {
1349                 // StrongBox does not have to support one byte of output per byte of input.
1350                 assertTrue("output length: " + outputLength,
1351                         outputLength >= blockSize || (expectedOutput.length == 0 && outputLength == 0));
1352                 outputEndIndexInBuffer = outputOffsetInBuffer + outputLength;
1353             } else {
1354                 assertEquals(expectedOutput.length, outputLength);
1355             }
1356             assertArrayEquals(subarray(expectedOutput, 0, outputLength),
1357                     subarray(buffer, outputOffsetInBuffer, outputEndIndexInBuffer));
1358         }
1359 
1360         // Test the update(ByteBuffer, ByteBuffer) variant
1361         Arrays.fill(buffer, (byte)0);
1362         System.arraycopy(input, 0, buffer, inputOffsetInBuffer, input.length);
1363         ByteBuffer inputBuffer = ByteBuffer.wrap(buffer, inputOffsetInBuffer, input.length);
1364         ByteBuffer outputBuffer =
1365                 ByteBuffer.wrap(buffer, outputOffsetInBuffer, expectedOutput.length);
1366         createCipher();
1367         initKat(opmode);
1368         outputLength = update(inputBuffer, outputBuffer);
1369         if (isStrongbox()) {
1370             // StrongBox does not have to support one byte of output per byte of input.
1371             assertTrue("output length: " + outputLength,
1372                     outputLength >= blockSize || (expectedOutput.length == 0 && outputLength == 0));
1373             outputEndIndexInBuffer = outputOffsetInBuffer + outputLength;
1374         } else {
1375             assertEquals(expectedOutput.length, outputLength);
1376         }
1377         assertArrayEquals(subarray(expectedOutput, 0, outputLength),
1378                 subarray(buffer, outputOffsetInBuffer, outputEndIndexInBuffer));
1379     }
1380 
1381     @Test
testDoFinalCopySafe()1382     public void testDoFinalCopySafe() throws Exception {
1383         // Assert that when input and output buffers passed to Cipher.doFinal reference the same
1384         // byte array, then no input data is overwritten before it's consumed.
1385         assertDoFinalCopySafe(Cipher.ENCRYPT_MODE, 0, 0);
1386         assertDoFinalCopySafe(Cipher.ENCRYPT_MODE, 0, 1);
1387         assertDoFinalCopySafe(Cipher.ENCRYPT_MODE, 1, 0);
1388         assertDoFinalCopySafe(Cipher.ENCRYPT_MODE, 0, getBlockSize() - 1);
1389         assertDoFinalCopySafe(Cipher.ENCRYPT_MODE, 0, getBlockSize());
1390         assertDoFinalCopySafe(Cipher.ENCRYPT_MODE, 0, getBlockSize() + 1);
1391         assertDoFinalCopySafe(Cipher.ENCRYPT_MODE, getBlockSize() * 2 - 1, 0);
1392         assertDoFinalCopySafe(Cipher.ENCRYPT_MODE, getBlockSize() * 2, 0);
1393         assertDoFinalCopySafe(Cipher.ENCRYPT_MODE, getBlockSize() * 2 + 1, 0);
1394 
1395         assertDoFinalCopySafe(Cipher.DECRYPT_MODE, 0, 0);
1396         assertDoFinalCopySafe(Cipher.DECRYPT_MODE, 0, 1);
1397         assertDoFinalCopySafe(Cipher.DECRYPT_MODE, 1, 0);
1398         assertDoFinalCopySafe(Cipher.DECRYPT_MODE, 0, getBlockSize() - 1);
1399         assertDoFinalCopySafe(Cipher.DECRYPT_MODE, 0, getBlockSize());
1400         assertDoFinalCopySafe(Cipher.DECRYPT_MODE, 0, getBlockSize() + 1);
1401         assertDoFinalCopySafe(Cipher.DECRYPT_MODE, getBlockSize() * 2 - 1, 0);
1402         assertDoFinalCopySafe(Cipher.DECRYPT_MODE, getBlockSize() * 2, 0);
1403         assertDoFinalCopySafe(Cipher.DECRYPT_MODE, getBlockSize() * 2 + 1, 0);
1404     }
1405 
assertDoFinalCopySafe( int opmode, int inputOffsetInBuffer, int outputOffsetInBuffer)1406     private void assertDoFinalCopySafe(
1407             int opmode, int inputOffsetInBuffer, int outputOffsetInBuffer)
1408             throws Exception {
1409         byte[] input = getKatInput(opmode);
1410         byte[] expectedOutput = getKatOutput(opmode);
1411 
1412         int inputEndIndexInBuffer = inputOffsetInBuffer + input.length;
1413         int outputEndIndexInBuffer = outputOffsetInBuffer + expectedOutput.length;
1414 
1415         // Test the doFinal(byte[], int, int, byte[], int) variant
1416         byte[] buffer = new byte[Math.max(inputEndIndexInBuffer, outputEndIndexInBuffer)];
1417         System.arraycopy(input, 0, buffer, inputOffsetInBuffer, input.length);
1418         createCipher();
1419         initKat(opmode);
1420         assertEquals(expectedOutput.length,
1421                 doFinal(buffer, inputOffsetInBuffer, input.length,
1422                         buffer, outputOffsetInBuffer));
1423         assertArrayEquals(expectedOutput,
1424                 subarray(buffer, outputOffsetInBuffer, outputEndIndexInBuffer));
1425 
1426         if (outputOffsetInBuffer == 0) {
1427             // We can use the doFinal variant which assumes that output offset is 0.
1428             buffer = new byte[Math.max(inputEndIndexInBuffer, outputEndIndexInBuffer)];
1429             System.arraycopy(input, 0, buffer, inputOffsetInBuffer, input.length);
1430             createCipher();
1431             initKat(opmode);
1432             assertEquals(expectedOutput.length,
1433                     doFinal(buffer, inputOffsetInBuffer, input.length, buffer));
1434             assertArrayEquals(expectedOutput,
1435                     subarray(buffer, outputOffsetInBuffer, outputEndIndexInBuffer));
1436         }
1437 
1438         // Test the doFinal(ByteBuffer, ByteBuffer) variant
1439         buffer = new byte[Math.max(inputEndIndexInBuffer, outputEndIndexInBuffer)];
1440         System.arraycopy(input, 0, buffer, inputOffsetInBuffer, input.length);
1441         ByteBuffer inputBuffer = ByteBuffer.wrap(buffer, inputOffsetInBuffer, input.length);
1442         ByteBuffer outputBuffer =
1443                 ByteBuffer.wrap(buffer, outputOffsetInBuffer, expectedOutput.length);
1444         createCipher();
1445         initKat(opmode);
1446         assertEquals(expectedOutput.length, doFinal(inputBuffer, outputBuffer));
1447         assertArrayEquals(expectedOutput,
1448                 subarray(buffer, outputOffsetInBuffer, outputEndIndexInBuffer));
1449     }
1450 
1451     @Test
testVeryLargeBlock()1452     public void testVeryLargeBlock() throws Exception {
1453         createCipher();
1454         Key key = importKey(getKatKey());
1455         init(Cipher.ENCRYPT_MODE, key, getKatAlgorithmParameterSpec());
1456         byte[] largeMessage = new byte[LARGE_MESSAGE_SIZE];
1457         mRand.nextBytes(largeMessage);
1458         byte[] ciphertext = doFinal(largeMessage);
1459         assertEquals(getExpectedCiphertextLength(LARGE_MESSAGE_SIZE), ciphertext.length);
1460 
1461         init(Cipher.DECRYPT_MODE, key, getKatAlgorithmParameterSpec());
1462         byte[] plaintext = doFinal(ciphertext);
1463         assertTrue(Arrays.equals(largeMessage, plaintext));
1464     }
1465 
createCipher()1466     protected void createCipher() throws NoSuchAlgorithmException,
1467             NoSuchPaddingException, NoSuchProviderException  {
1468         mCipher = Cipher.getInstance(getTransformation(), EXPECTED_PROVIDER_NAME);
1469     }
1470 
getKeyAlgorithm()1471     private String getKeyAlgorithm() {
1472         String transformation = getTransformation();
1473         int delimiterIndex = transformation.indexOf('/');
1474         if (delimiterIndex == -1) {
1475             fail("Unexpected transformation: " + transformation);
1476         }
1477         return transformation.substring(0, delimiterIndex);
1478     }
1479 
getBlockMode()1480     private String getBlockMode() {
1481         String transformation = getTransformation();
1482         int delimiterIndex = transformation.indexOf('/');
1483         if (delimiterIndex == -1) {
1484             fail("Unexpected transformation: " + transformation);
1485         }
1486         int nextDelimiterIndex = transformation.indexOf('/', delimiterIndex + 1);
1487         if (nextDelimiterIndex == -1) {
1488             fail("Unexpected transformation: " + transformation);
1489         }
1490         return transformation.substring(delimiterIndex + 1, nextDelimiterIndex);
1491     }
1492 
getPadding()1493     private String getPadding() {
1494         String transformation = getTransformation();
1495         int delimiterIndex = transformation.indexOf('/');
1496         if (delimiterIndex == -1) {
1497             fail("Unexpected transformation: " + transformation);
1498         }
1499         int nextDelimiterIndex = transformation.indexOf('/', delimiterIndex + 1);
1500         if (nextDelimiterIndex == -1) {
1501             fail("Unexpected transformation: " + transformation);
1502         }
1503         return transformation.substring(nextDelimiterIndex + 1);
1504     }
1505 
getKey()1506     private SecretKey getKey() {
1507         return importKey(getKatKey());
1508     }
1509 
importKey(byte[] keyMaterial)1510     protected SecretKey importKey(byte[] keyMaterial) {
1511         try {
1512             int keyId = mNextKeyId++;
1513             String keyAlias = "key" + keyId;
1514             mAndroidKeyStore.setEntry(
1515                     keyAlias,
1516                     new KeyStore.SecretKeyEntry(new SecretKeySpec(keyMaterial, getKeyAlgorithm())),
1517                     new KeyProtection.Builder(
1518                             KeyProperties.PURPOSE_ENCRYPT
1519                                     | KeyProperties.PURPOSE_DECRYPT)
1520                             .setBlockModes(getBlockMode())
1521                             .setEncryptionPaddings(getPadding())
1522                             .setRandomizedEncryptionRequired(false)
1523                             .setIsStrongBoxBacked(isStrongbox())
1524                             .build());
1525             return (SecretKey) mAndroidKeyStore.getKey(keyAlias, null);
1526         } catch (Exception e) {
1527             throw new RuntimeException("Failed to import key into AndroidKeyStore", e);
1528         }
1529     }
1530 
isPaddingEnabled()1531     private boolean isPaddingEnabled() {
1532         return !getTransformation().toLowerCase(Locale.US).endsWith("/nopadding");
1533     }
1534 
getExpectedCiphertextLength(int plaintextLength)1535     private int getExpectedCiphertextLength(int plaintextLength) {
1536         int authTagLength = 0;
1537         if (isAuthenticatedCipher()) {
1538             authTagLength = getKatAuthenticationTagLengthBytes();
1539         }
1540 
1541         int blockSize = getBlockSize();
1542         if (isStreamCipher()) {
1543             // Padding not supported for stream ciphers
1544             assertFalse(isPaddingEnabled());
1545             return plaintextLength + authTagLength;
1546         } else {
1547             if (isPaddingEnabled()) {
1548                 return ((plaintextLength / blockSize) + 1) * blockSize + authTagLength;
1549             } else {
1550                 return ((plaintextLength + blockSize - 1) / blockSize) * blockSize + authTagLength;
1551             }
1552         }
1553     }
1554 
initKat(int opmode)1555     protected void initKat(int opmode)
1556             throws InvalidKeyException, InvalidAlgorithmParameterException {
1557         init(opmode, getKey(), getKatAlgorithmParameterSpec());
1558     }
1559 
init(int opmode, Key key, AlgorithmParameters spec)1560     protected void init(int opmode, Key key, AlgorithmParameters spec)
1561             throws InvalidKeyException, InvalidAlgorithmParameterException {
1562         mCipher.init(opmode, key, spec);
1563         mOpmode = opmode;
1564     }
1565 
init(int opmode, Key key, AlgorithmParameters spec, SecureRandom random)1566     protected void init(int opmode, Key key, AlgorithmParameters spec, SecureRandom random)
1567             throws InvalidKeyException, InvalidAlgorithmParameterException {
1568         mCipher.init(opmode, key, spec, random);
1569         mOpmode = opmode;
1570     }
1571 
init(int opmode, Key key, AlgorithmParameterSpec spec)1572     protected void init(int opmode, Key key, AlgorithmParameterSpec spec)
1573             throws InvalidKeyException, InvalidAlgorithmParameterException {
1574         mCipher.init(opmode, key, spec);
1575         mOpmode = opmode;
1576     }
1577 
init(int opmode, Key key, AlgorithmParameterSpec spec, SecureRandom random)1578     protected void init(int opmode, Key key, AlgorithmParameterSpec spec, SecureRandom random)
1579             throws InvalidKeyException, InvalidAlgorithmParameterException {
1580         mCipher.init(opmode, key, spec, random);
1581         mOpmode = opmode;
1582     }
1583 
init(int opmode, Key key)1584     protected void init(int opmode, Key key) throws InvalidKeyException {
1585         mCipher.init(opmode, key);
1586         mOpmode = opmode;
1587     }
1588 
init(int opmode, Key key, SecureRandom random)1589     protected void init(int opmode, Key key, SecureRandom random) throws InvalidKeyException {
1590         mCipher.init(opmode, key, random);
1591         mOpmode = opmode;
1592     }
1593 
doFinal()1594     protected byte[] doFinal() throws IllegalBlockSizeException, BadPaddingException {
1595         return mCipher.doFinal();
1596     }
1597 
doFinal(byte[] input)1598     protected byte[] doFinal(byte[] input) throws IllegalBlockSizeException, BadPaddingException {
1599         return mCipher.doFinal(input);
1600     }
1601 
doFinal(byte[] input, int inputOffset, int inputLen)1602     protected byte[] doFinal(byte[] input, int inputOffset, int inputLen)
1603             throws IllegalBlockSizeException, BadPaddingException {
1604         return mCipher.doFinal(input, inputOffset, inputLen);
1605     }
1606 
doFinal(byte[] input, int inputOffset, int inputLen, byte[] output)1607     protected int doFinal(byte[] input, int inputOffset, int inputLen, byte[] output)
1608             throws ShortBufferException, IllegalBlockSizeException, BadPaddingException {
1609         return mCipher.doFinal(input, inputOffset, inputLen, output);
1610     }
1611 
doFinal(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset)1612     protected int doFinal(byte[] input, int inputOffset, int inputLen, byte[] output,
1613             int outputOffset) throws ShortBufferException, IllegalBlockSizeException,
1614             BadPaddingException {
1615         return mCipher.doFinal(input, inputOffset, inputLen, output, outputOffset);
1616     }
1617 
doFinal(byte[] output, int outputOffset)1618     protected int doFinal(byte[] output, int outputOffset) throws IllegalBlockSizeException,
1619             ShortBufferException, BadPaddingException {
1620         return mCipher.doFinal(output, outputOffset);
1621     }
1622 
doFinal(ByteBuffer input, ByteBuffer output)1623     protected int doFinal(ByteBuffer input, ByteBuffer output) throws ShortBufferException,
1624             IllegalBlockSizeException, BadPaddingException {
1625         return mCipher.doFinal(input, output);
1626     }
1627 
isEncrypting()1628     private boolean isEncrypting() {
1629         return (mOpmode == Cipher.ENCRYPT_MODE) || (mOpmode == Cipher.WRAP_MODE);
1630     }
1631 
assertUpdateOutputSize(int inputLength, int outputLength)1632     private void assertUpdateOutputSize(int inputLength, int outputLength) {
1633         if ((isAuthenticatedCipher()) && (!isEncrypting())) {
1634             assertEquals("Output of update must be empty for authenticated cipher when decrypting",
1635                     0, outputLength);
1636             return;
1637         }
1638 
1639         if (isStreamCipher()) {
1640             // Some StrongBox implementations cannot support 1:1 input:output lengths, so
1641             // we relax this API restriction for them.
1642             if (outputLength != inputLength && !isStrongbox()) {
1643                 fail("Output of update (" + outputLength + ") not same size as input ("
1644                         + inputLength + ")");
1645             }
1646         } else {
1647             if ((outputLength % getBlockSize()) != 0) {
1648                 fail("Output of update (" + outputLength + ") not a multiple of block size ("
1649                         + getBlockSize() + ")");
1650             }
1651         }
1652     }
1653 
update(byte[] input)1654     protected byte[] update(byte[] input) {
1655         byte[] output = mCipher.update(input);
1656         assertUpdateOutputSize(
1657                 (input != null) ? input.length : 0, (output != null) ? output.length : 0);
1658         return output;
1659     }
1660 
update(byte[] input, int offset, int len)1661     protected byte[] update(byte[] input, int offset, int len) {
1662         byte[] output = mCipher.update(input, offset, len);
1663         assertUpdateOutputSize(len, (output != null) ? output.length : 0);
1664 
1665         return output;
1666     }
1667 
update(byte[] input, int offset, int len, byte[] output)1668     protected int update(byte[] input, int offset, int len, byte[] output)
1669             throws ShortBufferException {
1670         int outputLen = mCipher.update(input, offset, len, output);
1671         assertUpdateOutputSize(len, outputLen);
1672 
1673         return outputLen;
1674     }
1675 
update(byte[] input, int offset, int len, byte[] output, int outputOffset)1676     protected int update(byte[] input, int offset, int len, byte[] output, int outputOffset)
1677             throws ShortBufferException {
1678         int outputLen = mCipher.update(input, offset, len, output, outputOffset);
1679         assertUpdateOutputSize(len, outputLen);
1680 
1681         return outputLen;
1682     }
1683 
update(ByteBuffer input, ByteBuffer output)1684     protected int update(ByteBuffer input, ByteBuffer output) throws ShortBufferException {
1685         int inputLimitBefore = input.limit();
1686         int outputLimitBefore = output.limit();
1687         int inputLen = input.remaining();
1688         int outputPosBefore = output.position();
1689 
1690         int outputLen = mCipher.update(input, output);
1691 
1692         assertUpdateOutputSize(inputLen, outputLen);
1693         assertEquals(inputLimitBefore, input.limit());
1694         assertEquals(input.limit(), input.position());
1695 
1696         assertEquals(outputLimitBefore, output.limit());
1697         assertEquals(outputPosBefore + outputLen, output.position());
1698 
1699         return outputLen;
1700     }
1701 
updateAAD(byte[] input)1702     protected void updateAAD(byte[] input) {
1703         mCipher.updateAAD(input);
1704     }
1705 
updateAAD(byte[] input, int offset, int len)1706     protected void updateAAD(byte[] input, int offset, int len) {
1707         mCipher.updateAAD(input, offset, len);
1708     }
1709 
updateAAD(ByteBuffer input)1710     protected void updateAAD(ByteBuffer input) {
1711         mCipher.updateAAD(input);
1712     }
1713 
1714     /**
1715      * Asserts that the position, limit, and capacity of the provided buffers are the same, and that
1716      * their contents (from position {@code 0} to capacity) are the same.
1717      */
assertByteBufferEquals(ByteBuffer expected, ByteBuffer actual)1718     protected static void assertByteBufferEquals(ByteBuffer expected, ByteBuffer actual) {
1719         if (expected == null) {
1720             if (actual == null) {
1721                 return;
1722             } else {
1723                 fail("Expected: null, actual: " + bufferToString(actual));
1724             }
1725         } else {
1726             if (actual == null) {
1727                 fail("Expected: " + bufferToString(expected) + ", actual: null");
1728             } else {
1729                 if ((expected.capacity() != actual.capacity())
1730                         || (expected.position() != actual.position())
1731                         || (expected.limit() != actual.limit())
1732                         || (!equals(expected.array(), expected.arrayOffset(), expected.capacity(),
1733                                     actual.array(), actual.arrayOffset(), actual.capacity()))) {
1734                     fail("Expected: " + bufferToString(expected)
1735                             + ", actual: " + bufferToString(actual));
1736                 }
1737             }
1738         }
1739     }
1740 
bufferToString(ByteBuffer buffer)1741     private static String bufferToString(ByteBuffer buffer) {
1742         return "ByteBuffer[pos: " + buffer.position() + ", limit: " + buffer.limit()
1743                 + ", capacity: " + buffer.capacity()
1744                 + ", backing array: " + HexEncoding.encode(
1745                         buffer.array(), buffer.arrayOffset(), buffer.capacity()) + "]";
1746     }
1747 
equals(byte[] arr1, int offset1, int len1, byte[] arr2, int offset2, int len2)1748     protected static boolean equals(byte[] arr1, int offset1, int len1, byte[] arr2, int offset2,
1749             int len2) {
1750         if (arr1 == null) {
1751             return (arr2 == null);
1752         } else if (arr2 == null) {
1753             return (arr1 == null);
1754         } else {
1755             if (len1 != len2) {
1756                 return false;
1757             }
1758             for (int i = 0; i < len1; i++) {
1759                 if (arr1[i + offset1] != arr2[i + offset2]) {
1760                     return false;
1761                 }
1762             }
1763             return true;
1764         }
1765     }
1766 
subarray(byte[] array, int beginIndex, int endIndex)1767     protected static byte[] subarray(byte[] array, int beginIndex, int endIndex) {
1768         byte[] result = new byte[endIndex - beginIndex];
1769         System.arraycopy(array, beginIndex, result, 0, result.length);
1770         return result;
1771     }
1772 
concat(byte[]... arrays)1773     protected static byte[] concat(byte[]... arrays) {
1774         int resultLength = 0;
1775         for (byte[] array : arrays) {
1776             resultLength += (array != null) ? array.length : 0;
1777         }
1778 
1779         byte[] result = new byte[resultLength];
1780         int resultOffset = 0;
1781         for (byte[] array : arrays) {
1782             if (array != null) {
1783                 System.arraycopy(array, 0, result, resultOffset, array.length);
1784                 resultOffset += array.length;
1785             }
1786         }
1787         return result;
1788     }
1789 
assertInitRejectsIvParameterSpec(byte[] iv)1790     protected final void assertInitRejectsIvParameterSpec(byte[] iv) throws Exception {
1791         Key key = importKey(getKatKey());
1792         createCipher();
1793         IvParameterSpec spec = new IvParameterSpec(iv);
1794         try {
1795             init(Cipher.ENCRYPT_MODE, key, spec);
1796             fail();
1797         } catch (InvalidAlgorithmParameterException expected) {}
1798 
1799         try {
1800             init(Cipher.WRAP_MODE, key, spec);
1801             fail();
1802         } catch (InvalidAlgorithmParameterException expected) {}
1803 
1804         try {
1805             init(Cipher.DECRYPT_MODE, key, spec);
1806             fail();
1807         } catch (InvalidAlgorithmParameterException expected) {}
1808 
1809         try {
1810             init(Cipher.UNWRAP_MODE, key, spec);
1811             fail();
1812         } catch (InvalidAlgorithmParameterException expected) {}
1813 
1814         AlgorithmParameters param = AlgorithmParameters.getInstance("AES");
1815         param.init(new IvParameterSpec(iv));
1816         try {
1817             init(Cipher.ENCRYPT_MODE, key, param);
1818             fail();
1819         } catch (InvalidAlgorithmParameterException expected) {}
1820 
1821         try {
1822             init(Cipher.WRAP_MODE, key, param);
1823             fail();
1824         } catch (InvalidAlgorithmParameterException expected) {}
1825 
1826         try {
1827             init(Cipher.DECRYPT_MODE, key, param);
1828             fail();
1829         } catch (InvalidAlgorithmParameterException expected) {}
1830 
1831         try {
1832             init(Cipher.UNWRAP_MODE, key, param);
1833             fail();
1834         } catch (InvalidAlgorithmParameterException expected) {}
1835     }
1836 }
1837