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