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