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