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