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