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