+++ /dev/null
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security;
-
-/**
- * Base class for exceptions during cryptographic operations which cannot throw a suitable checked
- * exception.
- *
- * <p>The contract of the majority of crypto primitives/operations (e.g. {@code Cipher} or
- * {@code Signature}) is that they can throw a checked exception during initialization, but are not
- * permitted to throw a checked exception during operation. Because crypto operations can fail
- * for a variety of reasons after initialization, this base class provides type-safety for unchecked
- * exceptions that may be thrown in those cases.
- *
- * @hide
- */
-public class CryptoOperationException extends RuntimeException {
-
- /**
- * Constructs a new {@code CryptoOperationException} without detail message and cause.
- */
- public CryptoOperationException() {
- super();
- }
-
- /**
- * Constructs a new {@code CryptoOperationException} with the provided detail message and no
- * cause.
- */
- public CryptoOperationException(String message) {
- super(message);
- }
-
- /**
- * Constructs a new {@code CryptoOperationException} with the provided detail message and cause.
- */
- public CryptoOperationException(String message, Throwable cause) {
- super(message, cause);
- }
-
- /**
- * Constructs a new {@code CryptoOperationException} with the provided cause.
- */
- public CryptoOperationException(Throwable cause) {
- super(cause);
- }
-}
package android.security;
+import java.security.InvalidKeyException;
+
/**
* Indicates that a cryptographic operation failed because the employed key's validity end date
* is in the past.
*
* @hide
*/
-public class KeyExpiredException extends CryptoOperationException {
+public class KeyExpiredException extends InvalidKeyException {
/**
* Constructs a new {@code KeyExpiredException} without detail message and cause.
package android.security;
+import java.security.InvalidKeyException;
+
/**
* Indicates that a cryptographic operation failed because the employed key's validity start date
* is in the future.
*
* @hide
*/
-public class KeyNotYetValidException extends CryptoOperationException {
+public class KeyNotYetValidException extends InvalidKeyException {
/**
* Constructs a new {@code KeyNotYetValidException} without detail message and cause.
import android.security.keymaster.OperationResult;
import android.util.Log;
+import java.security.InvalidKeyException;
import java.util.Locale;
/**
}
}
- public static KeyStoreException getKeyStoreException(int errorCode) {
+ /**
+ * Returns a {@link KeyStoreException} corresponding to the provided keystore/keymaster error
+ * code.
+ */
+ static KeyStoreException getKeyStoreException(int errorCode) {
if (errorCode > 0) {
// KeyStore layer error
switch (errorCode) {
}
}
- public static CryptoOperationException getCryptoOperationException(KeyStoreException e) {
+ /**
+ * Returns an {@link InvalidKeyException} corresponding to the provided
+ * {@link KeyStoreException}.
+ */
+ static InvalidKeyException getInvalidKeyException(KeyStoreException e) {
switch (e.getErrorCode()) {
case KeymasterDefs.KM_ERROR_KEY_EXPIRED:
return new KeyExpiredException();
case KeymasterDefs.KM_ERROR_KEY_USER_NOT_AUTHENTICATED:
return new UserNotAuthenticatedException();
default:
- return new CryptoOperationException("Crypto operation failed", e);
+ return new InvalidKeyException("Keystore operation failed", e);
}
}
- public static CryptoOperationException getCryptoOperationException(int errorCode) {
- return getCryptoOperationException(getKeyStoreException(errorCode));
+ /**
+ * Returns an {@link InvalidKeyException} corresponding to the provided keystore/keymaster error
+ * code.
+ */
+ static InvalidKeyException getInvalidKeyException(int errorCode) {
+ return getInvalidKeyException(getKeyStoreException(errorCode));
}
}
private Long mOperationHandle;
private KeyStoreCryptoOperationChunkedStreamer mMainDataStreamer;
+ /**
+ * Encountered exception which could not be immediately thrown because it was encountered inside
+ * a method that does not throw checked exception. This exception will be thrown from
+ * {@code engineDoFinal}. Once such an exception is encountered, {@code engineUpdate} and
+ * {@code engineDoFinal} start ignoring input data.
+ */
+ private Exception mCachedException;
+
protected KeyStoreCipherSpi(
int keymasterAlgorithm,
int keymasterBlockMode,
try {
init(opmode, key, random);
initAlgorithmSpecificParameters();
- ensureKeystoreOperationInitialized();
+ try {
+ ensureKeystoreOperationInitialized();
+ } catch (InvalidAlgorithmParameterException e) {
+ throw new InvalidKeyException(e);
+ }
success = true;
} finally {
if (!success) {
mOperationToken = null;
mOperationHandle = null;
mMainDataStreamer = null;
+ mCachedException = null;
}
private void resetWhilePreservingInitState() {
mOperationHandle = null;
mMainDataStreamer = null;
mAdditionalEntropyForBegin = null;
+ mCachedException = null;
}
- private void ensureKeystoreOperationInitialized() {
+ private void ensureKeystoreOperationInitialized() throws InvalidKeyException,
+ InvalidAlgorithmParameterException {
if (mMainDataStreamer != null) {
return;
}
+ if (mCachedException != null) {
+ return;
+ }
if (mKey == null) {
throw new IllegalStateException("Not initialized");
}
if (opResult == null) {
throw new KeyStoreConnectException();
} else if (opResult.resultCode != KeyStore.NO_ERROR) {
- throw KeyStore.getCryptoOperationException(opResult.resultCode);
+ switch (opResult.resultCode) {
+ case KeymasterDefs.KM_ERROR_INVALID_NONCE:
+ throw new InvalidAlgorithmParameterException("Invalid IV");
+ }
+ throw KeyStore.getInvalidKeyException(opResult.resultCode);
}
if (opResult.token == null) {
- throw new CryptoOperationException("Keystore returned null operation token");
+ throw new IllegalStateException("Keystore returned null operation token");
}
mOperationToken = opResult.token;
mOperationHandle = opResult.operationHandle;
@Override
protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) {
- ensureKeystoreOperationInitialized();
+ if (mCachedException != null) {
+ return null;
+ }
+ try {
+ ensureKeystoreOperationInitialized();
+ } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
+ mCachedException = e;
+ return null;
+ }
if (inputLen == 0) {
return null;
try {
output = mMainDataStreamer.update(input, inputOffset, inputLen);
} catch (KeyStoreException e) {
- throw KeyStore.getCryptoOperationException(e);
+ mCachedException = e;
+ return null;
}
if (output.length == 0) {
@Override
protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen)
throws IllegalBlockSizeException, BadPaddingException {
- ensureKeystoreOperationInitialized();
+ if (mCachedException != null) {
+ throw (IllegalBlockSizeException)
+ new IllegalBlockSizeException().initCause(mCachedException);
+ }
+
+ try {
+ ensureKeystoreOperationInitialized();
+ } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
+ throw (IllegalBlockSizeException) new IllegalBlockSizeException().initCause(e);
+ }
byte[] output;
try {
case KeymasterDefs.KM_ERROR_VERIFICATION_FAILED:
throw new AEADBadTagException();
default:
- throw KeyStore.getCryptoOperationException(e);
+ throw (IllegalBlockSizeException) new IllegalBlockSizeException().initCause(e);
}
}
if (mIv == null) {
mIv = returnedIv;
} else if ((returnedIv != null) && (!Arrays.equals(returnedIv, mIv))) {
- throw new CryptoOperationException("IV in use differs from provided IV");
+ throw new IllegalStateException("IV in use differs from provided IV");
}
} else {
if (returnedIv != null) {
- throw new CryptoOperationException(
+ throw new IllegalStateException(
"IV in use despite IV not being used by this transformation");
}
}
*
* @hide
*/
-public class KeyStoreConnectException extends CryptoOperationException {
+public class KeyStoreConnectException extends IllegalStateException {
public KeyStoreConnectException() {
super("Failed to communicate with keystore service");
}
// More input is available, but it wasn't included into the previous chunk
// because the chunk reached its maximum permitted size.
// Shouldn't have happened.
- throw new CryptoOperationException("Nothing consumed from max-sized chunk: "
+ throw new IllegalStateException("Nothing consumed from max-sized chunk: "
+ chunk.length + " bytes");
}
mBuffered = chunk;
mBufferedOffset = opResult.inputConsumed;
mBufferedLength = chunk.length - opResult.inputConsumed;
} else {
- throw new CryptoOperationException("Consumed more than provided: "
+ throw new IllegalStateException("Consumed more than provided: "
+ opResult.inputConsumed + ", provided: " + chunk.length);
}
try {
bufferedOutput.write(opResult.output);
} catch (IOException e) {
- throw new CryptoOperationException("Failed to buffer output", e);
+ throw new IllegalStateException("Failed to buffer output", e);
}
}
} else {
try {
bufferedOutput.write(opResult.output);
} catch (IOException e) {
- throw new CryptoOperationException("Failed to buffer output", e);
+ throw new IllegalStateException("Failed to buffer output", e);
}
return bufferedOutput.toByteArray();
}
}
if (opResult.inputConsumed < chunk.length) {
- throw new CryptoOperationException("Keystore failed to consume all input. Provided: "
+ throw new IllegalStateException("Keystore failed to consume all input. Provided: "
+ chunk.length + ", consumed: " + opResult.inputConsumed);
} else if (opResult.inputConsumed > chunk.length) {
- throw new CryptoOperationException("Keystore consumed more input than provided"
+ throw new IllegalStateException("Keystore consumed more input than provided"
+ " . Provided: " + chunk.length + ", consumed: " + opResult.inputConsumed);
}
resetWhilePreservingInitState();
}
- private void ensureKeystoreOperationInitialized() {
+ private void ensureKeystoreOperationInitialized() throws InvalidKeyException {
if (mChunkedStreamer != null) {
return;
}
if (opResult == null) {
throw new KeyStoreConnectException();
} else if (opResult.resultCode != KeyStore.NO_ERROR) {
- throw KeyStore.getCryptoOperationException(opResult.resultCode);
+ throw KeyStore.getInvalidKeyException(opResult.resultCode);
}
if (opResult.token == null) {
- throw new CryptoOperationException("Keystore returned null operation token");
+ throw new IllegalStateException("Keystore returned null operation token");
}
mOperationToken = opResult.token;
mOperationHandle = opResult.operationHandle;
@Override
protected void engineUpdate(byte[] input, int offset, int len) {
- ensureKeystoreOperationInitialized();
+ try {
+ ensureKeystoreOperationInitialized();
+ } catch (InvalidKeyException e) {
+ throw new IllegalStateException("Failed to reinitialize MAC", e);
+ }
byte[] output;
try {
output = mChunkedStreamer.update(input, offset, len);
} catch (KeyStoreException e) {
- throw KeyStore.getCryptoOperationException(e);
+ throw new IllegalStateException("Keystore operation failed", e);
}
if ((output != null) && (output.length != 0)) {
- throw new CryptoOperationException("Update operation unexpectedly produced output");
+ throw new IllegalStateException("Update operation unexpectedly produced output");
}
}
@Override
protected byte[] engineDoFinal() {
- ensureKeystoreOperationInitialized();
+ try {
+ ensureKeystoreOperationInitialized();
+ } catch (InvalidKeyException e) {
+ throw new IllegalStateException("Failed to reinitialize MAC", e);
+ }
byte[] result;
try {
result = mChunkedStreamer.doFinal(null, 0, 0);
} catch (KeyStoreException e) {
- throw KeyStore.getCryptoOperationException(e);
+ throw new IllegalStateException("Keystore operation failed", e);
}
resetWhilePreservingInitState();
int errorCode = mKeyStore.generateKey(
keyAliasInKeystore, args, additionalEntropy, flags, new KeyCharacteristics());
if (errorCode != KeyStore.NO_ERROR) {
- throw KeyStore.getCryptoOperationException(errorCode);
+ throw new IllegalStateException(
+ "Keystore operation failed", KeyStore.getKeyStoreException(errorCode));
}
String keyAlgorithmJCA =
KeymasterUtils.getJcaSecretKeyAlgorithm(mKeymasterAlgorithm, mKeymasterDigest);
package android.security;
+import java.security.InvalidKeyException;
+
/**
* Indicates that a cryptographic operation could not be performed because the user has not been
* authenticated recently enough.
*
* @hide
*/
-public class UserNotAuthenticatedException extends CryptoOperationException {
+public class UserNotAuthenticatedException extends InvalidKeyException {
/**
* Constructs a new {@code UserNotAuthenticatedException} without detail message and cause.