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.security.keystore; 18 19 import android.annotation.CallSuper; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.os.IBinder; 23 import android.security.KeyStore; 24 import android.security.KeyStoreException; 25 import android.security.keymaster.KeymasterArguments; 26 import android.security.keymaster.KeymasterDefs; 27 import android.security.keymaster.OperationResult; 28 29 import libcore.util.EmptyArray; 30 31 import java.nio.ByteBuffer; 32 import java.security.AlgorithmParameters; 33 import java.security.GeneralSecurityException; 34 import java.security.InvalidAlgorithmParameterException; 35 import java.security.InvalidKeyException; 36 import java.security.InvalidParameterException; 37 import java.security.Key; 38 import java.security.KeyFactory; 39 import java.security.NoSuchAlgorithmException; 40 import java.security.PrivateKey; 41 import java.security.ProviderException; 42 import java.security.PublicKey; 43 import java.security.SecureRandom; 44 import java.security.spec.AlgorithmParameterSpec; 45 import java.security.spec.InvalidKeySpecException; 46 import java.security.spec.PKCS8EncodedKeySpec; 47 import java.security.spec.X509EncodedKeySpec; 48 49 import javax.crypto.AEADBadTagException; 50 import javax.crypto.BadPaddingException; 51 import javax.crypto.Cipher; 52 import javax.crypto.CipherSpi; 53 import javax.crypto.IllegalBlockSizeException; 54 import javax.crypto.NoSuchPaddingException; 55 import javax.crypto.SecretKey; 56 import javax.crypto.SecretKeyFactory; 57 import javax.crypto.ShortBufferException; 58 import javax.crypto.spec.SecretKeySpec; 59 60 /** 61 * Base class for {@link CipherSpi} implementations of Android KeyStore backed ciphers. 62 * 63 * @hide 64 */ 65 abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStoreCryptoOperation { 66 private final KeyStore mKeyStore; 67 68 // Fields below are populated by Cipher.init and KeyStore.begin and should be preserved after 69 // doFinal finishes. 70 private boolean mEncrypting; 71 private int mKeymasterPurposeOverride = -1; 72 private AndroidKeyStoreKey mKey; 73 private SecureRandom mRng; 74 75 /** 76 * Token referencing this operation inside keystore service. It is initialized by 77 * {@code engineInit} and is invalidated when {@code engineDoFinal} succeeds and on some error 78 * conditions in between. 79 */ 80 private IBinder mOperationToken; 81 private long mOperationHandle; 82 private KeyStoreCryptoOperationStreamer mMainDataStreamer; 83 private KeyStoreCryptoOperationStreamer mAdditionalAuthenticationDataStreamer; 84 private boolean mAdditionalAuthenticationDataStreamerClosed; 85 86 /** 87 * Encountered exception which could not be immediately thrown because it was encountered inside 88 * a method that does not throw checked exception. This exception will be thrown from 89 * {@code engineDoFinal}. Once such an exception is encountered, {@code engineUpdate} and 90 * {@code engineDoFinal} start ignoring input data. 91 */ 92 private Exception mCachedException; 93 AndroidKeyStoreCipherSpiBase()94 AndroidKeyStoreCipherSpiBase() { 95 mKeyStore = KeyStore.getInstance(); 96 } 97 98 @Override engineInit(int opmode, Key key, SecureRandom random)99 protected final void engineInit(int opmode, Key key, SecureRandom random) 100 throws InvalidKeyException { 101 resetAll(); 102 103 boolean success = false; 104 try { 105 init(opmode, key, random); 106 initAlgorithmSpecificParameters(); 107 try { 108 ensureKeystoreOperationInitialized(); 109 } catch (InvalidAlgorithmParameterException e) { 110 throw new InvalidKeyException(e); 111 } 112 success = true; 113 } finally { 114 if (!success) { 115 resetAll(); 116 } 117 } 118 } 119 120 @Override engineInit(int opmode, Key key, AlgorithmParameters params, SecureRandom random)121 protected final void engineInit(int opmode, Key key, AlgorithmParameters params, 122 SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { 123 resetAll(); 124 125 boolean success = false; 126 try { 127 init(opmode, key, random); 128 initAlgorithmSpecificParameters(params); 129 ensureKeystoreOperationInitialized(); 130 success = true; 131 } finally { 132 if (!success) { 133 resetAll(); 134 } 135 } 136 } 137 138 @Override engineInit(int opmode, Key key, AlgorithmParameterSpec params, SecureRandom random)139 protected final void engineInit(int opmode, Key key, AlgorithmParameterSpec params, 140 SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { 141 resetAll(); 142 143 boolean success = false; 144 try { 145 init(opmode, key, random); 146 initAlgorithmSpecificParameters(params); 147 ensureKeystoreOperationInitialized(); 148 success = true; 149 } finally { 150 if (!success) { 151 resetAll(); 152 } 153 } 154 } 155 init(int opmode, Key key, SecureRandom random)156 private void init(int opmode, Key key, SecureRandom random) throws InvalidKeyException { 157 switch (opmode) { 158 case Cipher.ENCRYPT_MODE: 159 case Cipher.WRAP_MODE: 160 mEncrypting = true; 161 break; 162 case Cipher.DECRYPT_MODE: 163 case Cipher.UNWRAP_MODE: 164 mEncrypting = false; 165 break; 166 default: 167 throw new InvalidParameterException("Unsupported opmode: " + opmode); 168 } 169 initKey(opmode, key); 170 if (mKey == null) { 171 throw new ProviderException("initKey did not initialize the key"); 172 } 173 mRng = random; 174 } 175 176 /** 177 * Resets this cipher to its pristine pre-init state. This must be equivalent to obtaining a new 178 * cipher instance. 179 * 180 * <p>Subclasses storing additional state should override this method, reset the additional 181 * state, and then chain to superclass. 182 */ 183 @CallSuper resetAll()184 protected void resetAll() { 185 IBinder operationToken = mOperationToken; 186 if (operationToken != null) { 187 mKeyStore.abort(operationToken); 188 } 189 mEncrypting = false; 190 mKeymasterPurposeOverride = -1; 191 mKey = null; 192 mRng = null; 193 mOperationToken = null; 194 mOperationHandle = 0; 195 mMainDataStreamer = null; 196 mAdditionalAuthenticationDataStreamer = null; 197 mAdditionalAuthenticationDataStreamerClosed = false; 198 mCachedException = null; 199 } 200 201 /** 202 * Resets this cipher while preserving the initialized state. This must be equivalent to 203 * rolling back the cipher's state to just after the most recent {@code engineInit} completed 204 * successfully. 205 * 206 * <p>Subclasses storing additional post-init state should override this method, reset the 207 * additional state, and then chain to superclass. 208 */ 209 @CallSuper resetWhilePreservingInitState()210 protected void resetWhilePreservingInitState() { 211 IBinder operationToken = mOperationToken; 212 if (operationToken != null) { 213 mKeyStore.abort(operationToken); 214 } 215 mOperationToken = null; 216 mOperationHandle = 0; 217 mMainDataStreamer = null; 218 mAdditionalAuthenticationDataStreamer = null; 219 mAdditionalAuthenticationDataStreamerClosed = false; 220 mCachedException = null; 221 } 222 ensureKeystoreOperationInitialized()223 private void ensureKeystoreOperationInitialized() throws InvalidKeyException, 224 InvalidAlgorithmParameterException { 225 if (mMainDataStreamer != null) { 226 return; 227 } 228 if (mCachedException != null) { 229 return; 230 } 231 if (mKey == null) { 232 throw new IllegalStateException("Not initialized"); 233 } 234 235 KeymasterArguments keymasterInputArgs = new KeymasterArguments(); 236 addAlgorithmSpecificParametersToBegin(keymasterInputArgs); 237 byte[] additionalEntropy = KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng( 238 mRng, getAdditionalEntropyAmountForBegin()); 239 240 int purpose; 241 if (mKeymasterPurposeOverride != -1) { 242 purpose = mKeymasterPurposeOverride; 243 } else { 244 purpose = mEncrypting 245 ? KeymasterDefs.KM_PURPOSE_ENCRYPT : KeymasterDefs.KM_PURPOSE_DECRYPT; 246 } 247 OperationResult opResult = mKeyStore.begin( 248 mKey.getAlias(), 249 purpose, 250 true, // permit aborting this operation if keystore runs out of resources 251 keymasterInputArgs, 252 additionalEntropy); 253 if (opResult == null) { 254 throw new KeyStoreConnectException(); 255 } 256 257 // Store operation token and handle regardless of the error code returned by KeyStore to 258 // ensure that the operation gets aborted immediately if the code below throws an exception. 259 mOperationToken = opResult.token; 260 mOperationHandle = opResult.operationHandle; 261 262 // If necessary, throw an exception due to KeyStore operation having failed. 263 GeneralSecurityException e = KeyStoreCryptoOperationUtils.getExceptionForCipherInit( 264 mKeyStore, mKey, opResult.resultCode); 265 if (e != null) { 266 if (e instanceof InvalidKeyException) { 267 throw (InvalidKeyException) e; 268 } else if (e instanceof InvalidAlgorithmParameterException) { 269 throw (InvalidAlgorithmParameterException) e; 270 } else { 271 throw new ProviderException("Unexpected exception type", e); 272 } 273 } 274 275 if (mOperationToken == null) { 276 throw new ProviderException("Keystore returned null operation token"); 277 } 278 if (mOperationHandle == 0) { 279 throw new ProviderException("Keystore returned invalid operation handle"); 280 } 281 282 loadAlgorithmSpecificParametersFromBeginResult(opResult.outParams); 283 mMainDataStreamer = createMainDataStreamer(mKeyStore, opResult.token); 284 mAdditionalAuthenticationDataStreamer = 285 createAdditionalAuthenticationDataStreamer(mKeyStore, opResult.token); 286 mAdditionalAuthenticationDataStreamerClosed = false; 287 } 288 289 /** 290 * Creates a streamer which sends plaintext/ciphertext into the provided KeyStore and receives 291 * the corresponding ciphertext/plaintext from the KeyStore. 292 * 293 * <p>This implementation returns a working streamer. 294 */ 295 @NonNull createMainDataStreamer( KeyStore keyStore, IBinder operationToken)296 protected KeyStoreCryptoOperationStreamer createMainDataStreamer( 297 KeyStore keyStore, IBinder operationToken) { 298 return new KeyStoreCryptoOperationChunkedStreamer( 299 new KeyStoreCryptoOperationChunkedStreamer.MainDataStream( 300 keyStore, operationToken)); 301 } 302 303 /** 304 * Creates a streamer which sends Additional Authentication Data (AAD) into the KeyStore. 305 * 306 * <p>This implementation returns {@code null}. 307 * 308 * @returns stream or {@code null} if AAD is not supported by this cipher. 309 */ 310 @Nullable createAdditionalAuthenticationDataStreamer( @uppressWarnings"unused") KeyStore keyStore, @SuppressWarnings("unused") IBinder operationToken)311 protected KeyStoreCryptoOperationStreamer createAdditionalAuthenticationDataStreamer( 312 @SuppressWarnings("unused") KeyStore keyStore, 313 @SuppressWarnings("unused") IBinder operationToken) { 314 return null; 315 } 316 317 @Override engineUpdate(byte[] input, int inputOffset, int inputLen)318 protected final byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) { 319 if (mCachedException != null) { 320 return null; 321 } 322 try { 323 ensureKeystoreOperationInitialized(); 324 } catch (InvalidKeyException | InvalidAlgorithmParameterException e) { 325 mCachedException = e; 326 return null; 327 } 328 329 if (inputLen == 0) { 330 return null; 331 } 332 333 byte[] output; 334 try { 335 flushAAD(); 336 output = mMainDataStreamer.update(input, inputOffset, inputLen); 337 } catch (KeyStoreException e) { 338 mCachedException = e; 339 return null; 340 } 341 342 if (output.length == 0) { 343 return null; 344 } 345 346 return output; 347 } 348 flushAAD()349 private void flushAAD() throws KeyStoreException { 350 if ((mAdditionalAuthenticationDataStreamer != null) 351 && (!mAdditionalAuthenticationDataStreamerClosed)) { 352 byte[] output; 353 try { 354 output = mAdditionalAuthenticationDataStreamer.doFinal( 355 EmptyArray.BYTE, 0, 0, 356 null, // no signature 357 null // no additional entropy needed flushing AAD 358 ); 359 } finally { 360 mAdditionalAuthenticationDataStreamerClosed = true; 361 } 362 if ((output != null) && (output.length > 0)) { 363 throw new ProviderException( 364 "AAD update unexpectedly returned data: " + output.length + " bytes"); 365 } 366 } 367 } 368 369 @Override engineUpdate(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset)370 protected final int engineUpdate(byte[] input, int inputOffset, int inputLen, byte[] output, 371 int outputOffset) throws ShortBufferException { 372 byte[] outputCopy = engineUpdate(input, inputOffset, inputLen); 373 if (outputCopy == null) { 374 return 0; 375 } 376 int outputAvailable = output.length - outputOffset; 377 if (outputCopy.length > outputAvailable) { 378 throw new ShortBufferException("Output buffer too short. Produced: " 379 + outputCopy.length + ", available: " + outputAvailable); 380 } 381 System.arraycopy(outputCopy, 0, output, outputOffset, outputCopy.length); 382 return outputCopy.length; 383 } 384 385 @Override engineUpdate(ByteBuffer input, ByteBuffer output)386 protected final int engineUpdate(ByteBuffer input, ByteBuffer output) 387 throws ShortBufferException { 388 return super.engineUpdate(input, output); 389 } 390 391 @Override engineUpdateAAD(byte[] input, int inputOffset, int inputLen)392 protected final void engineUpdateAAD(byte[] input, int inputOffset, int inputLen) { 393 if (mCachedException != null) { 394 return; 395 } 396 397 try { 398 ensureKeystoreOperationInitialized(); 399 } catch (InvalidKeyException | InvalidAlgorithmParameterException e) { 400 mCachedException = e; 401 return; 402 } 403 404 if (mAdditionalAuthenticationDataStreamerClosed) { 405 throw new IllegalStateException( 406 "AAD can only be provided before Cipher.update is invoked"); 407 } 408 409 if (mAdditionalAuthenticationDataStreamer == null) { 410 throw new IllegalStateException("This cipher does not support AAD"); 411 } 412 413 byte[] output; 414 try { 415 output = mAdditionalAuthenticationDataStreamer.update(input, inputOffset, inputLen); 416 } catch (KeyStoreException e) { 417 mCachedException = e; 418 return; 419 } 420 421 if ((output != null) && (output.length > 0)) { 422 throw new ProviderException("AAD update unexpectedly produced output: " 423 + output.length + " bytes"); 424 } 425 } 426 427 @Override engineUpdateAAD(ByteBuffer src)428 protected final void engineUpdateAAD(ByteBuffer src) { 429 if (src == null) { 430 throw new IllegalArgumentException("src == null"); 431 } 432 if (!src.hasRemaining()) { 433 return; 434 } 435 436 byte[] input; 437 int inputOffset; 438 int inputLen; 439 if (src.hasArray()) { 440 input = src.array(); 441 inputOffset = src.arrayOffset() + src.position(); 442 inputLen = src.remaining(); 443 src.position(src.limit()); 444 } else { 445 input = new byte[src.remaining()]; 446 inputOffset = 0; 447 inputLen = input.length; 448 src.get(input); 449 } 450 engineUpdateAAD(input, inputOffset, inputLen); 451 } 452 453 @Override engineDoFinal(byte[] input, int inputOffset, int inputLen)454 protected final byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen) 455 throws IllegalBlockSizeException, BadPaddingException { 456 if (mCachedException != null) { 457 throw (IllegalBlockSizeException) 458 new IllegalBlockSizeException().initCause(mCachedException); 459 } 460 461 try { 462 ensureKeystoreOperationInitialized(); 463 } catch (InvalidKeyException | InvalidAlgorithmParameterException e) { 464 throw (IllegalBlockSizeException) new IllegalBlockSizeException().initCause(e); 465 } 466 467 byte[] output; 468 try { 469 flushAAD(); 470 byte[] additionalEntropy = 471 KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng( 472 mRng, getAdditionalEntropyAmountForFinish()); 473 output = mMainDataStreamer.doFinal( 474 input, inputOffset, inputLen, 475 null, // no signature involved 476 additionalEntropy); 477 } catch (KeyStoreException e) { 478 switch (e.getErrorCode()) { 479 case KeymasterDefs.KM_ERROR_INVALID_INPUT_LENGTH: 480 throw (IllegalBlockSizeException) new IllegalBlockSizeException().initCause(e); 481 case KeymasterDefs.KM_ERROR_INVALID_ARGUMENT: 482 throw (BadPaddingException) new BadPaddingException().initCause(e); 483 case KeymasterDefs.KM_ERROR_VERIFICATION_FAILED: 484 throw (AEADBadTagException) new AEADBadTagException().initCause(e); 485 default: 486 throw (IllegalBlockSizeException) new IllegalBlockSizeException().initCause(e); 487 } 488 } 489 490 resetWhilePreservingInitState(); 491 return output; 492 } 493 494 @Override engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset)495 protected final int engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[] output, 496 int outputOffset) throws ShortBufferException, IllegalBlockSizeException, 497 BadPaddingException { 498 byte[] outputCopy = engineDoFinal(input, inputOffset, inputLen); 499 if (outputCopy == null) { 500 return 0; 501 } 502 int outputAvailable = output.length - outputOffset; 503 if (outputCopy.length > outputAvailable) { 504 throw new ShortBufferException("Output buffer too short. Produced: " 505 + outputCopy.length + ", available: " + outputAvailable); 506 } 507 System.arraycopy(outputCopy, 0, output, outputOffset, outputCopy.length); 508 return outputCopy.length; 509 } 510 511 @Override engineDoFinal(ByteBuffer input, ByteBuffer output)512 protected final int engineDoFinal(ByteBuffer input, ByteBuffer output) 513 throws ShortBufferException, IllegalBlockSizeException, BadPaddingException { 514 return super.engineDoFinal(input, output); 515 } 516 517 @Override engineWrap(Key key)518 protected final byte[] engineWrap(Key key) 519 throws IllegalBlockSizeException, InvalidKeyException { 520 if (mKey == null) { 521 throw new IllegalStateException("Not initilized"); 522 } 523 524 if (!isEncrypting()) { 525 throw new IllegalStateException( 526 "Cipher must be initialized in Cipher.WRAP_MODE to wrap keys"); 527 } 528 529 if (key == null) { 530 throw new NullPointerException("key == null"); 531 } 532 byte[] encoded = null; 533 if (key instanceof SecretKey) { 534 if ("RAW".equalsIgnoreCase(key.getFormat())) { 535 encoded = key.getEncoded(); 536 } 537 if (encoded == null) { 538 try { 539 SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(key.getAlgorithm()); 540 SecretKeySpec spec = 541 (SecretKeySpec) keyFactory.getKeySpec( 542 (SecretKey) key, SecretKeySpec.class); 543 encoded = spec.getEncoded(); 544 } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { 545 throw new InvalidKeyException( 546 "Failed to wrap key because it does not export its key material", 547 e); 548 } 549 } 550 } else if (key instanceof PrivateKey) { 551 if ("PKCS8".equalsIgnoreCase(key.getFormat())) { 552 encoded = key.getEncoded(); 553 } 554 if (encoded == null) { 555 try { 556 KeyFactory keyFactory = KeyFactory.getInstance(key.getAlgorithm()); 557 PKCS8EncodedKeySpec spec = 558 keyFactory.getKeySpec(key, PKCS8EncodedKeySpec.class); 559 encoded = spec.getEncoded(); 560 } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { 561 throw new InvalidKeyException( 562 "Failed to wrap key because it does not export its key material", 563 e); 564 } 565 } 566 } else if (key instanceof PublicKey) { 567 if ("X.509".equalsIgnoreCase(key.getFormat())) { 568 encoded = key.getEncoded(); 569 } 570 if (encoded == null) { 571 try { 572 KeyFactory keyFactory = KeyFactory.getInstance(key.getAlgorithm()); 573 X509EncodedKeySpec spec = 574 keyFactory.getKeySpec(key, X509EncodedKeySpec.class); 575 encoded = spec.getEncoded(); 576 } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { 577 throw new InvalidKeyException( 578 "Failed to wrap key because it does not export its key material", 579 e); 580 } 581 } 582 } else { 583 throw new InvalidKeyException("Unsupported key type: " + key.getClass().getName()); 584 } 585 586 if (encoded == null) { 587 throw new InvalidKeyException( 588 "Failed to wrap key because it does not export its key material"); 589 } 590 591 try { 592 return engineDoFinal(encoded, 0, encoded.length); 593 } catch (BadPaddingException e) { 594 throw (IllegalBlockSizeException) new IllegalBlockSizeException().initCause(e); 595 } 596 } 597 598 @Override engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm, int wrappedKeyType)599 protected final Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm, 600 int wrappedKeyType) throws InvalidKeyException, NoSuchAlgorithmException { 601 if (mKey == null) { 602 throw new IllegalStateException("Not initilized"); 603 } 604 605 if (isEncrypting()) { 606 throw new IllegalStateException( 607 "Cipher must be initialized in Cipher.WRAP_MODE to wrap keys"); 608 } 609 610 if (wrappedKey == null) { 611 throw new NullPointerException("wrappedKey == null"); 612 } 613 614 byte[] encoded; 615 try { 616 encoded = engineDoFinal(wrappedKey, 0, wrappedKey.length); 617 } catch (IllegalBlockSizeException | BadPaddingException e) { 618 throw new InvalidKeyException("Failed to unwrap key", e); 619 } 620 621 switch (wrappedKeyType) { 622 case Cipher.SECRET_KEY: 623 { 624 return new SecretKeySpec(encoded, wrappedKeyAlgorithm); 625 // break; 626 } 627 case Cipher.PRIVATE_KEY: 628 { 629 KeyFactory keyFactory = KeyFactory.getInstance(wrappedKeyAlgorithm); 630 try { 631 return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encoded)); 632 } catch (InvalidKeySpecException e) { 633 throw new InvalidKeyException( 634 "Failed to create private key from its PKCS#8 encoded form", e); 635 } 636 // break; 637 } 638 case Cipher.PUBLIC_KEY: 639 { 640 KeyFactory keyFactory = KeyFactory.getInstance(wrappedKeyAlgorithm); 641 try { 642 return keyFactory.generatePublic(new X509EncodedKeySpec(encoded)); 643 } catch (InvalidKeySpecException e) { 644 throw new InvalidKeyException( 645 "Failed to create public key from its X.509 encoded form", e); 646 } 647 // break; 648 } 649 default: 650 throw new InvalidParameterException( 651 "Unsupported wrappedKeyType: " + wrappedKeyType); 652 } 653 } 654 655 @Override engineSetMode(String mode)656 protected final void engineSetMode(String mode) throws NoSuchAlgorithmException { 657 // This should never be invoked because all algorithms registered with the AndroidKeyStore 658 // provide explicitly specify block mode. 659 throw new UnsupportedOperationException(); 660 } 661 662 @Override engineSetPadding(String arg0)663 protected final void engineSetPadding(String arg0) throws NoSuchPaddingException { 664 // This should never be invoked because all algorithms registered with the AndroidKeyStore 665 // provide explicitly specify padding mode. 666 throw new UnsupportedOperationException(); 667 } 668 669 @Override engineGetKeySize(Key key)670 protected final int engineGetKeySize(Key key) throws InvalidKeyException { 671 throw new UnsupportedOperationException(); 672 } 673 674 @CallSuper 675 @Override finalize()676 public void finalize() throws Throwable { 677 try { 678 IBinder operationToken = mOperationToken; 679 if (operationToken != null) { 680 mKeyStore.abort(operationToken); 681 } 682 } finally { 683 super.finalize(); 684 } 685 } 686 687 @Override getOperationHandle()688 public final long getOperationHandle() { 689 return mOperationHandle; 690 } 691 setKey(@onNull AndroidKeyStoreKey key)692 protected final void setKey(@NonNull AndroidKeyStoreKey key) { 693 mKey = key; 694 } 695 696 /** 697 * Overrides the default purpose/type of the crypto operation. 698 */ setKeymasterPurposeOverride(int keymasterPurpose)699 protected final void setKeymasterPurposeOverride(int keymasterPurpose) { 700 mKeymasterPurposeOverride = keymasterPurpose; 701 } 702 getKeymasterPurposeOverride()703 protected final int getKeymasterPurposeOverride() { 704 return mKeymasterPurposeOverride; 705 } 706 707 /** 708 * Returns {@code true} if this cipher is initialized for encryption, {@code false} if this 709 * cipher is initialized for decryption. 710 */ isEncrypting()711 protected final boolean isEncrypting() { 712 return mEncrypting; 713 } 714 715 @NonNull getKeyStore()716 protected final KeyStore getKeyStore() { 717 return mKeyStore; 718 } 719 getConsumedInputSizeBytes()720 protected final long getConsumedInputSizeBytes() { 721 if (mMainDataStreamer == null) { 722 throw new IllegalStateException("Not initialized"); 723 } 724 return mMainDataStreamer.getConsumedInputSizeBytes(); 725 } 726 getProducedOutputSizeBytes()727 protected final long getProducedOutputSizeBytes() { 728 if (mMainDataStreamer == null) { 729 throw new IllegalStateException("Not initialized"); 730 } 731 return mMainDataStreamer.getProducedOutputSizeBytes(); 732 } 733 opmodeToString(int opmode)734 static String opmodeToString(int opmode) { 735 switch (opmode) { 736 case Cipher.ENCRYPT_MODE: 737 return "ENCRYPT_MODE"; 738 case Cipher.DECRYPT_MODE: 739 return "DECRYPT_MODE"; 740 case Cipher.WRAP_MODE: 741 return "WRAP_MODE"; 742 case Cipher.UNWRAP_MODE: 743 return "UNWRAP_MODE"; 744 default: 745 return String.valueOf(opmode); 746 } 747 } 748 749 // The methods below need to be implemented by subclasses. 750 751 /** 752 * Initializes this cipher with the provided key. 753 * 754 * @throws InvalidKeyException if the {@code key} is not suitable for this cipher in the 755 * specified {@code opmode}. 756 * 757 * @see #setKey(AndroidKeyStoreKey) 758 */ initKey(int opmode, @Nullable Key key)759 protected abstract void initKey(int opmode, @Nullable Key key) throws InvalidKeyException; 760 761 /** 762 * Returns algorithm-specific parameters used by this cipher or {@code null} if no 763 * algorithm-specific parameters are used. 764 */ 765 @Nullable 766 @Override engineGetParameters()767 protected abstract AlgorithmParameters engineGetParameters(); 768 769 /** 770 * Invoked by {@code engineInit} to initialize algorithm-specific parameters when no additional 771 * initialization parameters were provided. 772 * 773 * @throws InvalidKeyException if this cipher cannot be configured based purely on the provided 774 * key and needs additional parameters to be provided to {@code Cipher.init}. 775 */ initAlgorithmSpecificParameters()776 protected abstract void initAlgorithmSpecificParameters() throws InvalidKeyException; 777 778 /** 779 * Invoked by {@code engineInit} to initialize algorithm-specific parameters when additional 780 * parameters were provided. 781 * 782 * @param params additional algorithm parameters or {@code null} if not specified. 783 * 784 * @throws InvalidAlgorithmParameterException if there is insufficient information to configure 785 * this cipher or if the provided parameters are not suitable for this cipher. 786 */ initAlgorithmSpecificParameters( @ullable AlgorithmParameterSpec params)787 protected abstract void initAlgorithmSpecificParameters( 788 @Nullable AlgorithmParameterSpec params) throws InvalidAlgorithmParameterException; 789 790 /** 791 * Invoked by {@code engineInit} to initialize algorithm-specific parameters when additional 792 * parameters were provided. 793 * 794 * @param params additional algorithm parameters or {@code null} if not specified. 795 * 796 * @throws InvalidAlgorithmParameterException if there is insufficient information to configure 797 * this cipher or if the provided parameters are not suitable for this cipher. 798 */ initAlgorithmSpecificParameters(@ullable AlgorithmParameters params)799 protected abstract void initAlgorithmSpecificParameters(@Nullable AlgorithmParameters params) 800 throws InvalidAlgorithmParameterException; 801 802 /** 803 * Returns the amount of additional entropy (in bytes) to be provided to the KeyStore's 804 * {@code begin} operation. This amount of entropy is typically what's consumed to generate 805 * random parameters, such as IV. 806 * 807 * <p>For decryption, the return value should be {@code 0} because decryption should not be 808 * consuming any entropy. For encryption, the value combined with 809 * {@link #getAdditionalEntropyAmountForFinish()} should match (or exceed) the amount of Shannon 810 * entropy of the ciphertext produced by this cipher assuming the key, the plaintext, and all 811 * explicitly provided parameters to {@code Cipher.init} are known. For example, for AES CBC 812 * encryption with an explicitly provided IV the return value should be {@code 0}, whereas for 813 * the case where IV is generated by the KeyStore's {@code begin} operation it should be 814 * {@code 16}. 815 */ getAdditionalEntropyAmountForBegin()816 protected abstract int getAdditionalEntropyAmountForBegin(); 817 818 /** 819 * Returns the amount of additional entropy (in bytes) to be provided to the KeyStore's 820 * {@code finish} operation. This amount of entropy is typically what's consumed by encryption 821 * padding scheme. 822 * 823 * <p>For decryption, the return value should be {@code 0} because decryption should not be 824 * consuming any entropy. For encryption, the value combined with 825 * {@link #getAdditionalEntropyAmountForBegin()} should match (or exceed) the amount of Shannon 826 * entropy of the ciphertext produced by this cipher assuming the key, the plaintext, and all 827 * explicitly provided parameters to {@code Cipher.init} are known. For example, for RSA with 828 * OAEP the return value should be the size of the OAEP hash output. For RSA with PKCS#1 padding 829 * the return value should be the size of the padding string or could be raised (for simplicity) 830 * to the size of the modulus. 831 */ getAdditionalEntropyAmountForFinish()832 protected abstract int getAdditionalEntropyAmountForFinish(); 833 834 /** 835 * Invoked to add algorithm-specific parameters for the KeyStore's {@code begin} operation. 836 * 837 * @param keymasterArgs keystore/keymaster arguments to be populated with algorithm-specific 838 * parameters. 839 */ addAlgorithmSpecificParametersToBegin( @onNull KeymasterArguments keymasterArgs)840 protected abstract void addAlgorithmSpecificParametersToBegin( 841 @NonNull KeymasterArguments keymasterArgs); 842 843 /** 844 * Invoked to obtain algorithm-specific parameters from the result of the KeyStore's 845 * {@code begin} operation. 846 * 847 * <p>Some parameters, such as IV, are not required to be provided to {@code Cipher.init}. Such 848 * parameters, if not provided, must be generated by KeyStore and returned to the user of 849 * {@code Cipher} and potentially reused after {@code doFinal}. 850 * 851 * @param keymasterArgs keystore/keymaster arguments returned by KeyStore {@code begin} 852 * operation. 853 */ loadAlgorithmSpecificParametersFromBeginResult( @onNull KeymasterArguments keymasterArgs)854 protected abstract void loadAlgorithmSpecificParametersFromBeginResult( 855 @NonNull KeymasterArguments keymasterArgs); 856 } 857