diff options
author | Janis Danisevskis <jdanis@google.com> | 2021-04-03 02:02:03 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2021-04-03 02:02:03 +0000 |
commit | 9ba0f37f8a0286261e4074463ed0314836c9fd61 (patch) | |
tree | 66f14fa5a3b4f5037f8822d480936b7f8f0b3e76 /keystore | |
parent | 9da3f39ceb2a64013e82a772e6ca2116163f913b (diff) | |
parent | 66ead4fb0bdfb5fe7e9a608d193148ae53e6bf4f (diff) |
Merge "Keystore 2.0: Remove Keystore 1.0 SPI with all remaining references" am: a8b1b1a2e6 am: 08945c21ef am: 66ead4fb0b
Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1624872
Change-Id: I08fcf329e59c309d9292edc846653b02e7a60f21
Diffstat (limited to 'keystore')
46 files changed, 199 insertions, 9623 deletions
diff --git a/keystore/java/android/security/AndroidKeyStoreMaintenance.java b/keystore/java/android/security/AndroidKeyStoreMaintenance.java index 72cea0cacd12..82639def02de 100644 --- a/keystore/java/android/security/AndroidKeyStoreMaintenance.java +++ b/keystore/java/android/security/AndroidKeyStoreMaintenance.java @@ -47,7 +47,6 @@ public class AndroidKeyStoreMaintenance { * @hide */ public static int onUserAdded(@NonNull int userId) { - if (!android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) return 0; try { getService().onUserAdded(userId); return 0; @@ -68,7 +67,6 @@ public class AndroidKeyStoreMaintenance { * @hide */ public static int onUserRemoved(int userId) { - if (!android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) return 0; try { getService().onUserRemoved(userId); return 0; @@ -91,7 +89,6 @@ public class AndroidKeyStoreMaintenance { * @hide */ public static int onUserPasswordChanged(int userId, @Nullable byte[] password) { - if (!android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) return 0; try { getService().onUserPasswordChanged(userId, password); return 0; @@ -109,7 +106,6 @@ public class AndroidKeyStoreMaintenance { * be cleared. */ public static int clearNamespace(@Domain int domain, long namespace) { - if (!android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) return 0; try { getService().clearNamespace(domain, namespace); return 0; @@ -144,7 +140,6 @@ public class AndroidKeyStoreMaintenance { * Informs Keystore 2.0 that an off body event was detected. */ public static void onDeviceOffBody() { - if (!android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) return; try { getService().onDeviceOffBody(); } catch (Exception e) { diff --git a/keystore/java/android/security/Authorization.java b/keystore/java/android/security/Authorization.java index 50a90820117d..bd72d45297c1 100644 --- a/keystore/java/android/security/Authorization.java +++ b/keystore/java/android/security/Authorization.java @@ -48,7 +48,6 @@ public class Authorization { * @return 0 if successful or {@code ResponseCode.SYSTEM_ERROR}. */ public static int addAuthToken(@NonNull HardwareAuthToken authToken) { - if (!android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) return 0; try { getService().addAuthToken(authToken); return 0; @@ -80,7 +79,6 @@ public class Authorization { */ public static int onLockScreenEvent(@NonNull boolean locked, @NonNull int userId, @Nullable byte[] syntheticPassword) { - if (!android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) return 0; try { if (locked) { getService().onLockScreenEvent(LockScreenEvent.LOCK, userId, null); diff --git a/keystore/java/android/security/Credentials.java b/keystore/java/android/security/Credentials.java index ae9f866459d6..28c601ba0e6f 100644 --- a/keystore/java/android/security/Credentials.java +++ b/keystore/java/android/security/Credentials.java @@ -208,78 +208,4 @@ public class Credentials { pr.close(); } } - - /** - * Delete all types (private key, user certificate, CA certificate) for a - * particular {@code alias}. All three can exist for any given alias. - * Returns {@code true} if the alias no longer contains any types. - */ - public static boolean deleteAllTypesForAlias(KeyStore keystore, String alias) { - return deleteAllTypesForAlias(keystore, alias, KeyStore.UID_SELF); - } - - /** - * Delete all types (private key, user certificate, CA certificate) for a - * particular {@code alias}. All three can exist for any given alias. - * Returns {@code true} if the alias no longer contains any types. - */ - public static boolean deleteAllTypesForAlias(KeyStore keystore, String alias, int uid) { - /* - * Make sure every type is deleted. There can be all three types, so - * don't use a conditional here. - */ - return deleteUserKeyTypeForAlias(keystore, alias, uid) - & deleteCertificateTypesForAlias(keystore, alias, uid); - } - - /** - * Delete certificate types (user certificate, CA certificate) for a - * particular {@code alias}. Both can exist for any given alias. - * Returns {@code true} if the alias no longer contains either type. - */ - public static boolean deleteCertificateTypesForAlias(KeyStore keystore, String alias) { - return deleteCertificateTypesForAlias(keystore, alias, KeyStore.UID_SELF); - } - - /** - * Delete certificate types (user certificate, CA certificate) for a - * particular {@code alias}. Both can exist for any given alias. - * Returns {@code true} if the alias no longer contains either type. - */ - public static boolean deleteCertificateTypesForAlias(KeyStore keystore, String alias, int uid) { - /* - * Make sure every certificate type is deleted. There can be two types, - * so don't use a conditional here. - */ - return keystore.delete(Credentials.USER_CERTIFICATE + alias, uid) - & keystore.delete(Credentials.CA_CERTIFICATE + alias, uid); - } - - /** - * Delete user key for a particular {@code alias}. - * Returns {@code true} if the entry no longer exists. - */ - public static boolean deleteUserKeyTypeForAlias(KeyStore keystore, String alias) { - return deleteUserKeyTypeForAlias(keystore, alias, KeyStore.UID_SELF); - } - - /** - * Delete user key for a particular {@code alias}. - * Returns {@code true} if the entry no longer exists. - */ - public static boolean deleteUserKeyTypeForAlias(KeyStore keystore, String alias, int uid) { - int ret = keystore.delete2(Credentials.USER_PRIVATE_KEY + alias, uid); - if (ret == KeyStore.KEY_NOT_FOUND) { - return keystore.delete(Credentials.USER_SECRET_KEY + alias, uid); - } - return ret == KeyStore.NO_ERROR; - } - - /** - * Delete legacy prefixed entry for a particular {@code alias} - * Returns {@code true} if the entry no longer exists. - */ - public static boolean deleteLegacyKeyForAlias(KeyStore keystore, String alias, int uid) { - return keystore.delete(Credentials.USER_SECRET_KEY + alias, uid); - } } diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java index 7c80f70593df..02cdeef77bec 100644 --- a/keystore/java/android/security/KeyChain.java +++ b/keystore/java/android/security/KeyChain.java @@ -42,7 +42,6 @@ import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; -import android.security.keystore.AndroidKeyStoreProvider; import android.security.keystore.KeyPermanentlyInvalidatedException; import android.security.keystore.KeyProperties; import android.system.keystore2.Domain; @@ -806,23 +805,13 @@ public final class KeyChain { return null; } - if (AndroidKeyStoreProvider.isKeystore2Enabled()) { - try { - return android.security.keystore2.AndroidKeyStoreProvider - .loadAndroidKeyStoreKeyPairFromKeystore( - KeyStore2.getInstance(), - getGrantDescriptor(keyId)); - } catch (UnrecoverableKeyException | KeyPermanentlyInvalidatedException e) { - throw new KeyChainException(e); - } - } else { - try { - return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyPairFromKeystore( - KeyStore.getInstance(), keyId, KeyStore.UID_SELF); - } catch (RuntimeException | UnrecoverableKeyException - | KeyPermanentlyInvalidatedException e) { - throw new KeyChainException(e); - } + try { + return android.security.keystore2.AndroidKeyStoreProvider + .loadAndroidKeyStoreKeyPairFromKeystore( + KeyStore2.getInstance(), + getGrantDescriptor(keyId)); + } catch (UnrecoverableKeyException | KeyPermanentlyInvalidatedException e) { + throw new KeyChainException(e); } } diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java index b05149ef75bc..a9543443d3f4 100644 --- a/keystore/java/android/security/KeyStore.java +++ b/keystore/java/android/security/KeyStore.java @@ -16,53 +16,11 @@ package android.security; -import android.app.ActivityThread; -import android.app.Application; -import android.app.KeyguardManager; import android.compat.annotation.UnsupportedAppUsage; -import android.content.Context; -import android.hardware.biometrics.BiometricManager; -import android.os.Binder; import android.os.Build; -import android.os.IBinder; -import android.os.Process; -import android.os.RemoteException; -import android.os.ServiceManager; import android.os.UserHandle; -import android.security.keymaster.ExportResult; -import android.security.keymaster.KeyCharacteristics; -import android.security.keymaster.KeymasterArguments; -import android.security.keymaster.KeymasterBlob; -import android.security.keymaster.KeymasterCertificateChain; -import android.security.keymaster.KeymasterDefs; -import android.security.keymaster.OperationResult; -import android.security.keystore.IKeystoreService; -import android.security.keystore.KeyExpiredException; -import android.security.keystore.KeyNotYetValidException; -import android.security.keystore.KeyPermanentlyInvalidatedException; -import android.security.keystore.KeyProperties; -import android.security.keystore.KeystoreResponse; -import android.security.keystore.UserNotAuthenticatedException; import android.security.maintenance.UserState; import android.system.keystore2.Domain; -import android.util.Log; - -import com.android.internal.org.bouncycastle.asn1.ASN1InputStream; -import com.android.internal.org.bouncycastle.asn1.pkcs.PrivateKeyInfo; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.math.BigInteger; -import java.security.InvalidKeyException; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.Locale; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; - -import sun.security.util.ObjectIdentifier; -import sun.security.x509.AlgorithmId; /** * @hide This should not be made public in its present form because it @@ -75,79 +33,10 @@ public class KeyStore { // ResponseCodes - see system/security/keystore/include/keystore/keystore.h @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final int NO_ERROR = 1; - public static final int LOCKED = 2; - public static final int UNINITIALIZED = 3; - public static final int SYSTEM_ERROR = 4; - public static final int PROTOCOL_ERROR = 5; - public static final int PERMISSION_DENIED = 6; - public static final int KEY_NOT_FOUND = 7; - public static final int VALUE_CORRUPTED = 8; - public static final int UNDEFINED_ACTION = 9; - public static final int WRONG_PASSWORD = 10; - public static final int KEY_ALREADY_EXISTS = 16; - public static final int CANNOT_ATTEST_IDS = -66; - public static final int HARDWARE_TYPE_UNAVAILABLE = -68; - - /** - * Per operation authentication is needed before this operation is valid. - * This is returned from {@link #begin} when begin succeeds but the operation uses - * per-operation authentication and must authenticate before calling {@link #update} or - * {@link #finish}. - */ - public static final int OP_AUTH_NEEDED = 15; - - // Used when a user changes their pin, invalidating old auth bound keys. - public static final int KEY_PERMANENTLY_INVALIDATED = 17; // Used for UID field to indicate the calling UID. public static final int UID_SELF = -1; - // Flags for "put" "import" and "generate" - public static final int FLAG_NONE = 0; - - /** - * Indicates that this key (or key pair) must be encrypted at rest. This will protect the key - * (or key pair) with the secure lock screen credential (e.g., password, PIN, or pattern). - * - * <p>Note that this requires that the secure lock screen (e.g., password, PIN, pattern) is set - * up, otherwise key (or key pair) generation or import will fail. Moreover, this key (or key - * pair) will be deleted when the secure lock screen is disabled or reset (e.g., by the user or - * a Device Administrator). Finally, this key (or key pair) cannot be used until the user - * unlocks the secure lock screen after boot. - * - * @see KeyguardManager#isDeviceSecure() - */ - public static final int FLAG_ENCRYPTED = 1; - - /** - * Select Software keymaster device, which as of this writing is the lowest security - * level available on an android device. If neither FLAG_STRONGBOX nor FLAG_SOFTWARE is provided - * A TEE based keymaster implementation is implied. - * - * Need to be in sync with KeyStoreFlag in system/security/keystore/include/keystore/keystore.h - * For historical reasons this corresponds to the KEYSTORE_FLAG_FALLBACK flag. - */ - public static final int FLAG_SOFTWARE = 1 << 1; - - /** - * A private flag that's only available to system server to indicate that this key is part of - * device encryption flow so it receives special treatment from keystore. For example this key - * will not be super encrypted, and it will be stored separately under an unique UID instead - * of the caller UID i.e. SYSTEM. - * - * Need to be in sync with KeyStoreFlag in system/security/keystore/include/keystore/keystore.h - */ - public static final int FLAG_CRITICAL_TO_DEVICE_ENCRYPTION = 1 << 3; - - /** - * Select Strongbox keymaster device, which as of this writing the the highest security level - * available an android devices. If neither FLAG_STRONGBOX nor FLAG_SOFTWARE is provided - * A TEE based keymaster implementation is implied. - * - * Need to be in sync with KeyStoreFlag in system/security/keystore/include/keystore/keystore.h - */ - public static final int FLAG_STRONGBOX = 1 << 4; - // States public enum State { @UnsupportedAppUsage @@ -157,853 +46,87 @@ public class KeyStore { UNINITIALIZED }; - private int mError = NO_ERROR; - - private final IKeystoreService mBinder; - private final Context mContext; - - private IBinder mToken; - - private KeyStore(IKeystoreService binder) { - mBinder = binder; - mContext = getApplicationContext(); - } - - @UnsupportedAppUsage - public static Context getApplicationContext() { - Application application = ActivityThread.currentApplication(); - if (application == null) { - throw new IllegalStateException( - "Failed to obtain application Context from ActivityThread"); - } - return application; - } + private static final KeyStore KEY_STORE = new KeyStore(); @UnsupportedAppUsage public static KeyStore getInstance() { - IKeystoreService keystore = IKeystoreService.Stub.asInterface(ServiceManager - .getService("android.security.keystore")); - return new KeyStore(keystore); - } - - private synchronized IBinder getToken() { - if (mToken == null) { - mToken = new Binder(); - } - return mToken; + return KEY_STORE; } + /** @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public State state(int userId) { - final int ret; - try { - if (android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) { - int userState = AndroidKeyStoreMaintenance.getState(userId); - switch (userState) { - case UserState.UNINITIALIZED: - return KeyStore.State.UNINITIALIZED; - case UserState.LSKF_UNLOCKED: - return KeyStore.State.UNLOCKED; - case UserState.LSKF_LOCKED: - return KeyStore.State.LOCKED; - default: - throw new AssertionError(userState); - } - } - ret = mBinder.getState(userId); - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to keystore", e); - throw new AssertionError(e); - } - - switch (ret) { - case NO_ERROR: return State.UNLOCKED; - case LOCKED: return State.LOCKED; - case UNINITIALIZED: return State.UNINITIALIZED; - default: throw new AssertionError(mError); + int userState = AndroidKeyStoreMaintenance.getState(userId); + switch (userState) { + case UserState.UNINITIALIZED: + return KeyStore.State.UNINITIALIZED; + case UserState.LSKF_UNLOCKED: + return KeyStore.State.UNLOCKED; + case UserState.LSKF_LOCKED: + return KeyStore.State.LOCKED; + default: + throw new AssertionError(userState); } } + /** @hide */ @UnsupportedAppUsage public State state() { return state(UserHandle.myUserId()); } - public boolean isUnlocked() { - return state() == State.UNLOCKED; - } - - public byte[] get(String key, int uid) { - return get(key, uid, false); - } - + /** @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public byte[] get(String key) { - return get(key, UID_SELF); - } - - public byte[] get(String key, int uid, boolean suppressKeyNotFoundWarning) { - try { - key = key != null ? key : ""; - return mBinder.get(key, uid); - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to keystore", e); - return null; - } catch (android.os.ServiceSpecificException e) { - if (!suppressKeyNotFoundWarning || e.errorCode != KEY_NOT_FOUND) { - Log.w(TAG, "KeyStore exception", e); - } - return null; - } - } - - public byte[] get(String key, boolean suppressKeyNotFoundWarning) { - return get(key, UID_SELF, suppressKeyNotFoundWarning); - } - - - public boolean put(String key, byte[] value, int uid, int flags) { - return insert(key, value, uid, flags) == NO_ERROR; - } - - public int insert(String key, byte[] value, int uid, int flags) { - try { - if (value == null) { - value = new byte[0]; - } - int error = mBinder.insert(key, value, uid, flags); - if (error == KEY_ALREADY_EXISTS) { - mBinder.del(key, uid); - error = mBinder.insert(key, value, uid, flags); - } - return error; - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to keystore", e); - return SYSTEM_ERROR; - } - } - - int delete2(String key, int uid) { - try { - return mBinder.del(key, uid); - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to keystore", e); - return SYSTEM_ERROR; - } - } - - public boolean delete(String key, int uid) { - int ret = delete2(key, uid); - return ret == NO_ERROR || ret == KEY_NOT_FOUND; + return null; } + /** @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean delete(String key) { - return delete(key, UID_SELF); - } - - public boolean contains(String key, int uid) { - try { - return mBinder.exist(key, uid) == NO_ERROR; - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to keystore", e); - return false; - } - } - - public boolean contains(String key) { - return contains(key, UID_SELF); - } - - /** - * List all entries in the keystore for {@code uid} starting with {@code prefix}. - */ - public String[] list(String prefix, int uid) { - try { - return mBinder.list(prefix, uid); - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to keystore", e); - return null; - } catch (android.os.ServiceSpecificException e) { - Log.w(TAG, "KeyStore exception", e); - return null; - } + return false; } /** * List uids of all keys that are auth bound to the current user. * Only system is allowed to call this method. + * @hide + * @deprecated This function always returns null. */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public int[] listUidsOfAuthBoundKeys() { - // uids are returned as a list of strings because list of integers - // as an output parameter is not supported by aidl-cpp. - List<String> uidsOut = new ArrayList<>(); - try { - int rc = mBinder.listUidsOfAuthBoundKeys(uidsOut); - if (rc != NO_ERROR) { - Log.w(TAG, String.format("listUidsOfAuthBoundKeys failed with error code %d", rc)); - return null; - } - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to keystore", e); - return null; - } catch (android.os.ServiceSpecificException e) { - Log.w(TAG, "KeyStore exception", e); - return null; - } - // Turn list of strings into an array of uid integers. - return uidsOut.stream().mapToInt(Integer::parseInt).toArray(); - } - - public String[] list(String prefix) { - return list(prefix, UID_SELF); + return null; } - /** - * Attempt to lock the keystore for {@code user}. - * - * @param userId Android user to lock. - * @return whether {@code user}'s keystore was locked. - */ - public boolean lock(int userId) { - try { - return mBinder.lock(userId) == NO_ERROR; - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to keystore", e); - return false; - } - } - - public boolean lock() { - return lock(UserHandle.myUserId()); - } /** - * Attempt to unlock the keystore for {@code user} with the password {@code password}. - * This is required before keystore entries created with FLAG_ENCRYPTED can be accessed or - * created. - * - * @param userId Android user ID to operate on - * @param password user's keystore password. Should be the most recent value passed to - * {@link #onUserPasswordChanged} for the user. - * - * @return whether the keystore was unlocked. + * @hide + * @deprecated This function has no effect. */ - public boolean unlock(int userId, String password) { - try { - password = password != null ? password : ""; - mError = mBinder.unlock(userId, password); - return mError == NO_ERROR; - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to keystore", e); - return false; - } - } - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean unlock(String password) { - return unlock(UserHandle.getUserId(Process.myUid()), password); + return false; } /** - * Check if the keystore for {@code userId} is empty. + * + * @return + * @deprecated This function always returns true. + * @hide */ - public boolean isEmpty(int userId) { - try { - return mBinder.isEmpty(userId) != 0; - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to keystore", e); - return false; - } - } - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) public boolean isEmpty() { - return isEmpty(UserHandle.myUserId()); - } - - public String grant(String key, int uid) { - try { - String grantAlias = mBinder.grant(key, uid); - if (grantAlias == "") return null; - return grantAlias; - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to keystore", e); - return null; - } - } - - public boolean ungrant(String key, int uid) { - try { - return mBinder.ungrant(key, uid) == NO_ERROR; - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to keystore", e); - return false; - } - } - - /** - * Returns the last modification time of the key in milliseconds since the - * epoch. Will return -1L if the key could not be found or other error. - */ - public long getmtime(String key, int uid) { - try { - final long millis = mBinder.getmtime(key, uid); - if (millis == -1L) { - return -1L; - } - - return millis * 1000L; - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to keystore", e); - return -1L; - } - } - - public long getmtime(String key) { - return getmtime(key, UID_SELF); - } - - // TODO: remove this when it's removed from Settings - public boolean isHardwareBacked() { - return isHardwareBacked("RSA"); - } - - public boolean isHardwareBacked(String keyType) { - try { - return mBinder.is_hardware_backed(keyType.toUpperCase(Locale.US)) == NO_ERROR; - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to keystore", e); - return false; - } - } - - public boolean clearUid(int uid) { - try { - if (android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) { - return AndroidKeyStoreMaintenance.clearNamespace(Domain.APP, uid) == 0; - } - return mBinder.clear_uid(uid) == NO_ERROR; - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to keystore", e); - return false; - } - } - - public int getLastError() { - return mError; - } - - public boolean addRngEntropy(byte[] data, int flags) { - KeystoreResultPromise promise = new KeystoreResultPromise(); - try { - mBinder.asBinder().linkToDeath(promise, 0); - int errorCode = mBinder.addRngEntropy(promise, data, flags); - if (errorCode == NO_ERROR) { - return interruptedPreservingGet(promise.getFuture()).getErrorCode() == NO_ERROR; - } else { - return false; - } - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to keystore", e); - return false; - } catch (ExecutionException e) { - Log.e(TAG, "AddRngEntropy completed with exception", e); - return false; - } finally { - mBinder.asBinder().unlinkToDeath(promise, 0); - } - } - - private class KeyCharacteristicsCallbackResult { - private KeystoreResponse keystoreResponse; - private KeyCharacteristics keyCharacteristics; - - public KeyCharacteristicsCallbackResult(KeystoreResponse keystoreResponse, - KeyCharacteristics keyCharacteristics) { - this.keystoreResponse = keystoreResponse; - this.keyCharacteristics = keyCharacteristics; - } - - public KeystoreResponse getKeystoreResponse() { - return keystoreResponse; - } - - public void setKeystoreResponse(KeystoreResponse keystoreResponse) { - this.keystoreResponse = keystoreResponse; - } - - public KeyCharacteristics getKeyCharacteristics() { - return keyCharacteristics; - } - - public void setKeyCharacteristics(KeyCharacteristics keyCharacteristics) { - this.keyCharacteristics = keyCharacteristics; - } - } - - private class KeyCharacteristicsPromise - extends android.security.keystore.IKeystoreKeyCharacteristicsCallback.Stub - implements IBinder.DeathRecipient { - final private CompletableFuture<KeyCharacteristicsCallbackResult> future = - new CompletableFuture<KeyCharacteristicsCallbackResult>(); - @Override - public void onFinished(KeystoreResponse keystoreResponse, - KeyCharacteristics keyCharacteristics) - throws android.os.RemoteException { - future.complete( - new KeyCharacteristicsCallbackResult(keystoreResponse, keyCharacteristics)); - } - public final CompletableFuture<KeyCharacteristicsCallbackResult> getFuture() { - return future; - } - @Override - public void binderDied() { - future.completeExceptionally(new RemoteException("Keystore died")); - } - }; - - private int generateKeyInternal(String alias, KeymasterArguments args, byte[] entropy, int uid, - int flags, KeyCharacteristics outCharacteristics) - throws RemoteException, ExecutionException { - KeyCharacteristicsPromise promise = new KeyCharacteristicsPromise(); - int error = NO_ERROR; - KeyCharacteristicsCallbackResult result = null; - try { - mBinder.asBinder().linkToDeath(promise, 0); - error = mBinder.generateKey(promise, alias, args, entropy, uid, flags); - if (error != NO_ERROR) { - Log.e(TAG, "generateKeyInternal failed on request " + error); - return error; - } - result = interruptedPreservingGet(promise.getFuture()); - } finally { - mBinder.asBinder().unlinkToDeath(promise, 0); - } - - error = result.getKeystoreResponse().getErrorCode(); - if (error != NO_ERROR) { - Log.e(TAG, "generateKeyInternal failed on response " + error); - return error; - } - KeyCharacteristics characteristics = result.getKeyCharacteristics(); - if (characteristics == null) { - Log.e(TAG, "generateKeyInternal got empty key characteristics " + error); - return SYSTEM_ERROR; - } - outCharacteristics.shallowCopyFrom(characteristics); - return NO_ERROR; - } - - public int generateKey(String alias, KeymasterArguments args, byte[] entropy, int uid, - int flags, KeyCharacteristics outCharacteristics) { - try { - entropy = entropy != null ? entropy : new byte[0]; - args = args != null ? args : new KeymasterArguments(); - int error = generateKeyInternal(alias, args, entropy, uid, flags, outCharacteristics); - if (error == KEY_ALREADY_EXISTS) { - mBinder.del(alias, uid); - error = generateKeyInternal(alias, args, entropy, uid, flags, outCharacteristics); - } - return error; - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to keystore", e); - return SYSTEM_ERROR; - } catch (ExecutionException e) { - Log.e(TAG, "generateKey completed with exception", e); - return SYSTEM_ERROR; - } - } - - public int generateKey(String alias, KeymasterArguments args, byte[] entropy, int flags, - KeyCharacteristics outCharacteristics) { - return generateKey(alias, args, entropy, UID_SELF, flags, outCharacteristics); - } - - public int getKeyCharacteristics(String alias, KeymasterBlob clientId, KeymasterBlob appId, - int uid, KeyCharacteristics outCharacteristics) { - KeyCharacteristicsPromise promise = new KeyCharacteristicsPromise(); - try { - mBinder.asBinder().linkToDeath(promise, 0); - clientId = clientId != null ? clientId : new KeymasterBlob(new byte[0]); - appId = appId != null ? appId : new KeymasterBlob(new byte[0]); - - int error = mBinder.getKeyCharacteristics(promise, alias, clientId, appId, uid); - if (error != NO_ERROR) return error; - - KeyCharacteristicsCallbackResult result = interruptedPreservingGet(promise.getFuture()); - error = result.getKeystoreResponse().getErrorCode(); - if (error != NO_ERROR) return error; - - KeyCharacteristics characteristics = result.getKeyCharacteristics(); - if (characteristics == null) return SYSTEM_ERROR; - outCharacteristics.shallowCopyFrom(characteristics); - return NO_ERROR; - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to keystore", e); - return SYSTEM_ERROR; - } catch (ExecutionException e) { - Log.e(TAG, "GetKeyCharacteristics completed with exception", e); - return SYSTEM_ERROR; - } finally { - mBinder.asBinder().unlinkToDeath(promise, 0); - } - } - - public int getKeyCharacteristics(String alias, KeymasterBlob clientId, KeymasterBlob appId, - KeyCharacteristics outCharacteristics) { - return getKeyCharacteristics(alias, clientId, appId, UID_SELF, outCharacteristics); - } - - private int importKeyInternal(String alias, KeymasterArguments args, int format, byte[] keyData, - int uid, int flags, KeyCharacteristics outCharacteristics) - throws RemoteException, ExecutionException { - KeyCharacteristicsPromise promise = new KeyCharacteristicsPromise(); - mBinder.asBinder().linkToDeath(promise, 0); - try { - int error = mBinder.importKey(promise, alias, args, format, keyData, uid, flags); - if (error != NO_ERROR) return error; - - KeyCharacteristicsCallbackResult result = interruptedPreservingGet(promise.getFuture()); - - error = result.getKeystoreResponse().getErrorCode(); - if (error != NO_ERROR) return error; - - KeyCharacteristics characteristics = result.getKeyCharacteristics(); - if (characteristics == null) return SYSTEM_ERROR; - outCharacteristics.shallowCopyFrom(characteristics); - return NO_ERROR; - } finally { - mBinder.asBinder().unlinkToDeath(promise, 0); - } - } - - public int importKey(String alias, KeymasterArguments args, int format, byte[] keyData, - int uid, int flags, KeyCharacteristics outCharacteristics) { - try { - int error = importKeyInternal(alias, args, format, keyData, uid, flags, - outCharacteristics); - if (error == KEY_ALREADY_EXISTS) { - mBinder.del(alias, uid); - error = importKeyInternal(alias, args, format, keyData, uid, flags, - outCharacteristics); - } - return error; - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to keystore", e); - return SYSTEM_ERROR; - } catch (ExecutionException e) { - Log.e(TAG, "ImportKey completed with exception", e); - return SYSTEM_ERROR; - } - } - - public int importKey(String alias, KeymasterArguments args, int format, byte[] keyData, - int flags, KeyCharacteristics outCharacteristics) { - return importKey(alias, args, format, keyData, UID_SELF, flags, outCharacteristics); - } - - private String getAlgorithmFromPKCS8(byte[] keyData) { - try { - final ASN1InputStream bIn = new ASN1InputStream(new ByteArrayInputStream(keyData)); - final PrivateKeyInfo pki = PrivateKeyInfo.getInstance(bIn.readObject()); - final String algOid = pki.getPrivateKeyAlgorithm().getAlgorithm().getId(); - return new AlgorithmId(new ObjectIdentifier(algOid)).getName(); - } catch (IOException e) { - Log.e(TAG, "getAlgorithmFromPKCS8 Failed to parse key data"); - Log.e(TAG, Log.getStackTraceString(e)); - return null; - } - } - - private KeymasterArguments makeLegacyArguments(String algorithm) { - KeymasterArguments args = new KeymasterArguments(); - args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, - KeyProperties.KeyAlgorithm.toKeymasterAsymmetricKeyAlgorithm(algorithm)); - args.addEnum(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_SIGN); - args.addEnum(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_VERIFY); - args.addEnum(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_ENCRYPT); - args.addEnum(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_DECRYPT); - args.addEnum(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE); - if (algorithm.equalsIgnoreCase(KeyProperties.KEY_ALGORITHM_RSA)) { - args.addEnum(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_RSA_OAEP); - args.addEnum(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT); - args.addEnum(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN); - args.addEnum(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_RSA_PSS); - } - args.addEnum(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_NONE); - args.addEnum(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_MD5); - args.addEnum(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_SHA1); - args.addEnum(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_SHA_2_224); - args.addEnum(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_SHA_2_256); - args.addEnum(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_SHA_2_384); - args.addEnum(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_SHA_2_512); - args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED); - args.addDate(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME, new Date(Long.MAX_VALUE)); - args.addDate(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME, new Date(Long.MAX_VALUE)); - args.addDate(KeymasterDefs.KM_TAG_ACTIVE_DATETIME, new Date(0)); - return args; - } - - public boolean importKey(String alias, byte[] keyData, int uid, int flags) { - String algorithm = getAlgorithmFromPKCS8(keyData); - if (algorithm == null) return false; - KeymasterArguments args = makeLegacyArguments(algorithm); - KeyCharacteristics out = new KeyCharacteristics(); - int result = importKey(alias, args, KeymasterDefs.KM_KEY_FORMAT_PKCS8, keyData, uid, - flags, out); - if (result != NO_ERROR) { - Log.e(TAG, Log.getStackTraceString( - new KeyStoreException(result, "legacy key import failed"))); - return false; - } return true; } - private int importWrappedKeyInternal(String wrappedKeyAlias, byte[] wrappedKey, - String wrappingKeyAlias, - byte[] maskingKey, KeymasterArguments args, long rootSid, long fingerprintSid, - KeyCharacteristics outCharacteristics) - throws RemoteException, ExecutionException { - KeyCharacteristicsPromise promise = new KeyCharacteristicsPromise(); - mBinder.asBinder().linkToDeath(promise, 0); - try { - int error = mBinder.importWrappedKey(promise, wrappedKeyAlias, wrappedKey, - wrappingKeyAlias, maskingKey, args, rootSid, fingerprintSid); - if (error != NO_ERROR) return error; - - KeyCharacteristicsCallbackResult result = interruptedPreservingGet(promise.getFuture()); - - error = result.getKeystoreResponse().getErrorCode(); - if (error != NO_ERROR) return error; - - KeyCharacteristics characteristics = result.getKeyCharacteristics(); - if (characteristics == null) return SYSTEM_ERROR; - outCharacteristics.shallowCopyFrom(characteristics); - return NO_ERROR; - } finally { - mBinder.asBinder().unlinkToDeath(promise, 0); - } - } - - public int importWrappedKey(String wrappedKeyAlias, byte[] wrappedKey, - String wrappingKeyAlias, - byte[] maskingKey, KeymasterArguments args, long rootSid, long fingerprintSid, int uid, - KeyCharacteristics outCharacteristics) { - // TODO b/119217337 uid parameter gets silently ignored. - try { - int error = importWrappedKeyInternal(wrappedKeyAlias, wrappedKey, wrappingKeyAlias, - maskingKey, args, rootSid, fingerprintSid, outCharacteristics); - if (error == KEY_ALREADY_EXISTS) { - mBinder.del(wrappedKeyAlias, UID_SELF); - error = importWrappedKeyInternal(wrappedKeyAlias, wrappedKey, wrappingKeyAlias, - maskingKey, args, rootSid, fingerprintSid, outCharacteristics); - } - return error; - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to keystore", e); - return SYSTEM_ERROR; - } catch (ExecutionException e) { - Log.e(TAG, "ImportWrappedKey completed with exception", e); - return SYSTEM_ERROR; - } - } - - private class ExportKeyPromise - extends android.security.keystore.IKeystoreExportKeyCallback.Stub - implements IBinder.DeathRecipient { - final private CompletableFuture<ExportResult> future = new CompletableFuture<ExportResult>(); - @Override - public void onFinished(ExportResult exportKeyResult) throws android.os.RemoteException { - future.complete(exportKeyResult); - } - public final CompletableFuture<ExportResult> getFuture() { - return future; - } - @Override - public void binderDied() { - future.completeExceptionally(new RemoteException("Keystore died")); - } - }; - - public ExportResult exportKey(String alias, int format, KeymasterBlob clientId, - KeymasterBlob appId, int uid) { - ExportKeyPromise promise = new ExportKeyPromise(); - try { - mBinder.asBinder().linkToDeath(promise, 0); - clientId = clientId != null ? clientId : new KeymasterBlob(new byte[0]); - appId = appId != null ? appId : new KeymasterBlob(new byte[0]); - int error = mBinder.exportKey(promise, alias, format, clientId, appId, uid); - if (error == NO_ERROR) { - return interruptedPreservingGet(promise.getFuture()); - } else { - return new ExportResult(error); - } - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to keystore", e); - return null; - } catch (ExecutionException e) { - Log.e(TAG, "ExportKey completed with exception", e); - return null; - } finally { - mBinder.asBinder().unlinkToDeath(promise, 0); - } - } - public ExportResult exportKey(String alias, int format, KeymasterBlob clientId, - KeymasterBlob appId) { - return exportKey(alias, format, clientId, appId, UID_SELF); - } - - private class OperationPromise - extends android.security.keystore.IKeystoreOperationResultCallback.Stub - implements IBinder.DeathRecipient { - final private CompletableFuture<OperationResult> future = new CompletableFuture<OperationResult>(); - @Override - public void onFinished(OperationResult operationResult) throws android.os.RemoteException { - future.complete(operationResult); - } - public final CompletableFuture<OperationResult> getFuture() { - return future; - } - @Override - public void binderDied() { - future.completeExceptionally(new RemoteException("Keystore died")); - } - }; - - public OperationResult begin(String alias, int purpose, boolean pruneable, - KeymasterArguments args, byte[] entropy, int uid) { - OperationPromise promise = new OperationPromise(); - try { - mBinder.asBinder().linkToDeath(promise, 0); - args = args != null ? args : new KeymasterArguments(); - entropy = entropy != null ? entropy : new byte[0]; - int errorCode = mBinder.begin(promise, getToken(), alias, purpose, pruneable, args, - entropy, uid); - if (errorCode == NO_ERROR) { - return interruptedPreservingGet(promise.getFuture()); - } else { - return new OperationResult(errorCode); - } - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to keystore", e); - return null; - } catch (ExecutionException e) { - Log.e(TAG, "Begin completed with exception", e); - return null; - } finally { - mBinder.asBinder().unlinkToDeath(promise, 0); - } - } - - public OperationResult begin(String alias, int purpose, boolean pruneable, - KeymasterArguments args, byte[] entropy) { - entropy = entropy != null ? entropy : new byte[0]; - args = args != null ? args : new KeymasterArguments(); - return begin(alias, purpose, pruneable, args, entropy, UID_SELF); - } - - public OperationResult update(IBinder token, KeymasterArguments arguments, byte[] input) { - OperationPromise promise = new OperationPromise(); - try { - mBinder.asBinder().linkToDeath(promise, 0); - arguments = arguments != null ? arguments : new KeymasterArguments(); - input = input != null ? input : new byte[0]; - int errorCode = mBinder.update(promise, token, arguments, input); - if (errorCode == NO_ERROR) { - return interruptedPreservingGet(promise.getFuture()); - } else { - return new OperationResult(errorCode); - } - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to keystore", e); - return null; - } catch (ExecutionException e) { - Log.e(TAG, "Update completed with exception", e); - return null; - } finally { - mBinder.asBinder().unlinkToDeath(promise, 0); - } - } - /** - * Android KeyStore finish operation. - * - * @param token Authentication token. - * @param arguments Keymaster arguments - * @param input Optional additional input data. - * @param signature Optional signature to be verified. - * @param entropy Optional additional entropy - * @return OperationResult that will indicate success or error of the operation. + * Forwards the request to clear a UID to Keystore 2.0. + * @hide */ - public OperationResult finish(IBinder token, KeymasterArguments arguments, byte[] input, - byte[] signature, byte[] entropy) { - OperationPromise promise = new OperationPromise(); - try { - mBinder.asBinder().linkToDeath(promise, 0); - arguments = arguments != null ? arguments : new KeymasterArguments(); - entropy = entropy != null ? entropy : new byte[0]; - input = input != null ? input : new byte[0]; - signature = signature != null ? signature : new byte[0]; - int errorCode = mBinder.finish(promise, token, arguments, input, signature, entropy); - if (errorCode == NO_ERROR) { - return interruptedPreservingGet(promise.getFuture()); - } else { - return new OperationResult(errorCode); - } - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to keystore", e); - return null; - } catch (ExecutionException e) { - Log.e(TAG, "Finish completed with exception", e); - return null; - } finally { - mBinder.asBinder().unlinkToDeath(promise, 0); - } - } - - public OperationResult finish(IBinder token, KeymasterArguments arguments, byte[] signature) { - return finish(token, arguments, null, signature, null); + public boolean clearUid(int uid) { + return AndroidKeyStoreMaintenance.clearNamespace(Domain.APP, uid) == 0; } - private class KeystoreResultPromise - extends android.security.keystore.IKeystoreResponseCallback.Stub - implements IBinder.DeathRecipient { - final private CompletableFuture<KeystoreResponse> future = new CompletableFuture<KeystoreResponse>(); - @Override - public void onFinished(KeystoreResponse keystoreResponse) throws android.os.RemoteException { - future.complete(keystoreResponse); - } - public final CompletableFuture<KeystoreResponse> getFuture() { - return future; - } - @Override - public void binderDied() { - future.completeExceptionally(new RemoteException("Keystore died")); - } - }; - - public int abort(IBinder token) { - KeystoreResultPromise promise = new KeystoreResultPromise(); - try { - mBinder.asBinder().linkToDeath(promise, 0); - int errorCode = mBinder.abort(promise, token); - if (errorCode == NO_ERROR) { - return interruptedPreservingGet(promise.getFuture()).getErrorCode(); - } else { - return errorCode; - } - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to keystore", e); - return SYSTEM_ERROR; - } catch (ExecutionException e) { - Log.e(TAG, "Abort completed with exception", e); - return SYSTEM_ERROR; - } finally { - mBinder.asBinder().unlinkToDeath(promise, 0); - } - } /** * Add an authentication record to the keystore authorization table. @@ -1013,191 +136,7 @@ public class KeyStore { * a {@code KeymasterDefs.KM_ERROR_} value or {@code KeyStore} ResponseCode. */ public int addAuthToken(byte[] authToken) { - try { - Authorization.addAuthToken(authToken); - return mBinder.addAuthToken(authToken); - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to keystore", e); - return SYSTEM_ERROR; - } - } - - /** - * Notify keystore that a user's password has changed. - * - * @param userId the user whose password changed. - * @param newPassword the new password or "" if the password was removed. - */ - public boolean onUserPasswordChanged(int userId, String newPassword) { - // Parcel.cpp doesn't support deserializing null strings and treats them as "". Make that - // explicit here. - if (newPassword == null) { - newPassword = ""; - } - try { - return mBinder.onUserPasswordChanged(userId, newPassword) == NO_ERROR; - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to keystore", e); - return false; - } - } - - /** - * Notify keystore that a user was added. - * - * @param userId the new user. - * @param parentId the parent of the new user, or -1 if the user has no parent. If parentId is - * specified then the new user's keystore will be intialized with the same secure lockscreen - * password as the parent. - */ - public void onUserAdded(int userId, int parentId) { - try { - mBinder.onUserAdded(userId, parentId); - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to keystore", e); - } - } - - /** - * Notify keystore that a user was added. - * - * @param userId the new user. - */ - public void onUserAdded(int userId) { - onUserAdded(userId, -1); - } - - /** - * Notify keystore that a user was removed. - * - * @param userId the removed user. - */ - public void onUserRemoved(int userId) { - try { - mBinder.onUserRemoved(userId); - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to keystore", e); - } - } - - public boolean onUserPasswordChanged(String newPassword) { - return onUserPasswordChanged(UserHandle.getUserId(Process.myUid()), newPassword); - } - - /** - * Notify keystore about the latest user locked state. This is to support keyguard-bound key. - */ - public void onUserLockedStateChanged(int userHandle, boolean locked) { - try { - mBinder.onKeyguardVisibilityChanged(locked, userHandle); - } catch (RemoteException e) { - Log.w(TAG, "Failed to update user locked state " + userHandle, e); - } - } - - private class KeyAttestationCallbackResult { - private KeystoreResponse keystoreResponse; - private KeymasterCertificateChain certificateChain; - - public KeyAttestationCallbackResult(KeystoreResponse keystoreResponse, - KeymasterCertificateChain certificateChain) { - this.keystoreResponse = keystoreResponse; - this.certificateChain = certificateChain; - } - - public KeystoreResponse getKeystoreResponse() { - return keystoreResponse; - } - - public void setKeystoreResponse(KeystoreResponse keystoreResponse) { - this.keystoreResponse = keystoreResponse; - } - - public KeymasterCertificateChain getCertificateChain() { - return certificateChain; - } - - public void setCertificateChain(KeymasterCertificateChain certificateChain) { - this.certificateChain = certificateChain; - } - } - - private class CertificateChainPromise - extends android.security.keystore.IKeystoreCertificateChainCallback.Stub - implements IBinder.DeathRecipient { - final private CompletableFuture<KeyAttestationCallbackResult> future = new CompletableFuture<KeyAttestationCallbackResult>(); - @Override - public void onFinished(KeystoreResponse keystoreResponse, - KeymasterCertificateChain certificateChain) throws android.os.RemoteException { - future.complete(new KeyAttestationCallbackResult(keystoreResponse, certificateChain)); - } - public final CompletableFuture<KeyAttestationCallbackResult> getFuture() { - return future; - } - @Override - public void binderDied() { - future.completeExceptionally(new RemoteException("Keystore died")); - } - }; - - - public int attestKey( - String alias, KeymasterArguments params, KeymasterCertificateChain outChain) { - CertificateChainPromise promise = new CertificateChainPromise(); - try { - mBinder.asBinder().linkToDeath(promise, 0); - if (params == null) { - params = new KeymasterArguments(); - } - if (outChain == null) { - outChain = new KeymasterCertificateChain(); - } - int error = mBinder.attestKey(promise, alias, params); - if (error != NO_ERROR) return error; - KeyAttestationCallbackResult result = interruptedPreservingGet(promise.getFuture()); - error = result.getKeystoreResponse().getErrorCode(); - if (error == NO_ERROR) { - outChain.shallowCopyFrom(result.getCertificateChain()); - } - return error; - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to keystore", e); - return SYSTEM_ERROR; - } catch (ExecutionException e) { - Log.e(TAG, "AttestKey completed with exception", e); - return SYSTEM_ERROR; - } finally { - mBinder.asBinder().unlinkToDeath(promise, 0); - } - } - - public int attestDeviceIds(KeymasterArguments params, KeymasterCertificateChain outChain) { - CertificateChainPromise promise = new CertificateChainPromise(); - try { - mBinder.asBinder().linkToDeath(promise, 0); - if (params == null) { - params = new KeymasterArguments(); - } - if (outChain == null) { - outChain = new KeymasterCertificateChain(); - } - int error = mBinder.attestDeviceIds(promise, params); - if (error != NO_ERROR) return error; - KeyAttestationCallbackResult result = interruptedPreservingGet(promise.getFuture()); - error = result.getKeystoreResponse().getErrorCode(); - if (error == NO_ERROR) { - outChain.shallowCopyFrom(result.getCertificateChain()); - } - return error; - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to keystore", e); - return SYSTEM_ERROR; - } catch (ExecutionException e) { - Log.e(TAG, "AttestDevicdeIds completed with exception", e); - return SYSTEM_ERROR; - } finally { - mBinder.asBinder().unlinkToDeath(promise, 0); - } + return Authorization.addAuthToken(authToken); } /** @@ -1205,77 +144,6 @@ public class KeyStore { */ public void onDeviceOffBody() { AndroidKeyStoreMaintenance.onDeviceOffBody(); - try { - mBinder.onDeviceOffBody(); - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to keystore", e); - } - } - - // Keep in sync with confirmationui/1.0/types.hal. - public static final int CONFIRMATIONUI_OK = 0; - public static final int CONFIRMATIONUI_CANCELED = 1; - public static final int CONFIRMATIONUI_ABORTED = 2; - public static final int CONFIRMATIONUI_OPERATION_PENDING = 3; - public static final int CONFIRMATIONUI_IGNORED = 4; - public static final int CONFIRMATIONUI_SYSTEM_ERROR = 5; - public static final int CONFIRMATIONUI_UNIMPLEMENTED = 6; - public static final int CONFIRMATIONUI_UNEXPECTED = 7; - public static final int CONFIRMATIONUI_UIERROR = 0x10000; - public static final int CONFIRMATIONUI_UIERROR_MISSING_GLYPH = 0x10001; - public static final int CONFIRMATIONUI_UIERROR_MESSAGE_TOO_LONG = 0x10002; - public static final int CONFIRMATIONUI_UIERROR_MALFORMED_UTF8_ENCODING = 0x10003; - - /** - * Requests keystore call into the confirmationui HAL to display a prompt. - * - * @param listener the binder to use for callbacks. - * @param promptText the prompt to display. - * @param extraData extra data / nonce from application. - * @param locale the locale as a BCP 47 langauge tag. - * @param uiOptionsAsFlags the UI options to use, as flags. - * @return one of the {@code CONFIRMATIONUI_*} constants, for - * example {@code KeyStore.CONFIRMATIONUI_OK}. - */ - public int presentConfirmationPrompt(IBinder listener, String promptText, byte[] extraData, - String locale, int uiOptionsAsFlags) { - try { - return mBinder.presentConfirmationPrompt(listener, promptText, extraData, locale, - uiOptionsAsFlags); - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to keystore", e); - return CONFIRMATIONUI_SYSTEM_ERROR; - } - } - - /** - * Requests keystore call into the confirmationui HAL to cancel displaying a prompt. - * - * @param listener the binder passed to the {@link #presentConfirmationPrompt} method. - * @return one of the {@code CONFIRMATIONUI_*} constants, for - * example {@code KeyStore.CONFIRMATIONUI_OK}. - */ - public int cancelConfirmationPrompt(IBinder listener) { - try { - return mBinder.cancelConfirmationPrompt(listener); - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to keystore", e); - return CONFIRMATIONUI_SYSTEM_ERROR; - } - } - - /** - * Requests keystore to check if the confirmationui HAL is available. - * - * @return whether the confirmationUI HAL is available. - */ - public boolean isConfirmationPromptSupported() { - try { - return mBinder.isConfirmationPromptSupported(); - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to keystore", e); - return false; - } } /** @@ -1284,143 +152,6 @@ public class KeyStore { */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static KeyStoreException getKeyStoreException(int errorCode) { - if (errorCode > 0) { - // KeyStore layer error - switch (errorCode) { - case NO_ERROR: - return new KeyStoreException(errorCode, "OK"); - case LOCKED: - return new KeyStoreException(errorCode, "User authentication required"); - case UNINITIALIZED: - return new KeyStoreException(errorCode, "Keystore not initialized"); - case SYSTEM_ERROR: - return new KeyStoreException(errorCode, "System error"); - case PERMISSION_DENIED: - return new KeyStoreException(errorCode, "Permission denied"); - case KEY_NOT_FOUND: - return new KeyStoreException(errorCode, "Key not found"); - case VALUE_CORRUPTED: - return new KeyStoreException(errorCode, "Key blob corrupted"); - case OP_AUTH_NEEDED: - return new KeyStoreException(errorCode, "Operation requires authorization"); - case KEY_PERMANENTLY_INVALIDATED: - return new KeyStoreException(errorCode, "Key permanently invalidated"); - default: - return new KeyStoreException(errorCode, String.valueOf(errorCode)); - } - } else { - // Keymaster layer error - switch (errorCode) { - case KeymasterDefs.KM_ERROR_INVALID_AUTHORIZATION_TIMEOUT: - // The name of this parameter significantly differs between Keymaster and - // framework APIs. Use the framework wording to make life easier for developers. - return new KeyStoreException(errorCode, - "Invalid user authentication validity duration"); - default: - return new KeyStoreException(errorCode, - KeymasterDefs.getErrorMessage(errorCode)); - } - } - } - - /** - * Returns an {@link InvalidKeyException} corresponding to the provided - * {@link KeyStoreException}. - */ - public InvalidKeyException getInvalidKeyException( - String keystoreKeyAlias, int uid, KeyStoreException e) { - switch (e.getErrorCode()) { - case LOCKED: - return new UserNotAuthenticatedException(); - case KeymasterDefs.KM_ERROR_KEY_EXPIRED: - return new KeyExpiredException(); - case KeymasterDefs.KM_ERROR_KEY_NOT_YET_VALID: - return new KeyNotYetValidException(); - case KeymasterDefs.KM_ERROR_KEY_USER_NOT_AUTHENTICATED: - case OP_AUTH_NEEDED: - { - // We now need to determine whether the key/operation can become usable if user - // authentication is performed, or whether it can never become usable again. - // User authentication requirements are contained in the key's characteristics. We - // need to check whether these requirements can be be satisfied by asking the user - // to authenticate. - KeyCharacteristics keyCharacteristics = new KeyCharacteristics(); - int getKeyCharacteristicsErrorCode = - getKeyCharacteristics(keystoreKeyAlias, null, null, uid, - keyCharacteristics); - if (getKeyCharacteristicsErrorCode != NO_ERROR) { - return new InvalidKeyException( - "Failed to obtained key characteristics", - getKeyStoreException(getKeyCharacteristicsErrorCode)); - } - List<BigInteger> keySids = - keyCharacteristics.getUnsignedLongs(KeymasterDefs.KM_TAG_USER_SECURE_ID); - if (keySids.isEmpty()) { - // Key is not bound to any SIDs -- no amount of authentication will help here. - return new KeyPermanentlyInvalidatedException(); - } - long rootSid = GateKeeper.getSecureUserId(); - if ((rootSid != 0) && (keySids.contains(KeymasterArguments.toUint64(rootSid)))) { - // One of the key's SIDs is the current root SID -- user can be authenticated - // against that SID. - return new UserNotAuthenticatedException(); - } - - final BiometricManager bm = mContext.getSystemService(BiometricManager.class); - long[] biometricSids = bm.getAuthenticatorIds(); - - // The key must contain every biometric SID. This is because the current API surface - // treats all biometrics (capable of keystore integration) equally. e.g. if the - // device has multiple keystore-capable sensors, and one of the sensor's SIDs - // changed, 1) there is no way for a developer to specify authentication with a - // specific sensor (the one that hasn't changed), and 2) currently the only - // signal to developers is the UserNotAuthenticatedException, which doesn't - // indicate a specific sensor. - boolean canUnlockViaBiometrics = true; - for (long sid : biometricSids) { - if (!keySids.contains(KeymasterArguments.toUint64(sid))) { - canUnlockViaBiometrics = false; - break; - } - } - - if (canUnlockViaBiometrics) { - // All of the biometric SIDs are contained in the key's SIDs. - return new UserNotAuthenticatedException(); - } - - // None of the key's SIDs can ever be authenticated - return new KeyPermanentlyInvalidatedException(); - } - case UNINITIALIZED: - return new KeyPermanentlyInvalidatedException(); - default: - return new InvalidKeyException("Keystore operation failed", e); - } - } - - /** - * Returns an {@link InvalidKeyException} corresponding to the provided keystore/keymaster error - * code. - */ - public InvalidKeyException getInvalidKeyException(String keystoreKeyAlias, int uid, - int errorCode) { - return getInvalidKeyException(keystoreKeyAlias, uid, getKeyStoreException(errorCode)); - } - - private static <R> R interruptedPreservingGet(CompletableFuture<R> future) - throws ExecutionException { - boolean wasInterrupted = false; - while (true) { - try { - R result = future.get(); - if (wasInterrupted) { - Thread.currentThread().interrupt(); - } - return result; - } catch (InterruptedException e) { - wasInterrupted = true; - } - } + return new KeyStoreException(-10000, "Should not be called."); } } diff --git a/keystore/java/android/security/LegacyVpnProfileStore.java b/keystore/java/android/security/LegacyVpnProfileStore.java index 41cfb2707fcf..1d2738e71311 100644 --- a/keystore/java/android/security/LegacyVpnProfileStore.java +++ b/keystore/java/android/security/LegacyVpnProfileStore.java @@ -19,7 +19,6 @@ package android.security; import android.annotation.NonNull; import android.os.ServiceManager; import android.os.ServiceSpecificException; -import android.security.keystore.AndroidKeyStoreProvider; import android.security.vpnprofilestore.IVpnProfileStore; import android.util.Log; @@ -53,13 +52,8 @@ public class LegacyVpnProfileStore { */ public static boolean put(@NonNull String alias, @NonNull byte[] profile) { try { - if (AndroidKeyStoreProvider.isKeystore2Enabled()) { - getService().put(alias, profile); - return true; - } else { - return KeyStore.getInstance().put( - alias, profile, KeyStore.UID_SELF, 0); - } + getService().put(alias, profile); + return true; } catch (Exception e) { Log.e(TAG, "Failed to put vpn profile.", e); return false; @@ -77,11 +71,7 @@ public class LegacyVpnProfileStore { */ public static byte[] get(@NonNull String alias) { try { - if (AndroidKeyStoreProvider.isKeystore2Enabled()) { - return getService().get(alias); - } else { - return KeyStore.getInstance().get(alias, true /* suppressKeyNotFoundWarning */); - } + return getService().get(alias); } catch (ServiceSpecificException e) { if (e.errorCode != PROFILE_NOT_FOUND) { Log.e(TAG, "Failed to get vpn profile.", e); @@ -100,12 +90,8 @@ public class LegacyVpnProfileStore { */ public static boolean remove(@NonNull String alias) { try { - if (AndroidKeyStoreProvider.isKeystore2Enabled()) { - getService().remove(alias); - return true; - } else { - return KeyStore.getInstance().delete(alias); - } + getService().remove(alias); + return true; } catch (ServiceSpecificException e) { if (e.errorCode != PROFILE_NOT_FOUND) { Log.e(TAG, "Failed to remove vpn profile.", e); @@ -124,16 +110,11 @@ public class LegacyVpnProfileStore { */ public static @NonNull String[] list(@NonNull String prefix) { try { - if (AndroidKeyStoreProvider.isKeystore2Enabled()) { - final String[] aliases = getService().list(prefix); - for (int i = 0; i < aliases.length; ++i) { - aliases[i] = aliases[i].substring(prefix.length()); - } - return aliases; - } else { - final String[] result = KeyStore.getInstance().list(prefix); - return result != null ? result : new String[0]; + final String[] aliases = getService().list(prefix); + for (int i = 0; i < aliases.length; ++i) { + aliases[i] = aliases[i].substring(prefix.length()); } + return aliases; } catch (Exception e) { Log.e(TAG, "Failed to list vpn profiles.", e); } diff --git a/keystore/java/android/security/keystore/AndroidKeyStore3DESCipherSpi.java b/keystore/java/android/security/keystore/AndroidKeyStore3DESCipherSpi.java deleted file mode 100644 index 01fd0624572c..000000000000 --- a/keystore/java/android/security/keystore/AndroidKeyStore3DESCipherSpi.java +++ /dev/null @@ -1,298 +0,0 @@ -/* - * Copyright (C) 2018 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.keystore; - -import android.security.keymaster.KeymasterArguments; -import android.security.keymaster.KeymasterDefs; - -import java.security.AlgorithmParameters; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.Key; -import java.security.NoSuchAlgorithmException; -import java.security.ProviderException; -import java.security.spec.AlgorithmParameterSpec; -import java.security.spec.InvalidParameterSpecException; -import java.util.Arrays; - -import javax.crypto.CipherSpi; -import javax.crypto.spec.IvParameterSpec; - -/** - * Base class for Android Keystore 3DES {@link CipherSpi} implementations. - * - * @hide - */ -public class AndroidKeyStore3DESCipherSpi extends AndroidKeyStoreCipherSpiBase { - - private static final int BLOCK_SIZE_BYTES = 8; - - private final int mKeymasterBlockMode; - private final int mKeymasterPadding; - /** Whether this transformation requires an IV. */ - private final boolean mIvRequired; - - private byte[] mIv; - - /** Whether the current {@code #mIv} has been used by the underlying crypto operation. */ - private boolean mIvHasBeenUsed; - - AndroidKeyStore3DESCipherSpi( - int keymasterBlockMode, - int keymasterPadding, - boolean ivRequired) { - mKeymasterBlockMode = keymasterBlockMode; - mKeymasterPadding = keymasterPadding; - mIvRequired = ivRequired; - } - - abstract static class ECB extends AndroidKeyStore3DESCipherSpi { - protected ECB(int keymasterPadding) { - super(KeymasterDefs.KM_MODE_ECB, keymasterPadding, false); - } - - public static class NoPadding extends ECB { - public NoPadding() { - super(KeymasterDefs.KM_PAD_NONE); - } - } - - public static class PKCS7Padding extends ECB { - public PKCS7Padding() { - super(KeymasterDefs.KM_PAD_PKCS7); - } - } - } - - abstract static class CBC extends AndroidKeyStore3DESCipherSpi { - protected CBC(int keymasterPadding) { - super(KeymasterDefs.KM_MODE_CBC, keymasterPadding, true); - } - - public static class NoPadding extends CBC { - public NoPadding() { - super(KeymasterDefs.KM_PAD_NONE); - } - } - - public static class PKCS7Padding extends CBC { - public PKCS7Padding() { - super(KeymasterDefs.KM_PAD_PKCS7); - } - } - } - - @Override - protected void initKey(int i, Key key) throws InvalidKeyException { - if (!(key instanceof AndroidKeyStoreSecretKey)) { - throw new InvalidKeyException( - "Unsupported key: " + ((key != null) ? key.getClass().getName() : "null")); - } - if (!KeyProperties.KEY_ALGORITHM_3DES.equalsIgnoreCase(key.getAlgorithm())) { - throw new InvalidKeyException( - "Unsupported key algorithm: " + key.getAlgorithm() + ". Only " + - KeyProperties.KEY_ALGORITHM_3DES + " supported"); - } - setKey((AndroidKeyStoreSecretKey) key); - } - - @Override - protected int engineGetBlockSize() { - return BLOCK_SIZE_BYTES; - } - - @Override - protected int engineGetOutputSize(int inputLen) { - return inputLen + 3 * BLOCK_SIZE_BYTES; - } - - @Override - protected final byte[] engineGetIV() { - return ArrayUtils.cloneIfNotEmpty(mIv); - } - - @Override - protected AlgorithmParameters engineGetParameters() { - if (!mIvRequired) { - return null; - } - if ((mIv != null) && (mIv.length > 0)) { - try { - AlgorithmParameters params = AlgorithmParameters.getInstance("DESede"); - params.init(new IvParameterSpec(mIv)); - return params; - } catch (NoSuchAlgorithmException e) { - throw new ProviderException( - "Failed to obtain 3DES AlgorithmParameters", e); - } catch (InvalidParameterSpecException e) { - throw new ProviderException( - "Failed to initialize 3DES AlgorithmParameters with an IV", - e); - } - } - return null; - } - - @Override - protected void initAlgorithmSpecificParameters() throws InvalidKeyException { - if (!mIvRequired) { - return; - } - - // IV is used - if (!isEncrypting()) { - throw new InvalidKeyException("IV required when decrypting" - + ". Use IvParameterSpec or AlgorithmParameters to provide it."); - } - } - - @Override - protected void initAlgorithmSpecificParameters(AlgorithmParameterSpec params) - throws InvalidAlgorithmParameterException { - if (!mIvRequired) { - if (params != null) { - throw new InvalidAlgorithmParameterException("Unsupported parameters: " + params); - } - return; - } - - // IV is used - if (params == null) { - if (!isEncrypting()) { - // IV must be provided by the caller - throw new InvalidAlgorithmParameterException( - "IvParameterSpec must be provided when decrypting"); - } - return; - } - if (!(params instanceof IvParameterSpec)) { - throw new InvalidAlgorithmParameterException("Only IvParameterSpec supported"); - } - mIv = ((IvParameterSpec) params).getIV(); - if (mIv == null) { - throw new InvalidAlgorithmParameterException("Null IV in IvParameterSpec"); - } - } - - @Override - protected void initAlgorithmSpecificParameters(AlgorithmParameters params) - throws InvalidAlgorithmParameterException { - if (!mIvRequired) { - if (params != null) { - throw new InvalidAlgorithmParameterException("Unsupported parameters: " + params); - } - return; - } - - // IV is used - if (params == null) { - if (!isEncrypting()) { - // IV must be provided by the caller - throw new InvalidAlgorithmParameterException("IV required when decrypting" - + ". Use IvParameterSpec or AlgorithmParameters to provide it."); - } - return; - } - - if (!"DESede".equalsIgnoreCase(params.getAlgorithm())) { - throw new InvalidAlgorithmParameterException( - "Unsupported AlgorithmParameters algorithm: " + params.getAlgorithm() - + ". Supported: DESede"); - } - - IvParameterSpec ivSpec; - try { - ivSpec = params.getParameterSpec(IvParameterSpec.class); - } catch (InvalidParameterSpecException e) { - if (!isEncrypting()) { - // IV must be provided by the caller - throw new InvalidAlgorithmParameterException("IV required when decrypting" - + ", but not found in parameters: " + params, e); - } - mIv = null; - return; - } - mIv = ivSpec.getIV(); - if (mIv == null) { - throw new InvalidAlgorithmParameterException("Null IV in AlgorithmParameters"); - } - } - - @Override - protected final int getAdditionalEntropyAmountForBegin() { - if ((mIvRequired) && (mIv == null) && (isEncrypting())) { - // IV will need to be generated - return BLOCK_SIZE_BYTES; - } - - return 0; - } - - @Override - protected int getAdditionalEntropyAmountForFinish() { - return 0; - } - - @Override - protected void addAlgorithmSpecificParametersToBegin(KeymasterArguments keymasterArgs) { - if ((isEncrypting()) && (mIvRequired) && (mIvHasBeenUsed)) { - // IV is being reused for encryption: this violates security best practices. - throw new IllegalStateException( - "IV has already been used. Reusing IV in encryption mode violates security best" - + " practices."); - } - - keymasterArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_3DES); - keymasterArgs.addEnum(KeymasterDefs.KM_TAG_BLOCK_MODE, mKeymasterBlockMode); - keymasterArgs.addEnum(KeymasterDefs.KM_TAG_PADDING, mKeymasterPadding); - if ((mIvRequired) && (mIv != null)) { - keymasterArgs.addBytes(KeymasterDefs.KM_TAG_NONCE, mIv); - } - } - - @Override - protected void loadAlgorithmSpecificParametersFromBeginResult( - KeymasterArguments keymasterArgs) { - mIvHasBeenUsed = true; - - // NOTE: Keymaster doesn't always return an IV, even if it's used. - byte[] returnedIv = keymasterArgs.getBytes(KeymasterDefs.KM_TAG_NONCE, null); - if ((returnedIv != null) && (returnedIv.length == 0)) { - returnedIv = null; - } - - if (mIvRequired) { - if (mIv == null) { - mIv = returnedIv; - } else if ((returnedIv != null) && (!Arrays.equals(returnedIv, mIv))) { - throw new ProviderException("IV in use differs from provided IV"); - } - } else { - if (returnedIv != null) { - throw new ProviderException( - "IV in use despite IV not being used by this transformation"); - } - } - } - - @Override - protected final void resetAll() { - mIv = null; - mIvHasBeenUsed = false; - super.resetAll(); - } -} diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java deleted file mode 100644 index feb6101cb080..000000000000 --- a/keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java +++ /dev/null @@ -1,449 +0,0 @@ -/* - * 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.keystore; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.os.IBinder; -import android.security.KeyStore; -import android.security.KeyStoreException; -import android.security.keymaster.KeymasterArguments; -import android.security.keymaster.KeymasterDefs; -import android.security.keymaster.OperationResult; -import android.security.keystore.KeyStoreCryptoOperationChunkedStreamer.Stream; - -import libcore.util.EmptyArray; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.security.AlgorithmParameters; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.Key; -import java.security.NoSuchAlgorithmException; -import java.security.ProviderException; -import java.security.spec.AlgorithmParameterSpec; -import java.security.spec.InvalidParameterSpecException; -import java.util.Arrays; - -import javax.crypto.CipherSpi; -import javax.crypto.spec.GCMParameterSpec; - -/** - * Base class for Android Keystore authenticated AES {@link CipherSpi} implementations. - * - * @hide - */ -abstract class AndroidKeyStoreAuthenticatedAESCipherSpi extends AndroidKeyStoreCipherSpiBase { - - abstract static class GCM extends AndroidKeyStoreAuthenticatedAESCipherSpi { - static final int MIN_SUPPORTED_TAG_LENGTH_BITS = 96; - private static final int MAX_SUPPORTED_TAG_LENGTH_BITS = 128; - private static final int DEFAULT_TAG_LENGTH_BITS = 128; - private static final int IV_LENGTH_BYTES = 12; - - private int mTagLengthBits = DEFAULT_TAG_LENGTH_BITS; - - GCM(int keymasterPadding) { - super(KeymasterDefs.KM_MODE_GCM, keymasterPadding); - } - - @Override - protected final void resetAll() { - mTagLengthBits = DEFAULT_TAG_LENGTH_BITS; - super.resetAll(); - } - - @Override - protected final void resetWhilePreservingInitState() { - super.resetWhilePreservingInitState(); - } - - @Override - protected final void initAlgorithmSpecificParameters() throws InvalidKeyException { - if (!isEncrypting()) { - throw new InvalidKeyException("IV required when decrypting" - + ". Use IvParameterSpec or AlgorithmParameters to provide it."); - } - } - - @Override - protected final void initAlgorithmSpecificParameters(AlgorithmParameterSpec params) - throws InvalidAlgorithmParameterException { - // IV is used - if (params == null) { - if (!isEncrypting()) { - // IV must be provided by the caller - throw new InvalidAlgorithmParameterException( - "GCMParameterSpec must be provided when decrypting"); - } - return; - } - if (!(params instanceof GCMParameterSpec)) { - throw new InvalidAlgorithmParameterException("Only GCMParameterSpec supported"); - } - GCMParameterSpec spec = (GCMParameterSpec) params; - byte[] iv = spec.getIV(); - if (iv == null) { - throw new InvalidAlgorithmParameterException("Null IV in GCMParameterSpec"); - } else if (iv.length != IV_LENGTH_BYTES) { - throw new InvalidAlgorithmParameterException("Unsupported IV length: " - + iv.length + " bytes. Only " + IV_LENGTH_BYTES - + " bytes long IV supported"); - } - int tagLengthBits = spec.getTLen(); - if ((tagLengthBits < MIN_SUPPORTED_TAG_LENGTH_BITS) - || (tagLengthBits > MAX_SUPPORTED_TAG_LENGTH_BITS) - || ((tagLengthBits % 8) != 0)) { - throw new InvalidAlgorithmParameterException( - "Unsupported tag length: " + tagLengthBits + " bits" - + ". Supported lengths: 96, 104, 112, 120, 128"); - } - setIv(iv); - mTagLengthBits = tagLengthBits; - } - - @Override - protected final void initAlgorithmSpecificParameters(AlgorithmParameters params) - throws InvalidAlgorithmParameterException { - if (params == null) { - if (!isEncrypting()) { - // IV must be provided by the caller - throw new InvalidAlgorithmParameterException("IV required when decrypting" - + ". Use GCMParameterSpec or GCM AlgorithmParameters to provide it."); - } - return; - } - - if (!"GCM".equalsIgnoreCase(params.getAlgorithm())) { - throw new InvalidAlgorithmParameterException( - "Unsupported AlgorithmParameters algorithm: " + params.getAlgorithm() - + ". Supported: GCM"); - } - - GCMParameterSpec spec; - try { - spec = params.getParameterSpec(GCMParameterSpec.class); - } catch (InvalidParameterSpecException e) { - if (!isEncrypting()) { - // IV must be provided by the caller - throw new InvalidAlgorithmParameterException("IV and tag length required when" - + " decrypting, but not found in parameters: " + params, e); - } - setIv(null); - return; - } - initAlgorithmSpecificParameters(spec); - } - - @Nullable - @Override - protected final AlgorithmParameters engineGetParameters() { - byte[] iv = getIv(); - if ((iv != null) && (iv.length > 0)) { - try { - AlgorithmParameters params = AlgorithmParameters.getInstance("GCM"); - params.init(new GCMParameterSpec(mTagLengthBits, iv)); - return params; - } catch (NoSuchAlgorithmException e) { - throw new ProviderException( - "Failed to obtain GCM AlgorithmParameters", e); - } catch (InvalidParameterSpecException e) { - throw new ProviderException( - "Failed to initialize GCM AlgorithmParameters", e); - } - } - return null; - } - - @NonNull - @Override - protected KeyStoreCryptoOperationStreamer createMainDataStreamer( - KeyStore keyStore, IBinder operationToken) { - KeyStoreCryptoOperationStreamer streamer = new KeyStoreCryptoOperationChunkedStreamer( - new KeyStoreCryptoOperationChunkedStreamer.MainDataStream( - keyStore, operationToken), 0); - if (isEncrypting()) { - return streamer; - } else { - // When decrypting, to avoid leaking unauthenticated plaintext, do not return any - // plaintext before ciphertext is authenticated by KeyStore.finish. - return new BufferAllOutputUntilDoFinalStreamer(streamer); - } - } - - @NonNull - @Override - protected final KeyStoreCryptoOperationStreamer createAdditionalAuthenticationDataStreamer( - KeyStore keyStore, IBinder operationToken) { - return new KeyStoreCryptoOperationChunkedStreamer( - new AdditionalAuthenticationDataStream(keyStore, operationToken), 0); - } - - @Override - protected final int getAdditionalEntropyAmountForBegin() { - if ((getIv() == null) && (isEncrypting())) { - // IV will need to be generated - return IV_LENGTH_BYTES; - } - - return 0; - } - - @Override - protected final int getAdditionalEntropyAmountForFinish() { - return 0; - } - - @Override - protected final void addAlgorithmSpecificParametersToBegin( - @NonNull KeymasterArguments keymasterArgs) { - super.addAlgorithmSpecificParametersToBegin(keymasterArgs); - keymasterArgs.addUnsignedInt(KeymasterDefs.KM_TAG_MAC_LENGTH, mTagLengthBits); - } - - protected final int getTagLengthBits() { - return mTagLengthBits; - } - - public static final class NoPadding extends GCM { - public NoPadding() { - super(KeymasterDefs.KM_PAD_NONE); - } - - @Override - protected final int engineGetOutputSize(int inputLen) { - int tagLengthBytes = (getTagLengthBits() + 7) / 8; - long result; - if (isEncrypting()) { - result = getConsumedInputSizeBytes() - getProducedOutputSizeBytes() + inputLen - + tagLengthBytes; - } else { - result = getConsumedInputSizeBytes() - getProducedOutputSizeBytes() + inputLen - - tagLengthBytes; - } - if (result < 0) { - return 0; - } else if (result > Integer.MAX_VALUE) { - return Integer.MAX_VALUE; - } - return (int) result; - } - } - } - - private static final int BLOCK_SIZE_BYTES = 16; - - private final int mKeymasterBlockMode; - private final int mKeymasterPadding; - - private byte[] mIv; - - /** Whether the current {@code #mIv} has been used by the underlying crypto operation. */ - private boolean mIvHasBeenUsed; - - AndroidKeyStoreAuthenticatedAESCipherSpi( - int keymasterBlockMode, - int keymasterPadding) { - mKeymasterBlockMode = keymasterBlockMode; - mKeymasterPadding = keymasterPadding; - } - - @Override - protected void resetAll() { - mIv = null; - mIvHasBeenUsed = false; - super.resetAll(); - } - - @Override - protected final void initKey(int opmode, Key key) throws InvalidKeyException { - if (!(key instanceof AndroidKeyStoreSecretKey)) { - throw new InvalidKeyException( - "Unsupported key: " + ((key != null) ? key.getClass().getName() : "null")); - } - if (!KeyProperties.KEY_ALGORITHM_AES.equalsIgnoreCase(key.getAlgorithm())) { - throw new InvalidKeyException( - "Unsupported key algorithm: " + key.getAlgorithm() + ". Only " + - KeyProperties.KEY_ALGORITHM_AES + " supported"); - } - setKey((AndroidKeyStoreSecretKey) key); - } - - @Override - protected void addAlgorithmSpecificParametersToBegin( - @NonNull KeymasterArguments keymasterArgs) { - if ((isEncrypting()) && (mIvHasBeenUsed)) { - // IV is being reused for encryption: this violates security best practices. - throw new IllegalStateException( - "IV has already been used. Reusing IV in encryption mode violates security best" - + " practices."); - } - - keymasterArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES); - keymasterArgs.addEnum(KeymasterDefs.KM_TAG_BLOCK_MODE, mKeymasterBlockMode); - keymasterArgs.addEnum(KeymasterDefs.KM_TAG_PADDING, mKeymasterPadding); - if (mIv != null) { - keymasterArgs.addBytes(KeymasterDefs.KM_TAG_NONCE, mIv); - } - } - - @Override - protected final void loadAlgorithmSpecificParametersFromBeginResult( - @NonNull KeymasterArguments keymasterArgs) { - mIvHasBeenUsed = true; - - // NOTE: Keymaster doesn't always return an IV, even if it's used. - byte[] returnedIv = keymasterArgs.getBytes(KeymasterDefs.KM_TAG_NONCE, null); - if ((returnedIv != null) && (returnedIv.length == 0)) { - returnedIv = null; - } - - if (mIv == null) { - mIv = returnedIv; - } else if ((returnedIv != null) && (!Arrays.equals(returnedIv, mIv))) { - throw new ProviderException("IV in use differs from provided IV"); - } - } - - @Override - protected final int engineGetBlockSize() { - return BLOCK_SIZE_BYTES; - } - - @Override - protected final byte[] engineGetIV() { - return ArrayUtils.cloneIfNotEmpty(mIv); - } - - protected void setIv(byte[] iv) { - mIv = iv; - } - - protected byte[] getIv() { - return mIv; - } - - /** - * {@link KeyStoreCryptoOperationStreamer} which buffers all output until {@code doFinal} from - * which it returns all output in one go, provided {@code doFinal} succeeds. - */ - private static class BufferAllOutputUntilDoFinalStreamer - implements KeyStoreCryptoOperationStreamer { - - private final KeyStoreCryptoOperationStreamer mDelegate; - private ByteArrayOutputStream mBufferedOutput = new ByteArrayOutputStream(); - private long mProducedOutputSizeBytes; - - private BufferAllOutputUntilDoFinalStreamer(KeyStoreCryptoOperationStreamer delegate) { - mDelegate = delegate; - } - - @Override - public byte[] update(byte[] input, int inputOffset, int inputLength) - throws KeyStoreException { - byte[] output = mDelegate.update(input, inputOffset, inputLength); - if (output != null) { - try { - mBufferedOutput.write(output); - } catch (IOException e) { - throw new ProviderException("Failed to buffer output", e); - } - } - return EmptyArray.BYTE; - } - - @Override - public byte[] doFinal(byte[] input, int inputOffset, int inputLength, - byte[] signature, byte[] additionalEntropy) throws KeyStoreException { - byte[] output = mDelegate.doFinal(input, inputOffset, inputLength, signature, - additionalEntropy); - if (output != null) { - try { - mBufferedOutput.write(output); - } catch (IOException e) { - throw new ProviderException("Failed to buffer output", e); - } - } - byte[] result = mBufferedOutput.toByteArray(); - mBufferedOutput.reset(); - mProducedOutputSizeBytes += result.length; - return result; - } - - @Override - public long getConsumedInputSizeBytes() { - return mDelegate.getConsumedInputSizeBytes(); - } - - @Override - public long getProducedOutputSizeBytes() { - return mProducedOutputSizeBytes; - } - } - - /** - * Additional Authentication Data (AAD) stream via a KeyStore streaming operation. This stream - * sends AAD into the KeyStore. - */ - private static class AdditionalAuthenticationDataStream implements Stream { - - private final KeyStore mKeyStore; - private final IBinder mOperationToken; - - private AdditionalAuthenticationDataStream(KeyStore keyStore, IBinder operationToken) { - mKeyStore = keyStore; - mOperationToken = operationToken; - } - - @Override - public OperationResult update(byte[] input) { - KeymasterArguments keymasterArgs = new KeymasterArguments(); - keymasterArgs.addBytes(KeymasterDefs.KM_TAG_ASSOCIATED_DATA, input); - - // KeyStore does not reflect AAD in inputConsumed, but users of Stream rely on this - // field. We fix this discrepancy here. KeyStore.update contract is that all of AAD - // has been consumed if the method succeeds. - OperationResult result = mKeyStore.update(mOperationToken, keymasterArgs, null); - if (result.resultCode == KeyStore.NO_ERROR) { - result = new OperationResult( - result.resultCode, - result.token, - result.operationHandle, - input.length, // inputConsumed - result.output, - result.outParams); - } - return result; - } - - @Override - public OperationResult finish(byte[] input, byte[] signature, byte[] additionalEntropy) { - if ((additionalEntropy != null) && (additionalEntropy.length > 0)) { - throw new ProviderException("AAD stream does not support additional entropy"); - } - return new OperationResult( - KeyStore.NO_ERROR, - mOperationToken, - 0, // operation handle -- nobody cares about this being returned from finish - 0, // inputConsumed - EmptyArray.BYTE, // output - new KeymasterArguments() // additional params returned by finish - ); - } - } -}
\ No newline at end of file diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java deleted file mode 100644 index 5730234184ab..000000000000 --- a/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java +++ /dev/null @@ -1,279 +0,0 @@ -/* - * 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.keystore; - -import java.security.Provider; - -/** - * {@link Provider} of JCA crypto operations operating on Android KeyStore keys. - * - * <p>This provider was separated out of {@link AndroidKeyStoreProvider} to work around the issue - * that Bouncy Castle provider incorrectly declares that it accepts arbitrary keys (incl. Android - * KeyStore ones). This causes JCA to select the Bouncy Castle's implementation of JCA crypto - * operations for Android KeyStore keys unless Android KeyStore's own implementations are installed - * as higher-priority than Bouncy Castle ones. The purpose of this provider is to do just that: to - * offer crypto operations operating on Android KeyStore keys and to be installed at higher priority - * than the Bouncy Castle provider. - * - * <p>Once Bouncy Castle provider is fixed, this provider can be merged into the - * {@code AndroidKeyStoreProvider}. - * - * @hide - */ -public class AndroidKeyStoreBCWorkaroundProvider extends Provider { - - // IMPLEMENTATION NOTE: Class names are hard-coded in this provider to avoid loading these - // classes when this provider is instantiated and installed early on during each app's - // initialization process. - - private static final String PACKAGE_NAME = "android.security.keystore"; - private static final String KEYSTORE_SECRET_KEY_CLASS_NAME = - PACKAGE_NAME + ".AndroidKeyStoreSecretKey"; - private static final String KEYSTORE_PRIVATE_KEY_CLASS_NAME = - PACKAGE_NAME + ".AndroidKeyStorePrivateKey"; - private static final String KEYSTORE_PUBLIC_KEY_CLASS_NAME = - PACKAGE_NAME + ".AndroidKeyStorePublicKey"; - - private static final String DESEDE_SYSTEM_PROPERTY = "ro.hardware.keystore_desede"; - - /** @hide */ - public AndroidKeyStoreBCWorkaroundProvider() { - this("AndroidKeyStoreBCWorkaround"); - } - - /** @hide **/ - public AndroidKeyStoreBCWorkaroundProvider(String providerName) { - super(providerName, - 1.0, - "Android KeyStore security provider to work around Bouncy Castle"); - - // --------------------- javax.crypto.Mac - putMacImpl("HmacSHA1", PACKAGE_NAME + ".AndroidKeyStoreHmacSpi$HmacSHA1"); - put("Alg.Alias.Mac.1.2.840.113549.2.7", "HmacSHA1"); - put("Alg.Alias.Mac.HMAC-SHA1", "HmacSHA1"); - put("Alg.Alias.Mac.HMAC/SHA1", "HmacSHA1"); - - putMacImpl("HmacSHA224", PACKAGE_NAME + ".AndroidKeyStoreHmacSpi$HmacSHA224"); - put("Alg.Alias.Mac.1.2.840.113549.2.9", "HmacSHA224"); - put("Alg.Alias.Mac.HMAC-SHA224", "HmacSHA224"); - put("Alg.Alias.Mac.HMAC/SHA224", "HmacSHA224"); - - putMacImpl("HmacSHA256", PACKAGE_NAME + ".AndroidKeyStoreHmacSpi$HmacSHA256"); - put("Alg.Alias.Mac.1.2.840.113549.2.9", "HmacSHA256"); - put("Alg.Alias.Mac.HMAC-SHA256", "HmacSHA256"); - put("Alg.Alias.Mac.HMAC/SHA256", "HmacSHA256"); - - putMacImpl("HmacSHA384", PACKAGE_NAME + ".AndroidKeyStoreHmacSpi$HmacSHA384"); - put("Alg.Alias.Mac.1.2.840.113549.2.10", "HmacSHA384"); - put("Alg.Alias.Mac.HMAC-SHA384", "HmacSHA384"); - put("Alg.Alias.Mac.HMAC/SHA384", "HmacSHA384"); - - putMacImpl("HmacSHA512", PACKAGE_NAME + ".AndroidKeyStoreHmacSpi$HmacSHA512"); - put("Alg.Alias.Mac.1.2.840.113549.2.11", "HmacSHA512"); - put("Alg.Alias.Mac.HMAC-SHA512", "HmacSHA512"); - put("Alg.Alias.Mac.HMAC/SHA512", "HmacSHA512"); - - // --------------------- javax.crypto.Cipher - putSymmetricCipherImpl("AES/ECB/NoPadding", - PACKAGE_NAME + ".AndroidKeyStoreUnauthenticatedAESCipherSpi$ECB$NoPadding"); - putSymmetricCipherImpl("AES/ECB/PKCS7Padding", - PACKAGE_NAME + ".AndroidKeyStoreUnauthenticatedAESCipherSpi$ECB$PKCS7Padding"); - - putSymmetricCipherImpl("AES/CBC/NoPadding", - PACKAGE_NAME + ".AndroidKeyStoreUnauthenticatedAESCipherSpi$CBC$NoPadding"); - putSymmetricCipherImpl("AES/CBC/PKCS7Padding", - PACKAGE_NAME + ".AndroidKeyStoreUnauthenticatedAESCipherSpi$CBC$PKCS7Padding"); - - putSymmetricCipherImpl("AES/CTR/NoPadding", - PACKAGE_NAME + ".AndroidKeyStoreUnauthenticatedAESCipherSpi$CTR$NoPadding"); - - if ("true".equals(android.os.SystemProperties.get(DESEDE_SYSTEM_PROPERTY))) { - putSymmetricCipherImpl("DESede/CBC/NoPadding", - PACKAGE_NAME + ".AndroidKeyStore3DESCipherSpi$CBC$NoPadding"); - putSymmetricCipherImpl("DESede/CBC/PKCS7Padding", - PACKAGE_NAME + ".AndroidKeyStore3DESCipherSpi$CBC$PKCS7Padding"); - - putSymmetricCipherImpl("DESede/ECB/NoPadding", - PACKAGE_NAME + ".AndroidKeyStore3DESCipherSpi$ECB$NoPadding"); - putSymmetricCipherImpl("DESede/ECB/PKCS7Padding", - PACKAGE_NAME + ".AndroidKeyStore3DESCipherSpi$ECB$PKCS7Padding"); - } - - putSymmetricCipherImpl("AES/GCM/NoPadding", - PACKAGE_NAME + ".AndroidKeyStoreAuthenticatedAESCipherSpi$GCM$NoPadding"); - - putAsymmetricCipherImpl("RSA/ECB/NoPadding", - PACKAGE_NAME + ".AndroidKeyStoreRSACipherSpi$NoPadding"); - put("Alg.Alias.Cipher.RSA/None/NoPadding", "RSA/ECB/NoPadding"); - putAsymmetricCipherImpl("RSA/ECB/PKCS1Padding", - PACKAGE_NAME + ".AndroidKeyStoreRSACipherSpi$PKCS1Padding"); - put("Alg.Alias.Cipher.RSA/None/PKCS1Padding", "RSA/ECB/PKCS1Padding"); - putAsymmetricCipherImpl("RSA/ECB/OAEPPadding", - PACKAGE_NAME + ".AndroidKeyStoreRSACipherSpi$OAEPWithSHA1AndMGF1Padding"); - put("Alg.Alias.Cipher.RSA/None/OAEPPadding", "RSA/ECB/OAEPPadding"); - putAsymmetricCipherImpl("RSA/ECB/OAEPWithSHA-1AndMGF1Padding", - PACKAGE_NAME + ".AndroidKeyStoreRSACipherSpi$OAEPWithSHA1AndMGF1Padding"); - put("Alg.Alias.Cipher.RSA/None/OAEPWithSHA-1AndMGF1Padding", - "RSA/ECB/OAEPWithSHA-1AndMGF1Padding"); - putAsymmetricCipherImpl("RSA/ECB/OAEPWithSHA-224AndMGF1Padding", - PACKAGE_NAME + ".AndroidKeyStoreRSACipherSpi$OAEPWithSHA224AndMGF1Padding"); - put("Alg.Alias.Cipher.RSA/None/OAEPWithSHA-224AndMGF1Padding", - "RSA/ECB/OAEPWithSHA-256AndMGF1Padding"); - putAsymmetricCipherImpl("RSA/ECB/OAEPWithSHA-256AndMGF1Padding", - PACKAGE_NAME + ".AndroidKeyStoreRSACipherSpi$OAEPWithSHA256AndMGF1Padding"); - put("Alg.Alias.Cipher.RSA/None/OAEPWithSHA-256AndMGF1Padding", - "RSA/ECB/OAEPWithSHA-256AndMGF1Padding"); - putAsymmetricCipherImpl("RSA/ECB/OAEPWithSHA-384AndMGF1Padding", - PACKAGE_NAME + ".AndroidKeyStoreRSACipherSpi$OAEPWithSHA384AndMGF1Padding"); - put("Alg.Alias.Cipher.RSA/None/OAEPWithSHA-384AndMGF1Padding", - "RSA/ECB/OAEPWithSHA-384AndMGF1Padding"); - putAsymmetricCipherImpl("RSA/ECB/OAEPWithSHA-512AndMGF1Padding", - PACKAGE_NAME + ".AndroidKeyStoreRSACipherSpi$OAEPWithSHA512AndMGF1Padding"); - put("Alg.Alias.Cipher.RSA/None/OAEPWithSHA-512AndMGF1Padding", - "RSA/ECB/OAEPWithSHA-512AndMGF1Padding"); - - // --------------------- java.security.Signature - putSignatureImpl("NONEwithRSA", - PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$NONEWithPKCS1Padding"); - - putSignatureImpl("MD5withRSA", - PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$MD5WithPKCS1Padding"); - put("Alg.Alias.Signature.MD5WithRSAEncryption", "MD5withRSA"); - put("Alg.Alias.Signature.MD5/RSA", "MD5withRSA"); - put("Alg.Alias.Signature.1.2.840.113549.1.1.4", "MD5withRSA"); - put("Alg.Alias.Signature.1.2.840.113549.2.5with1.2.840.113549.1.1.1", "MD5withRSA"); - - putSignatureImpl("SHA1withRSA", - PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA1WithPKCS1Padding"); - put("Alg.Alias.Signature.SHA1WithRSAEncryption", "SHA1withRSA"); - put("Alg.Alias.Signature.SHA1/RSA", "SHA1withRSA"); - put("Alg.Alias.Signature.SHA-1/RSA", "SHA1withRSA"); - put("Alg.Alias.Signature.1.2.840.113549.1.1.5", "SHA1withRSA"); - put("Alg.Alias.Signature.1.3.14.3.2.26with1.2.840.113549.1.1.1", "SHA1withRSA"); - put("Alg.Alias.Signature.1.3.14.3.2.26with1.2.840.113549.1.1.5", "SHA1withRSA"); - put("Alg.Alias.Signature.1.3.14.3.2.29", "SHA1withRSA"); - - putSignatureImpl("SHA224withRSA", - PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA224WithPKCS1Padding"); - put("Alg.Alias.Signature.SHA224WithRSAEncryption", "SHA224withRSA"); - put("Alg.Alias.Signature.1.2.840.113549.1.1.11", "SHA224withRSA"); - put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.4with1.2.840.113549.1.1.1", - "SHA224withRSA"); - put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.4with1.2.840.113549.1.1.11", - "SHA224withRSA"); - - putSignatureImpl("SHA256withRSA", - PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA256WithPKCS1Padding"); - put("Alg.Alias.Signature.SHA256WithRSAEncryption", "SHA256withRSA"); - put("Alg.Alias.Signature.1.2.840.113549.1.1.11", "SHA256withRSA"); - put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.1with1.2.840.113549.1.1.1", - "SHA256withRSA"); - put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.1with1.2.840.113549.1.1.11", - "SHA256withRSA"); - - putSignatureImpl("SHA384withRSA", - PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA384WithPKCS1Padding"); - put("Alg.Alias.Signature.SHA384WithRSAEncryption", "SHA384withRSA"); - put("Alg.Alias.Signature.1.2.840.113549.1.1.12", "SHA384withRSA"); - put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.2with1.2.840.113549.1.1.1", - "SHA384withRSA"); - - putSignatureImpl("SHA512withRSA", - PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA512WithPKCS1Padding"); - put("Alg.Alias.Signature.SHA512WithRSAEncryption", "SHA512withRSA"); - put("Alg.Alias.Signature.1.2.840.113549.1.1.13", "SHA512withRSA"); - put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.3with1.2.840.113549.1.1.1", - "SHA512withRSA"); - - putSignatureImpl("SHA1withRSA/PSS", - PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA1WithPSSPadding"); - putSignatureImpl("SHA224withRSA/PSS", - PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA224WithPSSPadding"); - putSignatureImpl("SHA256withRSA/PSS", - PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA256WithPSSPadding"); - putSignatureImpl("SHA384withRSA/PSS", - PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA384WithPSSPadding"); - putSignatureImpl("SHA512withRSA/PSS", - PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA512WithPSSPadding"); - - putSignatureImpl("NONEwithECDSA", - PACKAGE_NAME + ".AndroidKeyStoreECDSASignatureSpi$NONE"); - - putSignatureImpl("SHA1withECDSA", PACKAGE_NAME + ".AndroidKeyStoreECDSASignatureSpi$SHA1"); - put("Alg.Alias.Signature.ECDSA", "SHA1withECDSA"); - put("Alg.Alias.Signature.ECDSAwithSHA1", "SHA1withECDSA"); - // iso(1) member-body(2) us(840) ansi-x962(10045) signatures(4) ecdsa-with-SHA1(1) - put("Alg.Alias.Signature.1.2.840.10045.4.1", "SHA1withECDSA"); - put("Alg.Alias.Signature.1.3.14.3.2.26with1.2.840.10045.2.1", "SHA1withECDSA"); - - // iso(1) member-body(2) us(840) ansi-x962(10045) signatures(4) ecdsa-with-SHA2(3) - putSignatureImpl("SHA224withECDSA", - PACKAGE_NAME + ".AndroidKeyStoreECDSASignatureSpi$SHA224"); - // ecdsa-with-SHA224(1) - put("Alg.Alias.Signature.1.2.840.10045.4.3.1", "SHA224withECDSA"); - put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.4with1.2.840.10045.2.1", "SHA224withECDSA"); - - // iso(1) member-body(2) us(840) ansi-x962(10045) signatures(4) ecdsa-with-SHA2(3) - putSignatureImpl("SHA256withECDSA", - PACKAGE_NAME + ".AndroidKeyStoreECDSASignatureSpi$SHA256"); - // ecdsa-with-SHA256(2) - put("Alg.Alias.Signature.1.2.840.10045.4.3.2", "SHA256withECDSA"); - put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.1with1.2.840.10045.2.1", "SHA256withECDSA"); - - putSignatureImpl("SHA384withECDSA", - PACKAGE_NAME + ".AndroidKeyStoreECDSASignatureSpi$SHA384"); - // ecdsa-with-SHA384(3) - put("Alg.Alias.Signature.1.2.840.10045.4.3.3", "SHA384withECDSA"); - put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.2with1.2.840.10045.2.1", "SHA384withECDSA"); - - putSignatureImpl("SHA512withECDSA", - PACKAGE_NAME + ".AndroidKeyStoreECDSASignatureSpi$SHA512"); - // ecdsa-with-SHA512(4) - put("Alg.Alias.Signature.1.2.840.10045.4.3.4", "SHA512withECDSA"); - put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.3with1.2.840.10045.2.1", "SHA512withECDSA"); - } - - private void putMacImpl(String algorithm, String implClass) { - put("Mac." + algorithm, implClass); - put("Mac." + algorithm + " SupportedKeyClasses", KEYSTORE_SECRET_KEY_CLASS_NAME); - } - - private void putSymmetricCipherImpl(String transformation, String implClass) { - put("Cipher." + transformation, implClass); - put("Cipher." + transformation + " SupportedKeyClasses", KEYSTORE_SECRET_KEY_CLASS_NAME); - } - - private void putAsymmetricCipherImpl(String transformation, String implClass) { - put("Cipher." + transformation, implClass); - put("Cipher." + transformation + " SupportedKeyClasses", - KEYSTORE_PRIVATE_KEY_CLASS_NAME + "|" + KEYSTORE_PUBLIC_KEY_CLASS_NAME); - } - - private void putSignatureImpl(String algorithm, String implClass) { - put("Signature." + algorithm, implClass); - put("Signature." + algorithm + " SupportedKeyClasses", - KEYSTORE_PRIVATE_KEY_CLASS_NAME + "|" + KEYSTORE_PUBLIC_KEY_CLASS_NAME); - } - - public static String[] getSupportedEcdsaSignatureDigests() { - return new String[] {"NONE", "SHA-1", "SHA-224", "SHA-256", "SHA-384", "SHA-512"}; - } - - public static String[] getSupportedRsaSignatureWithPkcs1PaddingDigests() { - return new String[] {"NONE", "MD5", "SHA-1", "SHA-224", "SHA-256", "SHA-384", "SHA-512"}; - } -} diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java b/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java deleted file mode 100644 index ccc3153749fb..000000000000 --- a/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java +++ /dev/null @@ -1,920 +0,0 @@ -/* - * 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.keystore; - -import android.annotation.CallSuper; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.os.IBinder; -import android.security.KeyStore; -import android.security.KeyStoreException; -import android.security.keymaster.KeymasterArguments; -import android.security.keymaster.KeymasterDefs; -import android.security.keymaster.OperationResult; - -import libcore.util.EmptyArray; - -import java.nio.BufferOverflowException; -import java.nio.ByteBuffer; -import java.security.AlgorithmParameters; -import java.security.GeneralSecurityException; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.InvalidParameterException; -import java.security.Key; -import java.security.KeyFactory; -import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.ProviderException; -import java.security.PublicKey; -import java.security.SecureRandom; -import java.security.spec.AlgorithmParameterSpec; -import java.security.spec.InvalidKeySpecException; -import java.security.spec.PKCS8EncodedKeySpec; -import java.security.spec.X509EncodedKeySpec; - -import javax.crypto.AEADBadTagException; -import javax.crypto.BadPaddingException; -import javax.crypto.Cipher; -import javax.crypto.CipherSpi; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.NoSuchPaddingException; -import javax.crypto.SecretKey; -import javax.crypto.SecretKeyFactory; -import javax.crypto.ShortBufferException; -import javax.crypto.spec.SecretKeySpec; - -/** - * Base class for {@link CipherSpi} implementations of Android KeyStore backed ciphers. - * - * @hide - */ -abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStoreCryptoOperation { - private final KeyStore mKeyStore; - - // Fields below are populated by Cipher.init and KeyStore.begin and should be preserved after - // doFinal finishes. - private boolean mEncrypting; - private int mKeymasterPurposeOverride = -1; - private AndroidKeyStoreKey mKey; - private SecureRandom mRng; - - /** - * Token referencing this operation inside keystore service. It is initialized by - * {@code engineInit} and is invalidated when {@code engineDoFinal} succeeds and on some error - * conditions in between. - */ - private IBinder mOperationToken; - private long mOperationHandle; - private KeyStoreCryptoOperationStreamer mMainDataStreamer; - private KeyStoreCryptoOperationStreamer mAdditionalAuthenticationDataStreamer; - private boolean mAdditionalAuthenticationDataStreamerClosed; - - /** - * 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; - - AndroidKeyStoreCipherSpiBase() { - mKeyStore = KeyStore.getInstance(); - } - - @Override - protected final void engineInit(int opmode, Key key, SecureRandom random) - throws InvalidKeyException { - resetAll(); - - boolean success = false; - try { - init(opmode, key, random); - initAlgorithmSpecificParameters(); - try { - ensureKeystoreOperationInitialized(); - } catch (InvalidAlgorithmParameterException e) { - throw new InvalidKeyException(e); - } - success = true; - } finally { - if (!success) { - resetAll(); - } - } - } - - @Override - protected final void engineInit(int opmode, Key key, AlgorithmParameters params, - SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { - resetAll(); - - boolean success = false; - try { - init(opmode, key, random); - initAlgorithmSpecificParameters(params); - ensureKeystoreOperationInitialized(); - success = true; - } finally { - if (!success) { - resetAll(); - } - } - } - - @Override - protected final void engineInit(int opmode, Key key, AlgorithmParameterSpec params, - SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { - resetAll(); - - boolean success = false; - try { - init(opmode, key, random); - initAlgorithmSpecificParameters(params); - ensureKeystoreOperationInitialized(); - success = true; - } finally { - if (!success) { - resetAll(); - } - } - } - - private void init(int opmode, Key key, SecureRandom random) throws InvalidKeyException { - switch (opmode) { - case Cipher.ENCRYPT_MODE: - case Cipher.WRAP_MODE: - mEncrypting = true; - break; - case Cipher.DECRYPT_MODE: - case Cipher.UNWRAP_MODE: - mEncrypting = false; - break; - default: - throw new InvalidParameterException("Unsupported opmode: " + opmode); - } - initKey(opmode, key); - if (mKey == null) { - throw new ProviderException("initKey did not initialize the key"); - } - mRng = random; - } - - /** - * Resets this cipher to its pristine pre-init state. This must be equivalent to obtaining a new - * cipher instance. - * - * <p>Subclasses storing additional state should override this method, reset the additional - * state, and then chain to superclass. - */ - @CallSuper - protected void resetAll() { - IBinder operationToken = mOperationToken; - if (operationToken != null) { - mKeyStore.abort(operationToken); - } - mEncrypting = false; - mKeymasterPurposeOverride = -1; - mKey = null; - mRng = null; - mOperationToken = null; - mOperationHandle = 0; - mMainDataStreamer = null; - mAdditionalAuthenticationDataStreamer = null; - mAdditionalAuthenticationDataStreamerClosed = false; - mCachedException = null; - } - - /** - * Resets this cipher while preserving the initialized state. This must be equivalent to - * rolling back the cipher's state to just after the most recent {@code engineInit} completed - * successfully. - * - * <p>Subclasses storing additional post-init state should override this method, reset the - * additional state, and then chain to superclass. - */ - @CallSuper - protected void resetWhilePreservingInitState() { - IBinder operationToken = mOperationToken; - if (operationToken != null) { - mKeyStore.abort(operationToken); - } - mOperationToken = null; - mOperationHandle = 0; - mMainDataStreamer = null; - mAdditionalAuthenticationDataStreamer = null; - mAdditionalAuthenticationDataStreamerClosed = false; - mCachedException = null; - } - - private void ensureKeystoreOperationInitialized() throws InvalidKeyException, - InvalidAlgorithmParameterException { - if (mMainDataStreamer != null) { - return; - } - if (mCachedException != null) { - return; - } - if (mKey == null) { - throw new IllegalStateException("Not initialized"); - } - - KeymasterArguments keymasterInputArgs = new KeymasterArguments(); - addAlgorithmSpecificParametersToBegin(keymasterInputArgs); - byte[] additionalEntropy = KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng( - mRng, getAdditionalEntropyAmountForBegin()); - - int purpose; - if (mKeymasterPurposeOverride != -1) { - purpose = mKeymasterPurposeOverride; - } else { - purpose = mEncrypting - ? KeymasterDefs.KM_PURPOSE_ENCRYPT : KeymasterDefs.KM_PURPOSE_DECRYPT; - } - OperationResult opResult = mKeyStore.begin( - mKey.getAlias(), - purpose, - true, // permit aborting this operation if keystore runs out of resources - keymasterInputArgs, - additionalEntropy, - mKey.getUid()); - if (opResult == null) { - throw new KeyStoreConnectException(); - } - - // Store operation token and handle regardless of the error code returned by KeyStore to - // ensure that the operation gets aborted immediately if the code below throws an exception. - mOperationToken = opResult.token; - mOperationHandle = opResult.operationHandle; - - // If necessary, throw an exception due to KeyStore operation having failed. - GeneralSecurityException e = KeyStoreCryptoOperationUtils.getExceptionForCipherInit( - mKeyStore, mKey, opResult.resultCode); - if (e != null) { - if (e instanceof InvalidKeyException) { - throw (InvalidKeyException) e; - } else if (e instanceof InvalidAlgorithmParameterException) { - throw (InvalidAlgorithmParameterException) e; - } else { - throw new ProviderException("Unexpected exception type", e); - } - } - - if (mOperationToken == null) { - throw new ProviderException("Keystore returned null operation token"); - } - if (mOperationHandle == 0) { - throw new ProviderException("Keystore returned invalid operation handle"); - } - - loadAlgorithmSpecificParametersFromBeginResult(opResult.outParams); - mMainDataStreamer = createMainDataStreamer(mKeyStore, opResult.token); - mAdditionalAuthenticationDataStreamer = - createAdditionalAuthenticationDataStreamer(mKeyStore, opResult.token); - mAdditionalAuthenticationDataStreamerClosed = false; - } - - /** - * Creates a streamer which sends plaintext/ciphertext into the provided KeyStore and receives - * the corresponding ciphertext/plaintext from the KeyStore. - * - * <p>This implementation returns a working streamer. - */ - @NonNull - protected KeyStoreCryptoOperationStreamer createMainDataStreamer( - KeyStore keyStore, IBinder operationToken) { - return new KeyStoreCryptoOperationChunkedStreamer( - new KeyStoreCryptoOperationChunkedStreamer.MainDataStream( - keyStore, operationToken), 0); - } - - /** - * Creates a streamer which sends Additional Authentication Data (AAD) into the KeyStore. - * - * <p>This implementation returns {@code null}. - * - * @return stream or {@code null} if AAD is not supported by this cipher. - */ - @Nullable - protected KeyStoreCryptoOperationStreamer createAdditionalAuthenticationDataStreamer( - @SuppressWarnings("unused") KeyStore keyStore, - @SuppressWarnings("unused") IBinder operationToken) { - return null; - } - - @Override - protected final byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) { - if (mCachedException != null) { - return null; - } - try { - ensureKeystoreOperationInitialized(); - } catch (InvalidKeyException | InvalidAlgorithmParameterException e) { - mCachedException = e; - return null; - } - - if (inputLen == 0) { - return null; - } - - byte[] output; - try { - flushAAD(); - output = mMainDataStreamer.update(input, inputOffset, inputLen); - } catch (KeyStoreException e) { - mCachedException = e; - return null; - } - - if (output.length == 0) { - return null; - } - - return output; - } - - private void flushAAD() throws KeyStoreException { - if ((mAdditionalAuthenticationDataStreamer != null) - && (!mAdditionalAuthenticationDataStreamerClosed)) { - byte[] output; - try { - output = mAdditionalAuthenticationDataStreamer.doFinal( - EmptyArray.BYTE, 0, 0, - null, // no signature - null // no additional entropy needed flushing AAD - ); - } finally { - mAdditionalAuthenticationDataStreamerClosed = true; - } - if ((output != null) && (output.length > 0)) { - throw new ProviderException( - "AAD update unexpectedly returned data: " + output.length + " bytes"); - } - } - } - - @Override - protected final int engineUpdate(byte[] input, int inputOffset, int inputLen, byte[] output, - int outputOffset) throws ShortBufferException { - byte[] outputCopy = engineUpdate(input, inputOffset, inputLen); - if (outputCopy == null) { - return 0; - } - int outputAvailable = output.length - outputOffset; - if (outputCopy.length > outputAvailable) { - throw new ShortBufferException("Output buffer too short. Produced: " - + outputCopy.length + ", available: " + outputAvailable); - } - System.arraycopy(outputCopy, 0, output, outputOffset, outputCopy.length); - return outputCopy.length; - } - - @Override - protected final int engineUpdate(ByteBuffer input, ByteBuffer output) - throws ShortBufferException { - if (input == null) { - throw new NullPointerException("input == null"); - } - if (output == null) { - throw new NullPointerException("output == null"); - } - - int inputSize = input.remaining(); - byte[] outputArray; - if (input.hasArray()) { - outputArray = - engineUpdate( - input.array(), input.arrayOffset() + input.position(), inputSize); - input.position(input.position() + inputSize); - } else { - byte[] inputArray = new byte[inputSize]; - input.get(inputArray); - outputArray = engineUpdate(inputArray, 0, inputSize); - } - - int outputSize = (outputArray != null) ? outputArray.length : 0; - if (outputSize > 0) { - int outputBufferAvailable = output.remaining(); - try { - output.put(outputArray); - } catch (BufferOverflowException e) { - throw new ShortBufferException( - "Output buffer too small. Produced: " + outputSize + ", available: " - + outputBufferAvailable); - } - } - return outputSize; - } - - @Override - protected final void engineUpdateAAD(byte[] input, int inputOffset, int inputLen) { - if (mCachedException != null) { - return; - } - - try { - ensureKeystoreOperationInitialized(); - } catch (InvalidKeyException | InvalidAlgorithmParameterException e) { - mCachedException = e; - return; - } - - if (mAdditionalAuthenticationDataStreamerClosed) { - throw new IllegalStateException( - "AAD can only be provided before Cipher.update is invoked"); - } - - if (mAdditionalAuthenticationDataStreamer == null) { - throw new IllegalStateException("This cipher does not support AAD"); - } - - byte[] output; - try { - output = mAdditionalAuthenticationDataStreamer.update(input, inputOffset, inputLen); - } catch (KeyStoreException e) { - mCachedException = e; - return; - } - - if ((output != null) && (output.length > 0)) { - throw new ProviderException("AAD update unexpectedly produced output: " - + output.length + " bytes"); - } - } - - @Override - protected final void engineUpdateAAD(ByteBuffer src) { - if (src == null) { - throw new IllegalArgumentException("src == null"); - } - if (!src.hasRemaining()) { - return; - } - - byte[] input; - int inputOffset; - int inputLen; - if (src.hasArray()) { - input = src.array(); - inputOffset = src.arrayOffset() + src.position(); - inputLen = src.remaining(); - src.position(src.limit()); - } else { - input = new byte[src.remaining()]; - inputOffset = 0; - inputLen = input.length; - src.get(input); - } - engineUpdateAAD(input, inputOffset, inputLen); - } - - @Override - protected final byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen) - throws IllegalBlockSizeException, BadPaddingException { - if (mCachedException != null) { - throw (IllegalBlockSizeException) - new IllegalBlockSizeException().initCause(mCachedException); - } - - try { - ensureKeystoreOperationInitialized(); - } catch (InvalidKeyException | InvalidAlgorithmParameterException e) { - throw (IllegalBlockSizeException) new IllegalBlockSizeException().initCause(e); - } - - byte[] output; - try { - flushAAD(); - byte[] additionalEntropy = - KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng( - mRng, getAdditionalEntropyAmountForFinish()); - output = mMainDataStreamer.doFinal( - input, inputOffset, inputLen, - null, // no signature involved - additionalEntropy); - } catch (KeyStoreException e) { - switch (e.getErrorCode()) { - case KeymasterDefs.KM_ERROR_INVALID_INPUT_LENGTH: - throw (IllegalBlockSizeException) new IllegalBlockSizeException().initCause(e); - case KeymasterDefs.KM_ERROR_INVALID_ARGUMENT: - throw (BadPaddingException) new BadPaddingException().initCause(e); - case KeymasterDefs.KM_ERROR_VERIFICATION_FAILED: - throw (AEADBadTagException) new AEADBadTagException().initCause(e); - default: - throw (IllegalBlockSizeException) new IllegalBlockSizeException().initCause(e); - } - } - - resetWhilePreservingInitState(); - return output; - } - - @Override - protected final int engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[] output, - int outputOffset) throws ShortBufferException, IllegalBlockSizeException, - BadPaddingException { - byte[] outputCopy = engineDoFinal(input, inputOffset, inputLen); - if (outputCopy == null) { - return 0; - } - int outputAvailable = output.length - outputOffset; - if (outputCopy.length > outputAvailable) { - throw new ShortBufferException("Output buffer too short. Produced: " - + outputCopy.length + ", available: " + outputAvailable); - } - System.arraycopy(outputCopy, 0, output, outputOffset, outputCopy.length); - return outputCopy.length; - } - - @Override - protected final int engineDoFinal(ByteBuffer input, ByteBuffer output) - throws ShortBufferException, IllegalBlockSizeException, BadPaddingException { - if (input == null) { - throw new NullPointerException("input == null"); - } - if (output == null) { - throw new NullPointerException("output == null"); - } - - int inputSize = input.remaining(); - byte[] outputArray; - if (input.hasArray()) { - outputArray = - engineDoFinal( - input.array(), input.arrayOffset() + input.position(), inputSize); - input.position(input.position() + inputSize); - } else { - byte[] inputArray = new byte[inputSize]; - input.get(inputArray); - outputArray = engineDoFinal(inputArray, 0, inputSize); - } - - int outputSize = (outputArray != null) ? outputArray.length : 0; - if (outputSize > 0) { - int outputBufferAvailable = output.remaining(); - try { - output.put(outputArray); - } catch (BufferOverflowException e) { - throw new ShortBufferException( - "Output buffer too small. Produced: " + outputSize + ", available: " - + outputBufferAvailable); - } - } - return outputSize; - } - - @Override - protected final byte[] engineWrap(Key key) - throws IllegalBlockSizeException, InvalidKeyException { - if (mKey == null) { - throw new IllegalStateException("Not initilized"); - } - - if (!isEncrypting()) { - throw new IllegalStateException( - "Cipher must be initialized in Cipher.WRAP_MODE to wrap keys"); - } - - if (key == null) { - throw new NullPointerException("key == null"); - } - byte[] encoded = null; - if (key instanceof SecretKey) { - if ("RAW".equalsIgnoreCase(key.getFormat())) { - encoded = key.getEncoded(); - } - if (encoded == null) { - try { - SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(key.getAlgorithm()); - SecretKeySpec spec = - (SecretKeySpec) keyFactory.getKeySpec( - (SecretKey) key, SecretKeySpec.class); - encoded = spec.getEncoded(); - } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { - throw new InvalidKeyException( - "Failed to wrap key because it does not export its key material", - e); - } - } - } else if (key instanceof PrivateKey) { - if ("PKCS8".equalsIgnoreCase(key.getFormat())) { - encoded = key.getEncoded(); - } - if (encoded == null) { - try { - KeyFactory keyFactory = KeyFactory.getInstance(key.getAlgorithm()); - PKCS8EncodedKeySpec spec = - keyFactory.getKeySpec(key, PKCS8EncodedKeySpec.class); - encoded = spec.getEncoded(); - } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { - throw new InvalidKeyException( - "Failed to wrap key because it does not export its key material", - e); - } - } - } else if (key instanceof PublicKey) { - if ("X.509".equalsIgnoreCase(key.getFormat())) { - encoded = key.getEncoded(); - } - if (encoded == null) { - try { - KeyFactory keyFactory = KeyFactory.getInstance(key.getAlgorithm()); - X509EncodedKeySpec spec = - keyFactory.getKeySpec(key, X509EncodedKeySpec.class); - encoded = spec.getEncoded(); - } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { - throw new InvalidKeyException( - "Failed to wrap key because it does not export its key material", - e); - } - } - } else { - throw new InvalidKeyException("Unsupported key type: " + key.getClass().getName()); - } - - if (encoded == null) { - throw new InvalidKeyException( - "Failed to wrap key because it does not export its key material"); - } - - try { - return engineDoFinal(encoded, 0, encoded.length); - } catch (BadPaddingException e) { - throw (IllegalBlockSizeException) new IllegalBlockSizeException().initCause(e); - } - } - - @Override - protected final Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm, - int wrappedKeyType) throws InvalidKeyException, NoSuchAlgorithmException { - if (mKey == null) { - throw new IllegalStateException("Not initilized"); - } - - if (isEncrypting()) { - throw new IllegalStateException( - "Cipher must be initialized in Cipher.WRAP_MODE to wrap keys"); - } - - if (wrappedKey == null) { - throw new NullPointerException("wrappedKey == null"); - } - - byte[] encoded; - try { - encoded = engineDoFinal(wrappedKey, 0, wrappedKey.length); - } catch (IllegalBlockSizeException | BadPaddingException e) { - throw new InvalidKeyException("Failed to unwrap key", e); - } - - switch (wrappedKeyType) { - case Cipher.SECRET_KEY: - { - return new SecretKeySpec(encoded, wrappedKeyAlgorithm); - // break; - } - case Cipher.PRIVATE_KEY: - { - KeyFactory keyFactory = KeyFactory.getInstance(wrappedKeyAlgorithm); - try { - return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encoded)); - } catch (InvalidKeySpecException e) { - throw new InvalidKeyException( - "Failed to create private key from its PKCS#8 encoded form", e); - } - // break; - } - case Cipher.PUBLIC_KEY: - { - KeyFactory keyFactory = KeyFactory.getInstance(wrappedKeyAlgorithm); - try { - return keyFactory.generatePublic(new X509EncodedKeySpec(encoded)); - } catch (InvalidKeySpecException e) { - throw new InvalidKeyException( - "Failed to create public key from its X.509 encoded form", e); - } - // break; - } - default: - throw new InvalidParameterException( - "Unsupported wrappedKeyType: " + wrappedKeyType); - } - } - - @Override - protected final void engineSetMode(String mode) throws NoSuchAlgorithmException { - // This should never be invoked because all algorithms registered with the AndroidKeyStore - // provide explicitly specify block mode. - throw new UnsupportedOperationException(); - } - - @Override - protected final void engineSetPadding(String arg0) throws NoSuchPaddingException { - // This should never be invoked because all algorithms registered with the AndroidKeyStore - // provide explicitly specify padding mode. - throw new UnsupportedOperationException(); - } - - @Override - protected final int engineGetKeySize(Key key) throws InvalidKeyException { - throw new UnsupportedOperationException(); - } - - @CallSuper - @Override - public void finalize() throws Throwable { - try { - IBinder operationToken = mOperationToken; - if (operationToken != null) { - mKeyStore.abort(operationToken); - } - } finally { - super.finalize(); - } - } - - @Override - public final long getOperationHandle() { - return mOperationHandle; - } - - protected final void setKey(@NonNull AndroidKeyStoreKey key) { - mKey = key; - } - - /** - * Overrides the default purpose/type of the crypto operation. - */ - protected final void setKeymasterPurposeOverride(int keymasterPurpose) { - mKeymasterPurposeOverride = keymasterPurpose; - } - - protected final int getKeymasterPurposeOverride() { - return mKeymasterPurposeOverride; - } - - /** - * Returns {@code true} if this cipher is initialized for encryption, {@code false} if this - * cipher is initialized for decryption. - */ - protected final boolean isEncrypting() { - return mEncrypting; - } - - @NonNull - protected final KeyStore getKeyStore() { - return mKeyStore; - } - - protected final long getConsumedInputSizeBytes() { - if (mMainDataStreamer == null) { - throw new IllegalStateException("Not initialized"); - } - return mMainDataStreamer.getConsumedInputSizeBytes(); - } - - protected final long getProducedOutputSizeBytes() { - if (mMainDataStreamer == null) { - throw new IllegalStateException("Not initialized"); - } - return mMainDataStreamer.getProducedOutputSizeBytes(); - } - - static String opmodeToString(int opmode) { - switch (opmode) { - case Cipher.ENCRYPT_MODE: - return "ENCRYPT_MODE"; - case Cipher.DECRYPT_MODE: - return "DECRYPT_MODE"; - case Cipher.WRAP_MODE: - return "WRAP_MODE"; - case Cipher.UNWRAP_MODE: - return "UNWRAP_MODE"; - default: - return String.valueOf(opmode); - } - } - - // The methods below need to be implemented by subclasses. - - /** - * Initializes this cipher with the provided key. - * - * @throws InvalidKeyException if the {@code key} is not suitable for this cipher in the - * specified {@code opmode}. - * - * @see #setKey(AndroidKeyStoreKey) - */ - protected abstract void initKey(int opmode, @Nullable Key key) throws InvalidKeyException; - - /** - * Returns algorithm-specific parameters used by this cipher or {@code null} if no - * algorithm-specific parameters are used. - */ - @Nullable - @Override - protected abstract AlgorithmParameters engineGetParameters(); - - /** - * Invoked by {@code engineInit} to initialize algorithm-specific parameters when no additional - * initialization parameters were provided. - * - * @throws InvalidKeyException if this cipher cannot be configured based purely on the provided - * key and needs additional parameters to be provided to {@code Cipher.init}. - */ - protected abstract void initAlgorithmSpecificParameters() throws InvalidKeyException; - - /** - * Invoked by {@code engineInit} to initialize algorithm-specific parameters when additional - * parameters were provided. - * - * @param params additional algorithm parameters or {@code null} if not specified. - * - * @throws InvalidAlgorithmParameterException if there is insufficient information to configure - * this cipher or if the provided parameters are not suitable for this cipher. - */ - protected abstract void initAlgorithmSpecificParameters( - @Nullable AlgorithmParameterSpec params) throws InvalidAlgorithmParameterException; - - /** - * Invoked by {@code engineInit} to initialize algorithm-specific parameters when additional - * parameters were provided. - * - * @param params additional algorithm parameters or {@code null} if not specified. - * - * @throws InvalidAlgorithmParameterException if there is insufficient information to configure - * this cipher or if the provided parameters are not suitable for this cipher. - */ - protected abstract void initAlgorithmSpecificParameters(@Nullable AlgorithmParameters params) - throws InvalidAlgorithmParameterException; - - /** - * Returns the amount of additional entropy (in bytes) to be provided to the KeyStore's - * {@code begin} operation. This amount of entropy is typically what's consumed to generate - * random parameters, such as IV. - * - * <p>For decryption, the return value should be {@code 0} because decryption should not be - * consuming any entropy. For encryption, the value combined with - * {@link #getAdditionalEntropyAmountForFinish()} should match (or exceed) the amount of Shannon - * entropy of the ciphertext produced by this cipher assuming the key, the plaintext, and all - * explicitly provided parameters to {@code Cipher.init} are known. For example, for AES CBC - * encryption with an explicitly provided IV the return value should be {@code 0}, whereas for - * the case where IV is generated by the KeyStore's {@code begin} operation it should be - * {@code 16}. - */ - protected abstract int getAdditionalEntropyAmountForBegin(); - - /** - * Returns the amount of additional entropy (in bytes) to be provided to the KeyStore's - * {@code finish} operation. This amount of entropy is typically what's consumed by encryption - * padding scheme. - * - * <p>For decryption, the return value should be {@code 0} because decryption should not be - * consuming any entropy. For encryption, the value combined with - * {@link #getAdditionalEntropyAmountForBegin()} should match (or exceed) the amount of Shannon - * entropy of the ciphertext produced by this cipher assuming the key, the plaintext, and all - * explicitly provided parameters to {@code Cipher.init} are known. For example, for RSA with - * OAEP the return value should be the size of the OAEP hash output. For RSA with PKCS#1 padding - * the return value should be the size of the padding string or could be raised (for simplicity) - * to the size of the modulus. - */ - protected abstract int getAdditionalEntropyAmountForFinish(); - - /** - * Invoked to add algorithm-specific parameters for the KeyStore's {@code begin} operation. - * - * @param keymasterArgs keystore/keymaster arguments to be populated with algorithm-specific - * parameters. - */ - protected abstract void addAlgorithmSpecificParametersToBegin( - @NonNull KeymasterArguments keymasterArgs); - - /** - * Invoked to obtain algorithm-specific parameters from the result of the KeyStore's - * {@code begin} operation. - * - * <p>Some parameters, such as IV, are not required to be provided to {@code Cipher.init}. Such - * parameters, if not provided, must be generated by KeyStore and returned to the user of - * {@code Cipher} and potentially reused after {@code doFinal}. - * - * @param keymasterArgs keystore/keymaster arguments returned by KeyStore {@code begin} - * operation. - */ - protected abstract void loadAlgorithmSpecificParametersFromBeginResult( - @NonNull KeymasterArguments keymasterArgs); -} diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreECDSASignatureSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreECDSASignatureSpi.java deleted file mode 100644 index 45f2110e0c77..000000000000 --- a/keystore/java/android/security/keystore/AndroidKeyStoreECDSASignatureSpi.java +++ /dev/null @@ -1,202 +0,0 @@ -/* - * 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.keystore; - -import android.annotation.NonNull; -import android.os.IBinder; -import android.security.KeyStore; -import android.security.KeyStoreException; -import android.security.keymaster.KeyCharacteristics; -import android.security.keymaster.KeymasterArguments; -import android.security.keymaster.KeymasterDefs; - -import libcore.util.EmptyArray; - -import java.io.ByteArrayOutputStream; -import java.security.InvalidKeyException; -import java.security.SignatureSpi; - -/** - * Base class for {@link SignatureSpi} providing Android KeyStore backed ECDSA signatures. - * - * @hide - */ -abstract class AndroidKeyStoreECDSASignatureSpi extends AndroidKeyStoreSignatureSpiBase { - - public final static class NONE extends AndroidKeyStoreECDSASignatureSpi { - public NONE() { - super(KeymasterDefs.KM_DIGEST_NONE); - } - - @Override - protected KeyStoreCryptoOperationStreamer createMainDataStreamer(KeyStore keyStore, - IBinder operationToken) { - return new TruncateToFieldSizeMessageStreamer( - super.createMainDataStreamer(keyStore, operationToken), - getGroupSizeBits()); - } - - /** - * Streamer which buffers all input, then truncates it to field size, and then sends it into - * KeyStore via the provided delegate streamer. - */ - private static class TruncateToFieldSizeMessageStreamer - implements KeyStoreCryptoOperationStreamer { - - private final KeyStoreCryptoOperationStreamer mDelegate; - private final int mGroupSizeBits; - private final ByteArrayOutputStream mInputBuffer = new ByteArrayOutputStream(); - private long mConsumedInputSizeBytes; - - private TruncateToFieldSizeMessageStreamer( - KeyStoreCryptoOperationStreamer delegate, - int groupSizeBits) { - mDelegate = delegate; - mGroupSizeBits = groupSizeBits; - } - - @Override - public byte[] update(byte[] input, int inputOffset, int inputLength) - throws KeyStoreException { - if (inputLength > 0) { - mInputBuffer.write(input, inputOffset, inputLength); - mConsumedInputSizeBytes += inputLength; - } - return EmptyArray.BYTE; - } - - @Override - public byte[] doFinal(byte[] input, int inputOffset, int inputLength, byte[] signature, - byte[] additionalEntropy) throws KeyStoreException { - if (inputLength > 0) { - mConsumedInputSizeBytes += inputLength; - mInputBuffer.write(input, inputOffset, inputLength); - } - - byte[] bufferedInput = mInputBuffer.toByteArray(); - mInputBuffer.reset(); - // Truncate input at field size (bytes) - return mDelegate.doFinal(bufferedInput, - 0, - Math.min(bufferedInput.length, ((mGroupSizeBits + 7) / 8)), - signature, additionalEntropy); - } - - @Override - public long getConsumedInputSizeBytes() { - return mConsumedInputSizeBytes; - } - - @Override - public long getProducedOutputSizeBytes() { - return mDelegate.getProducedOutputSizeBytes(); - } - } - } - - public final static class SHA1 extends AndroidKeyStoreECDSASignatureSpi { - public SHA1() { - super(KeymasterDefs.KM_DIGEST_SHA1); - } - } - - public final static class SHA224 extends AndroidKeyStoreECDSASignatureSpi { - public SHA224() { - super(KeymasterDefs.KM_DIGEST_SHA_2_224); - } - } - - public final static class SHA256 extends AndroidKeyStoreECDSASignatureSpi { - public SHA256() { - super(KeymasterDefs.KM_DIGEST_SHA_2_256); - } - } - - public final static class SHA384 extends AndroidKeyStoreECDSASignatureSpi { - public SHA384() { - super(KeymasterDefs.KM_DIGEST_SHA_2_384); - } - } - - public final static class SHA512 extends AndroidKeyStoreECDSASignatureSpi { - public SHA512() { - super(KeymasterDefs.KM_DIGEST_SHA_2_512); - } - } - - private final int mKeymasterDigest; - - private int mGroupSizeBits = -1; - - AndroidKeyStoreECDSASignatureSpi(int keymasterDigest) { - mKeymasterDigest = keymasterDigest; - } - - @Override - protected final void initKey(AndroidKeyStoreKey key) throws InvalidKeyException { - if (!KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(key.getAlgorithm())) { - throw new InvalidKeyException("Unsupported key algorithm: " + key.getAlgorithm() - + ". Only" + KeyProperties.KEY_ALGORITHM_EC + " supported"); - } - - KeyCharacteristics keyCharacteristics = new KeyCharacteristics(); - int errorCode = getKeyStore().getKeyCharacteristics( - key.getAlias(), null, null, key.getUid(), keyCharacteristics); - if (errorCode != KeyStore.NO_ERROR) { - throw getKeyStore().getInvalidKeyException(key.getAlias(), key.getUid(), errorCode); - } - long keySizeBits = keyCharacteristics.getUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, -1); - if (keySizeBits == -1) { - throw new InvalidKeyException("Size of key not known"); - } else if (keySizeBits > Integer.MAX_VALUE) { - throw new InvalidKeyException("Key too large: " + keySizeBits + " bits"); - } - mGroupSizeBits = (int) keySizeBits; - - super.initKey(key); - } - - @Override - protected final void resetAll() { - mGroupSizeBits = -1; - super.resetAll(); - } - - @Override - protected final void resetWhilePreservingInitState() { - super.resetWhilePreservingInitState(); - } - - @Override - protected final void addAlgorithmSpecificParametersToBegin( - @NonNull KeymasterArguments keymasterArgs) { - keymasterArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_EC); - keymasterArgs.addEnum(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest); - } - - @Override - protected final int getAdditionalEntropyAmountForSign() { - return (mGroupSizeBits + 7) / 8; - } - - protected final int getGroupSizeBits() { - if (mGroupSizeBits == -1) { - throw new IllegalStateException("Not initialized"); - } - return mGroupSizeBits; - } -} diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreECPrivateKey.java b/keystore/java/android/security/keystore/AndroidKeyStoreECPrivateKey.java deleted file mode 100644 index aa7bdffb9e07..000000000000 --- a/keystore/java/android/security/keystore/AndroidKeyStoreECPrivateKey.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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.keystore; - -import java.security.PrivateKey; -import java.security.interfaces.ECKey; -import java.security.spec.ECParameterSpec; - -/** - * EC private key (instance of {@link PrivateKey} and {@link ECKey}) backed by keystore. - * - * @hide - */ -public class AndroidKeyStoreECPrivateKey extends AndroidKeyStorePrivateKey implements ECKey { - private final ECParameterSpec mParams; - - public AndroidKeyStoreECPrivateKey(String alias, int uid, ECParameterSpec params) { - super(alias, uid, KeyProperties.KEY_ALGORITHM_EC); - mParams = params; - } - - @Override - public ECParameterSpec getParams() { - return mParams; - } -} diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreECPublicKey.java b/keystore/java/android/security/keystore/AndroidKeyStoreECPublicKey.java deleted file mode 100644 index 2efaeb6545a0..000000000000 --- a/keystore/java/android/security/keystore/AndroidKeyStoreECPublicKey.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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.keystore; - -import java.security.interfaces.ECPublicKey; -import java.security.spec.ECParameterSpec; -import java.security.spec.ECPoint; - -/** - * {@link ECPublicKey} backed by keystore. - * - * @hide - */ -public class AndroidKeyStoreECPublicKey extends AndroidKeyStorePublicKey implements ECPublicKey { - - private final ECParameterSpec mParams; - private final ECPoint mW; - - public AndroidKeyStoreECPublicKey(String alias, int uid, byte[] x509EncodedForm, ECParameterSpec params, - ECPoint w) { - super(alias, uid, KeyProperties.KEY_ALGORITHM_EC, x509EncodedForm); - mParams = params; - mW = w; - } - - public AndroidKeyStoreECPublicKey(String alias, int uid, ECPublicKey info) { - this(alias, uid, info.getEncoded(), info.getParams(), info.getW()); - if (!"X.509".equalsIgnoreCase(info.getFormat())) { - throw new IllegalArgumentException( - "Unsupported key export format: " + info.getFormat()); - } - } - - @Override - public ECParameterSpec getParams() { - return mParams; - } - - @Override - public ECPoint getW() { - return mW; - } -}
\ No newline at end of file diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreHmacSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreHmacSpi.java deleted file mode 100644 index 2e8ac3236463..000000000000 --- a/keystore/java/android/security/keystore/AndroidKeyStoreHmacSpi.java +++ /dev/null @@ -1,265 +0,0 @@ -/* - * 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.keystore; - -import android.os.IBinder; -import android.security.KeyStore; -import android.security.KeyStoreException; -import android.security.keymaster.KeymasterArguments; -import android.security.keymaster.KeymasterDefs; -import android.security.keymaster.OperationResult; - -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.Key; -import java.security.ProviderException; -import java.security.spec.AlgorithmParameterSpec; - -import javax.crypto.MacSpi; - -/** - * {@link MacSpi} which provides HMAC implementations backed by Android KeyStore. - * - * @hide - */ -public abstract class AndroidKeyStoreHmacSpi extends MacSpi implements KeyStoreCryptoOperation { - - public static class HmacSHA1 extends AndroidKeyStoreHmacSpi { - public HmacSHA1() { - super(KeymasterDefs.KM_DIGEST_SHA1); - } - } - - public static class HmacSHA224 extends AndroidKeyStoreHmacSpi { - public HmacSHA224() { - super(KeymasterDefs.KM_DIGEST_SHA_2_224); - } - } - - public static class HmacSHA256 extends AndroidKeyStoreHmacSpi { - public HmacSHA256() { - super(KeymasterDefs.KM_DIGEST_SHA_2_256); - } - } - - public static class HmacSHA384 extends AndroidKeyStoreHmacSpi { - public HmacSHA384() { - super(KeymasterDefs.KM_DIGEST_SHA_2_384); - } - } - - public static class HmacSHA512 extends AndroidKeyStoreHmacSpi { - public HmacSHA512() { - super(KeymasterDefs.KM_DIGEST_SHA_2_512); - } - } - - private final KeyStore mKeyStore = KeyStore.getInstance(); - private final int mKeymasterDigest; - private final int mMacSizeBits; - - // Fields below are populated by engineInit and should be preserved after engineDoFinal. - private AndroidKeyStoreSecretKey mKey; - - // Fields below are reset when engineDoFinal succeeds. - private KeyStoreCryptoOperationChunkedStreamer mChunkedStreamer; - private IBinder mOperationToken; - private long mOperationHandle; - - protected AndroidKeyStoreHmacSpi(int keymasterDigest) { - mKeymasterDigest = keymasterDigest; - mMacSizeBits = KeymasterUtils.getDigestOutputSizeBits(keymasterDigest); - } - - @Override - protected int engineGetMacLength() { - return (mMacSizeBits + 7) / 8; - } - - @Override - protected void engineInit(Key key, AlgorithmParameterSpec params) throws InvalidKeyException, - InvalidAlgorithmParameterException { - resetAll(); - - boolean success = false; - try { - init(key, params); - ensureKeystoreOperationInitialized(); - success = true; - } finally { - if (!success) { - resetAll(); - } - } - } - - private void init(Key key, AlgorithmParameterSpec params) throws InvalidKeyException, - InvalidAlgorithmParameterException { - if (key == null) { - throw new InvalidKeyException("key == null"); - } else if (!(key instanceof AndroidKeyStoreSecretKey)) { - throw new InvalidKeyException( - "Only Android KeyStore secret keys supported. Key: " + key); - } - mKey = (AndroidKeyStoreSecretKey) key; - - if (params != null) { - throw new InvalidAlgorithmParameterException( - "Unsupported algorithm parameters: " + params); - } - - } - - private void resetAll() { - mKey = null; - IBinder operationToken = mOperationToken; - if (operationToken != null) { - mKeyStore.abort(operationToken); - } - mOperationToken = null; - mOperationHandle = 0; - mChunkedStreamer = null; - } - - private void resetWhilePreservingInitState() { - IBinder operationToken = mOperationToken; - if (operationToken != null) { - mKeyStore.abort(operationToken); - } - mOperationToken = null; - mOperationHandle = 0; - mChunkedStreamer = null; - } - - @Override - protected void engineReset() { - resetWhilePreservingInitState(); - } - - private void ensureKeystoreOperationInitialized() throws InvalidKeyException { - if (mChunkedStreamer != null) { - return; - } - if (mKey == null) { - throw new IllegalStateException("Not initialized"); - } - - KeymasterArguments keymasterArgs = new KeymasterArguments(); - keymasterArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_HMAC); - keymasterArgs.addEnum(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest); - keymasterArgs.addUnsignedInt(KeymasterDefs.KM_TAG_MAC_LENGTH, mMacSizeBits); - - OperationResult opResult = mKeyStore.begin( - mKey.getAlias(), - KeymasterDefs.KM_PURPOSE_SIGN, - true, - keymasterArgs, - null, // no additional entropy needed for HMAC because it's deterministic - mKey.getUid()); - - if (opResult == null) { - throw new KeyStoreConnectException(); - } - - // Store operation token and handle regardless of the error code returned by KeyStore to - // ensure that the operation gets aborted immediately if the code below throws an exception. - mOperationToken = opResult.token; - mOperationHandle = opResult.operationHandle; - - // If necessary, throw an exception due to KeyStore operation having failed. - InvalidKeyException e = KeyStoreCryptoOperationUtils.getInvalidKeyExceptionForInit( - mKeyStore, mKey, opResult.resultCode); - if (e != null) { - throw e; - } - - if (mOperationToken == null) { - throw new ProviderException("Keystore returned null operation token"); - } - if (mOperationHandle == 0) { - throw new ProviderException("Keystore returned invalid operation handle"); - } - - mChunkedStreamer = new KeyStoreCryptoOperationChunkedStreamer( - new KeyStoreCryptoOperationChunkedStreamer.MainDataStream( - mKeyStore, mOperationToken)); - } - - @Override - protected void engineUpdate(byte input) { - engineUpdate(new byte[] {input}, 0, 1); - } - - @Override - protected void engineUpdate(byte[] input, int offset, int len) { - try { - ensureKeystoreOperationInitialized(); - } catch (InvalidKeyException e) { - throw new ProviderException("Failed to reinitialize MAC", e); - } - - byte[] output; - try { - output = mChunkedStreamer.update(input, offset, len); - } catch (KeyStoreException e) { - throw new ProviderException("Keystore operation failed", e); - } - if ((output != null) && (output.length != 0)) { - throw new ProviderException("Update operation unexpectedly produced output"); - } - } - - @Override - protected byte[] engineDoFinal() { - try { - ensureKeystoreOperationInitialized(); - } catch (InvalidKeyException e) { - throw new ProviderException("Failed to reinitialize MAC", e); - } - - byte[] result; - try { - result = mChunkedStreamer.doFinal( - null, 0, 0, - null, // no signature provided -- this invocation will generate one - null // no additional entropy needed -- HMAC is deterministic - ); - } catch (KeyStoreException e) { - throw new ProviderException("Keystore operation failed", e); - } - - resetWhilePreservingInitState(); - return result; - } - - @Override - public void finalize() throws Throwable { - try { - IBinder operationToken = mOperationToken; - if (operationToken != null) { - mKeyStore.abort(operationToken); - } - } finally { - super.finalize(); - } - } - - @Override - public long getOperationHandle() { - return mOperationHandle; - } -} diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKey.java b/keystore/java/android/security/keystore/AndroidKeyStoreKey.java deleted file mode 100644 index e8e63105925f..000000000000 --- a/keystore/java/android/security/keystore/AndroidKeyStoreKey.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * 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.keystore; - -import java.security.Key; - -/** - * {@link Key} backed by Android Keystore. - * - * @hide - */ -public class AndroidKeyStoreKey implements Key { - private final String mAlias; - private final int mUid; - private final String mAlgorithm; - - public AndroidKeyStoreKey(String alias, int uid, String algorithm) { - mAlias = alias; - mUid = uid; - mAlgorithm = algorithm; - } - - String getAlias() { - return mAlias; - } - - int getUid() { - return mUid; - } - - @Override - public String getAlgorithm() { - return mAlgorithm; - } - - @Override - public String getFormat() { - // This key does not export its key material - return null; - } - - @Override - public byte[] getEncoded() { - // This key does not export its key material - return null; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((mAlgorithm == null) ? 0 : mAlgorithm.hashCode()); - result = prime * result + ((mAlias == null) ? 0 : mAlias.hashCode()); - result = prime * result + mUid; - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - AndroidKeyStoreKey other = (AndroidKeyStoreKey) obj; - if (mAlgorithm == null) { - if (other.mAlgorithm != null) { - return false; - } - } else if (!mAlgorithm.equals(other.mAlgorithm)) { - return false; - } - if (mAlias == null) { - if (other.mAlias != null) { - return false; - } - } else if (!mAlias.equals(other.mAlias)) { - return false; - } - if (mUid != other.mUid) { - return false; - } - return true; - } -} diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyFactorySpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyFactorySpi.java deleted file mode 100644 index 303b0f2c05c2..000000000000 --- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyFactorySpi.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * 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.keystore; - -import android.security.Credentials; -import android.security.KeyStore; - -import java.security.InvalidKeyException; -import java.security.Key; -import java.security.KeyFactorySpi; -import java.security.PrivateKey; -import java.security.PublicKey; -import java.security.spec.ECPublicKeySpec; -import java.security.spec.InvalidKeySpecException; -import java.security.spec.KeySpec; -import java.security.spec.PKCS8EncodedKeySpec; -import java.security.spec.RSAPublicKeySpec; -import java.security.spec.X509EncodedKeySpec; - -/** - * {@link KeyFactorySpi} backed by Android KeyStore. - * - * @hide - */ -public class AndroidKeyStoreKeyFactorySpi extends KeyFactorySpi { - - private final KeyStore mKeyStore = KeyStore.getInstance(); - - @Override - protected <T extends KeySpec> T engineGetKeySpec(Key key, Class<T> keySpecClass) - throws InvalidKeySpecException { - if (key == null) { - throw new InvalidKeySpecException("key == null"); - } else if ((!(key instanceof AndroidKeyStorePrivateKey)) - && (!(key instanceof AndroidKeyStorePublicKey))) { - throw new InvalidKeySpecException( - "Unsupported key type: " + key.getClass().getName() - + ". This KeyFactory supports only Android Keystore asymmetric keys"); - } - - // key is an Android Keystore private or public key - - if (keySpecClass == null) { - throw new InvalidKeySpecException("keySpecClass == null"); - } else if (KeyInfo.class.equals(keySpecClass)) { - if (!(key instanceof AndroidKeyStorePrivateKey)) { - throw new InvalidKeySpecException( - "Unsupported key type: " + key.getClass().getName() - + ". KeyInfo can be obtained only for Android Keystore private keys"); - } - AndroidKeyStorePrivateKey keystorePrivateKey = (AndroidKeyStorePrivateKey) key; - String keyAliasInKeystore = keystorePrivateKey.getAlias(); - String entryAlias; - if (keyAliasInKeystore.startsWith(Credentials.USER_PRIVATE_KEY)) { - entryAlias = keyAliasInKeystore.substring(Credentials.USER_PRIVATE_KEY.length()); - } else { - throw new InvalidKeySpecException("Invalid key alias: " + keyAliasInKeystore); - } - @SuppressWarnings("unchecked") - T result = (T) AndroidKeyStoreSecretKeyFactorySpi.getKeyInfo( - mKeyStore, entryAlias, keyAliasInKeystore, keystorePrivateKey.getUid()); - return result; - } else if (X509EncodedKeySpec.class.equals(keySpecClass)) { - if (!(key instanceof AndroidKeyStorePublicKey)) { - throw new InvalidKeySpecException( - "Unsupported key type: " + key.getClass().getName() - + ". X509EncodedKeySpec can be obtained only for Android Keystore public" - + " keys"); - } - @SuppressWarnings("unchecked") - T result = (T) new X509EncodedKeySpec(((AndroidKeyStorePublicKey) key).getEncoded()); - return result; - } else if (PKCS8EncodedKeySpec.class.equals(keySpecClass)) { - if (key instanceof AndroidKeyStorePrivateKey) { - throw new InvalidKeySpecException( - "Key material export of Android Keystore private keys is not supported"); - } else { - throw new InvalidKeySpecException( - "Cannot export key material of public key in PKCS#8 format." - + " Only X.509 format (X509EncodedKeySpec) supported for public keys."); - } - } else if (RSAPublicKeySpec.class.equals(keySpecClass)) { - if (key instanceof AndroidKeyStoreRSAPublicKey) { - AndroidKeyStoreRSAPublicKey rsaKey = (AndroidKeyStoreRSAPublicKey) key; - @SuppressWarnings("unchecked") - T result = - (T) new RSAPublicKeySpec(rsaKey.getModulus(), rsaKey.getPublicExponent()); - return result; - } else { - throw new InvalidKeySpecException( - "Obtaining RSAPublicKeySpec not supported for " + key.getAlgorithm() + " " - + ((key instanceof AndroidKeyStorePrivateKey) ? "private" : "public") - + " key"); - } - } else if (ECPublicKeySpec.class.equals(keySpecClass)) { - if (key instanceof AndroidKeyStoreECPublicKey) { - AndroidKeyStoreECPublicKey ecKey = (AndroidKeyStoreECPublicKey) key; - @SuppressWarnings("unchecked") - T result = (T) new ECPublicKeySpec(ecKey.getW(), ecKey.getParams()); - return result; - } else { - throw new InvalidKeySpecException( - "Obtaining ECPublicKeySpec not supported for " + key.getAlgorithm() + " " - + ((key instanceof AndroidKeyStorePrivateKey) ? "private" : "public") - + " key"); - } - } else { - throw new InvalidKeySpecException("Unsupported key spec: " + keySpecClass.getName()); - } - } - - @Override - protected PrivateKey engineGeneratePrivate(KeySpec spec) throws InvalidKeySpecException { - throw new InvalidKeySpecException( - "To generate a key pair in Android Keystore, use KeyPairGenerator initialized with" - + " " + KeyGenParameterSpec.class.getName()); - } - - @Override - protected PublicKey engineGeneratePublic(KeySpec spec) throws InvalidKeySpecException { - throw new InvalidKeySpecException( - "To generate a key pair in Android Keystore, use KeyPairGenerator initialized with" - + " " + KeyGenParameterSpec.class.getName()); - } - - @Override - protected Key engineTranslateKey(Key key) throws InvalidKeyException { - if (key == null) { - throw new InvalidKeyException("key == null"); - } else if ((!(key instanceof AndroidKeyStorePrivateKey)) - && (!(key instanceof AndroidKeyStorePublicKey))) { - throw new InvalidKeyException( - "To import a key into Android Keystore, use KeyStore.setEntry"); - } - return key; - } -} diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java deleted file mode 100644 index fedde422e0be..000000000000 --- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java +++ /dev/null @@ -1,352 +0,0 @@ -/* - * 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.keystore; - -import android.security.Credentials; -import android.security.KeyStore; -import android.security.keymaster.KeyCharacteristics; -import android.security.keymaster.KeymasterArguments; -import android.security.keymaster.KeymasterDefs; -import android.security.keystore.KeyGenParameterSpec; -import android.security.keystore.KeyProperties; - -import libcore.util.EmptyArray; - -import java.security.InvalidAlgorithmParameterException; -import java.security.ProviderException; -import java.security.SecureRandom; -import java.security.spec.AlgorithmParameterSpec; -import java.util.Arrays; - -import javax.crypto.KeyGeneratorSpi; -import javax.crypto.SecretKey; - -/** - * {@link KeyGeneratorSpi} backed by Android KeyStore. - * - * @hide - */ -public abstract class AndroidKeyStoreKeyGeneratorSpi extends KeyGeneratorSpi { - - public static class AES extends AndroidKeyStoreKeyGeneratorSpi { - public AES() { - super(KeymasterDefs.KM_ALGORITHM_AES, 128); - } - - @Override - protected void engineInit(AlgorithmParameterSpec params, SecureRandom random) - throws InvalidAlgorithmParameterException { - super.engineInit(params, random); - if ((mKeySizeBits != 128) && (mKeySizeBits != 192) && (mKeySizeBits != 256)) { - throw new InvalidAlgorithmParameterException( - "Unsupported key size: " + mKeySizeBits - + ". Supported: 128, 192, 256."); - } - } - } - - public static class DESede extends AndroidKeyStoreKeyGeneratorSpi { - public DESede() { - super(KeymasterDefs.KM_ALGORITHM_3DES, 168); - } - } - - protected static abstract class HmacBase extends AndroidKeyStoreKeyGeneratorSpi { - protected HmacBase(int keymasterDigest) { - super(KeymasterDefs.KM_ALGORITHM_HMAC, - keymasterDigest, - KeymasterUtils.getDigestOutputSizeBits(keymasterDigest)); - } - } - - public static class HmacSHA1 extends HmacBase { - public HmacSHA1() { - super(KeymasterDefs.KM_DIGEST_SHA1); - } - } - - public static class HmacSHA224 extends HmacBase { - public HmacSHA224() { - super(KeymasterDefs.KM_DIGEST_SHA_2_224); - } - } - - public static class HmacSHA256 extends HmacBase { - public HmacSHA256() { - super(KeymasterDefs.KM_DIGEST_SHA_2_256); - } - } - - public static class HmacSHA384 extends HmacBase { - public HmacSHA384() { - super(KeymasterDefs.KM_DIGEST_SHA_2_384); - } - } - - public static class HmacSHA512 extends HmacBase { - public HmacSHA512() { - super(KeymasterDefs.KM_DIGEST_SHA_2_512); - } - } - - private final KeyStore mKeyStore = KeyStore.getInstance(); - private final int mKeymasterAlgorithm; - private final int mKeymasterDigest; - private final int mDefaultKeySizeBits; - - private KeyGenParameterSpec mSpec; - private SecureRandom mRng; - - protected int mKeySizeBits; - private int[] mKeymasterPurposes; - private int[] mKeymasterBlockModes; - private int[] mKeymasterPaddings; - private int[] mKeymasterDigests; - - protected AndroidKeyStoreKeyGeneratorSpi( - int keymasterAlgorithm, - int defaultKeySizeBits) { - this(keymasterAlgorithm, -1, defaultKeySizeBits); - } - - protected AndroidKeyStoreKeyGeneratorSpi( - int keymasterAlgorithm, - int keymasterDigest, - int defaultKeySizeBits) { - mKeymasterAlgorithm = keymasterAlgorithm; - mKeymasterDigest = keymasterDigest; - mDefaultKeySizeBits = defaultKeySizeBits; - if (mDefaultKeySizeBits <= 0) { - throw new IllegalArgumentException("Default key size must be positive"); - } - - if ((mKeymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) && (mKeymasterDigest == -1)) { - throw new IllegalArgumentException( - "Digest algorithm must be specified for HMAC key"); - } - } - - @Override - protected void engineInit(SecureRandom random) { - throw new UnsupportedOperationException("Cannot initialize without a " - + KeyGenParameterSpec.class.getName() + " parameter"); - } - - @Override - protected void engineInit(int keySize, SecureRandom random) { - throw new UnsupportedOperationException("Cannot initialize without a " - + KeyGenParameterSpec.class.getName() + " parameter"); - } - - @Override - protected void engineInit(AlgorithmParameterSpec params, SecureRandom random) - throws InvalidAlgorithmParameterException { - resetAll(); - - boolean success = false; - try { - if ((params == null) || (!(params instanceof KeyGenParameterSpec))) { - throw new InvalidAlgorithmParameterException("Cannot initialize without a " - + KeyGenParameterSpec.class.getName() + " parameter"); - } - KeyGenParameterSpec spec = (KeyGenParameterSpec) params; - if (spec.getKeystoreAlias() == null) { - throw new InvalidAlgorithmParameterException("KeyStore entry alias not provided"); - } - - mRng = random; - mSpec = spec; - - mKeySizeBits = (spec.getKeySize() != -1) ? spec.getKeySize() : mDefaultKeySizeBits; - if (mKeySizeBits <= 0) { - throw new InvalidAlgorithmParameterException( - "Key size must be positive: " + mKeySizeBits); - } else if ((mKeySizeBits % 8) != 0) { - throw new InvalidAlgorithmParameterException( - "Key size must be a multiple of 8: " + mKeySizeBits); - } - - try { - mKeymasterPurposes = KeyProperties.Purpose.allToKeymaster(spec.getPurposes()); - mKeymasterPaddings = KeyProperties.EncryptionPadding.allToKeymaster( - spec.getEncryptionPaddings()); - if (spec.getSignaturePaddings().length > 0) { - throw new InvalidAlgorithmParameterException( - "Signature paddings not supported for symmetric key algorithms"); - } - mKeymasterBlockModes = KeyProperties.BlockMode.allToKeymaster(spec.getBlockModes()); - if (((spec.getPurposes() & KeyProperties.PURPOSE_ENCRYPT) != 0) - && (spec.isRandomizedEncryptionRequired())) { - for (int keymasterBlockMode : mKeymasterBlockModes) { - if (!KeymasterUtils.isKeymasterBlockModeIndCpaCompatibleWithSymmetricCrypto( - keymasterBlockMode)) { - throw new InvalidAlgorithmParameterException( - "Randomized encryption (IND-CPA) required but may be violated" - + " by block mode: " - + KeyProperties.BlockMode.fromKeymaster(keymasterBlockMode) - + ". See " + KeyGenParameterSpec.class.getName() - + " documentation."); - } - } - } - if (mKeymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_3DES) { - if (mKeySizeBits != 168) { - throw new InvalidAlgorithmParameterException( - "3DES key size must be 168 bits."); - } - } - if (mKeymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) { - if (mKeySizeBits < 64 || mKeySizeBits > 512) { - throw new InvalidAlgorithmParameterException( - "HMAC key sizes must be within 64-512 bits, inclusive."); - } - - // JCA HMAC key algorithm implies a digest (e.g., HmacSHA256 key algorithm - // implies SHA-256 digest). Because keymaster HMAC key is authorized only for - // one digest, we don't let algorithm parameter spec override the digest implied - // by the key. If the spec specifies digests at all, it must specify only one - // digest, the only implied by key algorithm. - mKeymasterDigests = new int[] {mKeymasterDigest}; - if (spec.isDigestsSpecified()) { - // Digest(s) explicitly specified in the spec. Check that the list - // consists of exactly one digest, the one implied by key algorithm. - int[] keymasterDigestsFromSpec = - KeyProperties.Digest.allToKeymaster(spec.getDigests()); - if ((keymasterDigestsFromSpec.length != 1) - || (keymasterDigestsFromSpec[0] != mKeymasterDigest)) { - throw new InvalidAlgorithmParameterException( - "Unsupported digests specification: " - + Arrays.asList(spec.getDigests()) + ". Only " - + KeyProperties.Digest.fromKeymaster(mKeymasterDigest) - + " supported for this HMAC key algorithm"); - } - } - } else { - // Key algorithm does not imply a digest. - if (spec.isDigestsSpecified()) { - mKeymasterDigests = KeyProperties.Digest.allToKeymaster(spec.getDigests()); - } else { - mKeymasterDigests = EmptyArray.INT; - } - } - - // Check that user authentication related parameters are acceptable. This method - // will throw an IllegalStateException if there are issues (e.g., secure lock screen - // not set up). - KeymasterUtils.addUserAuthArgs(new KeymasterArguments(), spec); - } catch (IllegalStateException | IllegalArgumentException e) { - throw new InvalidAlgorithmParameterException(e); - } - - success = true; - } finally { - if (!success) { - resetAll(); - } - } - } - - private void resetAll() { - mSpec = null; - mRng = null; - mKeySizeBits = -1; - mKeymasterPurposes = null; - mKeymasterPaddings = null; - mKeymasterBlockModes = null; - } - - @Override - protected SecretKey engineGenerateKey() { - KeyGenParameterSpec spec = mSpec; - if (spec == null) { - throw new IllegalStateException("Not initialized"); - } - - KeymasterArguments args = new KeymasterArguments(); - args.addUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, mKeySizeBits); - args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, mKeymasterAlgorithm); - args.addEnums(KeymasterDefs.KM_TAG_PURPOSE, mKeymasterPurposes); - args.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE, mKeymasterBlockModes); - args.addEnums(KeymasterDefs.KM_TAG_PADDING, mKeymasterPaddings); - args.addEnums(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigests); - KeymasterUtils.addUserAuthArgs(args, spec); - KeymasterUtils.addMinMacLengthAuthorizationIfNecessary( - args, - mKeymasterAlgorithm, - mKeymasterBlockModes, - mKeymasterDigests); - args.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME, spec.getKeyValidityStart()); - args.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME, - spec.getKeyValidityForOriginationEnd()); - args.addDateIfNotNull(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME, - spec.getKeyValidityForConsumptionEnd()); - - if (((spec.getPurposes() & KeyProperties.PURPOSE_ENCRYPT) != 0) - && (!spec.isRandomizedEncryptionRequired())) { - // Permit caller-provided IV when encrypting with this key - args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE); - } - - byte[] additionalEntropy = - KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng( - mRng, (mKeySizeBits + 7) / 8); - int flags = 0; - if (spec.isStrongBoxBacked()) { - flags |= KeyStore.FLAG_STRONGBOX; - } - if (spec.isCriticalToDeviceEncryption()) { - flags |= KeyStore.FLAG_CRITICAL_TO_DEVICE_ENCRYPTION; - } - String keyAliasInKeystore = Credentials.USER_PRIVATE_KEY + spec.getKeystoreAlias(); - KeyCharacteristics resultingKeyCharacteristics = new KeyCharacteristics(); - boolean success = false; - try { - Credentials.deleteAllTypesForAlias(mKeyStore, spec.getKeystoreAlias(), spec.getUid()); - int errorCode = mKeyStore.generateKey( - keyAliasInKeystore, - args, - additionalEntropy, - spec.getUid(), - flags, - resultingKeyCharacteristics); - if (errorCode != KeyStore.NO_ERROR) { - if (errorCode == KeyStore.HARDWARE_TYPE_UNAVAILABLE) { - throw new StrongBoxUnavailableException("Failed to generate key"); - } else { - throw new ProviderException( - "Keystore operation failed", KeyStore.getKeyStoreException(errorCode)); - } - } - @KeyProperties.KeyAlgorithmEnum String keyAlgorithmJCA; - try { - keyAlgorithmJCA = KeyProperties.KeyAlgorithm.fromKeymasterSecretKeyAlgorithm( - mKeymasterAlgorithm, mKeymasterDigest); - } catch (IllegalArgumentException e) { - throw new ProviderException("Failed to obtain JCA secret key algorithm name", e); - } - SecretKey result = new AndroidKeyStoreSecretKey( - keyAliasInKeystore, spec.getUid(), keyAlgorithmJCA); - success = true; - return result; - } finally { - if (!success) { - Credentials.deleteAllTypesForAlias( - mKeyStore, spec.getKeystoreAlias(), spec.getUid()); - } - } - } -} diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java deleted file mode 100644 index 988838b46334..000000000000 --- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java +++ /dev/null @@ -1,986 +0,0 @@ -/* - * Copyright (C) 2012 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.keystore; - -import android.annotation.Nullable; -import android.content.Context; -import android.os.Build; -import android.security.Credentials; -import android.security.KeyPairGeneratorSpec; -import android.security.KeyStore; -import android.security.keymaster.KeyCharacteristics; -import android.security.keymaster.KeymasterArguments; -import android.security.keymaster.KeymasterCertificateChain; -import android.security.keymaster.KeymasterDefs; -import android.telephony.TelephonyManager; -import android.util.ArraySet; - -import com.android.internal.org.bouncycastle.asn1.ASN1EncodableVector; -import com.android.internal.org.bouncycastle.asn1.ASN1InputStream; -import com.android.internal.org.bouncycastle.asn1.ASN1Integer; -import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier; -import com.android.internal.org.bouncycastle.asn1.DERBitString; -import com.android.internal.org.bouncycastle.asn1.DERNull; -import com.android.internal.org.bouncycastle.asn1.DERSequence; -import com.android.internal.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; -import com.android.internal.org.bouncycastle.asn1.x509.AlgorithmIdentifier; -import com.android.internal.org.bouncycastle.asn1.x509.Certificate; -import com.android.internal.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; -import com.android.internal.org.bouncycastle.asn1.x509.TBSCertificate; -import com.android.internal.org.bouncycastle.asn1.x509.Time; -import com.android.internal.org.bouncycastle.asn1.x509.V3TBSCertificateGenerator; -import com.android.internal.org.bouncycastle.asn1.x9.X9ObjectIdentifiers; -import com.android.internal.org.bouncycastle.jce.X509Principal; -import com.android.internal.org.bouncycastle.jce.provider.X509CertificateObject; -import com.android.internal.org.bouncycastle.x509.X509V3CertificateGenerator; - -import libcore.util.EmptyArray; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.math.BigInteger; -import java.nio.charset.StandardCharsets; -import java.security.InvalidAlgorithmParameterException; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.KeyPairGeneratorSpi; -import java.security.PrivateKey; -import java.security.ProviderException; -import java.security.PublicKey; -import java.security.SecureRandom; -import java.security.UnrecoverableKeyException; -import java.security.cert.CertificateEncodingException; -import java.security.cert.CertificateParsingException; -import java.security.cert.X509Certificate; -import java.security.spec.AlgorithmParameterSpec; -import java.security.spec.ECGenParameterSpec; -import java.security.spec.RSAKeyGenParameterSpec; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; - -/** - * Provides a way to create instances of a KeyPair which will be placed in the - * Android keystore service usable only by the application that called it. This - * can be used in conjunction with - * {@link java.security.KeyStore#getInstance(String)} using the - * {@code "AndroidKeyStore"} type. - * <p> - * This class can not be directly instantiated and must instead be used via the - * {@link KeyPairGenerator#getInstance(String) - * KeyPairGenerator.getInstance("AndroidKeyStore")} API. - * - * @hide - */ -public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGeneratorSpi { - - public static class RSA extends AndroidKeyStoreKeyPairGeneratorSpi { - public RSA() { - super(KeymasterDefs.KM_ALGORITHM_RSA); - } - } - - public static class EC extends AndroidKeyStoreKeyPairGeneratorSpi { - public EC() { - super(KeymasterDefs.KM_ALGORITHM_EC); - } - } - - /* - * These must be kept in sync with system/security/keystore/defaults.h - */ - - /* EC */ - private static final int EC_DEFAULT_KEY_SIZE = 256; - - /* RSA */ - private static final int RSA_DEFAULT_KEY_SIZE = 2048; - private static final int RSA_MIN_KEY_SIZE = 512; - private static final int RSA_MAX_KEY_SIZE = 8192; - - private static final Map<String, Integer> SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE = - new HashMap<String, Integer>(); - private static final List<String> SUPPORTED_EC_NIST_CURVE_NAMES = new ArrayList<String>(); - private static final List<Integer> SUPPORTED_EC_NIST_CURVE_SIZES = new ArrayList<Integer>(); - static { - // Aliases for NIST P-224 - SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("p-224", 224); - SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("secp224r1", 224); - - - // Aliases for NIST P-256 - SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("p-256", 256); - SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("secp256r1", 256); - SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("prime256v1", 256); - - // Aliases for NIST P-384 - SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("p-384", 384); - SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("secp384r1", 384); - - // Aliases for NIST P-521 - SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("p-521", 521); - SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("secp521r1", 521); - - SUPPORTED_EC_NIST_CURVE_NAMES.addAll(SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.keySet()); - Collections.sort(SUPPORTED_EC_NIST_CURVE_NAMES); - - SUPPORTED_EC_NIST_CURVE_SIZES.addAll( - new HashSet<Integer>(SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.values())); - Collections.sort(SUPPORTED_EC_NIST_CURVE_SIZES); - } - - private final int mOriginalKeymasterAlgorithm; - - private KeyStore mKeyStore; - - private KeyGenParameterSpec mSpec; - - private String mEntryAlias; - private int mEntryUid; - private boolean mEncryptionAtRestRequired; - private @KeyProperties.KeyAlgorithmEnum String mJcaKeyAlgorithm; - private int mKeymasterAlgorithm = -1; - private int mKeySizeBits; - private SecureRandom mRng; - - private int[] mKeymasterPurposes; - private int[] mKeymasterBlockModes; - private int[] mKeymasterEncryptionPaddings; - private int[] mKeymasterSignaturePaddings; - private int[] mKeymasterDigests; - - private BigInteger mRSAPublicExponent; - - protected AndroidKeyStoreKeyPairGeneratorSpi(int keymasterAlgorithm) { - mOriginalKeymasterAlgorithm = keymasterAlgorithm; - } - - @SuppressWarnings("deprecation") - @Override - public void initialize(int keysize, SecureRandom random) { - throw new IllegalArgumentException( - KeyGenParameterSpec.class.getName() + " or " + KeyPairGeneratorSpec.class.getName() - + " required to initialize this KeyPairGenerator"); - } - - @SuppressWarnings("deprecation") - @Override - public void initialize(AlgorithmParameterSpec params, SecureRandom random) - throws InvalidAlgorithmParameterException { - resetAll(); - - boolean success = false; - try { - if (params == null) { - throw new InvalidAlgorithmParameterException( - "Must supply params of type " + KeyGenParameterSpec.class.getName() - + " or " + KeyPairGeneratorSpec.class.getName()); - } - - KeyGenParameterSpec spec; - boolean encryptionAtRestRequired = false; - int keymasterAlgorithm = mOriginalKeymasterAlgorithm; - if (params instanceof KeyGenParameterSpec) { - spec = (KeyGenParameterSpec) params; - } else if (params instanceof KeyPairGeneratorSpec) { - // Legacy/deprecated spec - KeyPairGeneratorSpec legacySpec = (KeyPairGeneratorSpec) params; - try { - KeyGenParameterSpec.Builder specBuilder; - String specKeyAlgorithm = legacySpec.getKeyType(); - if (specKeyAlgorithm != null) { - // Spec overrides the generator's default key algorithm - try { - keymasterAlgorithm = - KeyProperties.KeyAlgorithm.toKeymasterAsymmetricKeyAlgorithm( - specKeyAlgorithm); - } catch (IllegalArgumentException e) { - throw new InvalidAlgorithmParameterException( - "Invalid key type in parameters", e); - } - } - switch (keymasterAlgorithm) { - case KeymasterDefs.KM_ALGORITHM_EC: - specBuilder = new KeyGenParameterSpec.Builder( - legacySpec.getKeystoreAlias(), - KeyProperties.PURPOSE_SIGN - | KeyProperties.PURPOSE_VERIFY); - // Authorized to be used with any digest (including no digest). - // MD5 was never offered for Android Keystore for ECDSA. - specBuilder.setDigests( - KeyProperties.DIGEST_NONE, - KeyProperties.DIGEST_SHA1, - KeyProperties.DIGEST_SHA224, - KeyProperties.DIGEST_SHA256, - KeyProperties.DIGEST_SHA384, - KeyProperties.DIGEST_SHA512); - break; - case KeymasterDefs.KM_ALGORITHM_RSA: - specBuilder = new KeyGenParameterSpec.Builder( - legacySpec.getKeystoreAlias(), - KeyProperties.PURPOSE_ENCRYPT - | KeyProperties.PURPOSE_DECRYPT - | KeyProperties.PURPOSE_SIGN - | KeyProperties.PURPOSE_VERIFY); - // Authorized to be used with any digest (including no digest). - specBuilder.setDigests( - KeyProperties.DIGEST_NONE, - KeyProperties.DIGEST_MD5, - KeyProperties.DIGEST_SHA1, - KeyProperties.DIGEST_SHA224, - KeyProperties.DIGEST_SHA256, - KeyProperties.DIGEST_SHA384, - KeyProperties.DIGEST_SHA512); - // Authorized to be used with any encryption and signature padding - // schemes (including no padding). - specBuilder.setEncryptionPaddings( - KeyProperties.ENCRYPTION_PADDING_NONE, - KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1, - KeyProperties.ENCRYPTION_PADDING_RSA_OAEP); - specBuilder.setSignaturePaddings( - KeyProperties.SIGNATURE_PADDING_RSA_PKCS1, - KeyProperties.SIGNATURE_PADDING_RSA_PSS); - // Disable randomized encryption requirement to support encryption - // padding NONE above. - specBuilder.setRandomizedEncryptionRequired(false); - break; - default: - throw new ProviderException( - "Unsupported algorithm: " + mKeymasterAlgorithm); - } - - if (legacySpec.getKeySize() != -1) { - specBuilder.setKeySize(legacySpec.getKeySize()); - } - if (legacySpec.getAlgorithmParameterSpec() != null) { - specBuilder.setAlgorithmParameterSpec( - legacySpec.getAlgorithmParameterSpec()); - } - specBuilder.setCertificateSubject(legacySpec.getSubjectDN()); - specBuilder.setCertificateSerialNumber(legacySpec.getSerialNumber()); - specBuilder.setCertificateNotBefore(legacySpec.getStartDate()); - specBuilder.setCertificateNotAfter(legacySpec.getEndDate()); - encryptionAtRestRequired = legacySpec.isEncryptionRequired(); - specBuilder.setUserAuthenticationRequired(false); - - spec = specBuilder.build(); - } catch (NullPointerException | IllegalArgumentException e) { - throw new InvalidAlgorithmParameterException(e); - } - } else { - throw new InvalidAlgorithmParameterException( - "Unsupported params class: " + params.getClass().getName() - + ". Supported: " + KeyGenParameterSpec.class.getName() - + ", " + KeyPairGeneratorSpec.class.getName()); - } - - mEntryAlias = spec.getKeystoreAlias(); - mEntryUid = spec.getUid(); - mSpec = spec; - mKeymasterAlgorithm = keymasterAlgorithm; - mEncryptionAtRestRequired = encryptionAtRestRequired; - mKeySizeBits = spec.getKeySize(); - initAlgorithmSpecificParameters(); - if (mKeySizeBits == -1) { - mKeySizeBits = getDefaultKeySize(keymasterAlgorithm); - } - checkValidKeySize(keymasterAlgorithm, mKeySizeBits, mSpec.isStrongBoxBacked()); - - if (spec.getKeystoreAlias() == null) { - throw new InvalidAlgorithmParameterException("KeyStore entry alias not provided"); - } - - String jcaKeyAlgorithm; - try { - jcaKeyAlgorithm = KeyProperties.KeyAlgorithm.fromKeymasterAsymmetricKeyAlgorithm( - keymasterAlgorithm); - mKeymasterPurposes = KeyProperties.Purpose.allToKeymaster(spec.getPurposes()); - mKeymasterBlockModes = KeyProperties.BlockMode.allToKeymaster(spec.getBlockModes()); - mKeymasterEncryptionPaddings = KeyProperties.EncryptionPadding.allToKeymaster( - spec.getEncryptionPaddings()); - if (((spec.getPurposes() & KeyProperties.PURPOSE_ENCRYPT) != 0) - && (spec.isRandomizedEncryptionRequired())) { - for (int keymasterPadding : mKeymasterEncryptionPaddings) { - if (!KeymasterUtils - .isKeymasterPaddingSchemeIndCpaCompatibleWithAsymmetricCrypto( - keymasterPadding)) { - throw new InvalidAlgorithmParameterException( - "Randomized encryption (IND-CPA) required but may be violated" - + " by padding scheme: " - + KeyProperties.EncryptionPadding.fromKeymaster( - keymasterPadding) - + ". See " + KeyGenParameterSpec.class.getName() - + " documentation."); - } - } - } - mKeymasterSignaturePaddings = KeyProperties.SignaturePadding.allToKeymaster( - spec.getSignaturePaddings()); - if (spec.isDigestsSpecified()) { - mKeymasterDigests = KeyProperties.Digest.allToKeymaster(spec.getDigests()); - } else { - mKeymasterDigests = EmptyArray.INT; - } - - // Check that user authentication related parameters are acceptable. This method - // will throw an IllegalStateException if there are issues (e.g., secure lock screen - // not set up). - KeymasterUtils.addUserAuthArgs(new KeymasterArguments(), mSpec); - } catch (IllegalArgumentException | IllegalStateException e) { - throw new InvalidAlgorithmParameterException(e); - } - - mJcaKeyAlgorithm = jcaKeyAlgorithm; - mRng = random; - mKeyStore = KeyStore.getInstance(); - success = true; - } finally { - if (!success) { - resetAll(); - } - } - } - - private void resetAll() { - mEntryAlias = null; - mEntryUid = KeyStore.UID_SELF; - mJcaKeyAlgorithm = null; - mKeymasterAlgorithm = -1; - mKeymasterPurposes = null; - mKeymasterBlockModes = null; - mKeymasterEncryptionPaddings = null; - mKeymasterSignaturePaddings = null; - mKeymasterDigests = null; - mKeySizeBits = 0; - mSpec = null; - mRSAPublicExponent = null; - mEncryptionAtRestRequired = false; - mRng = null; - mKeyStore = null; - } - - private void initAlgorithmSpecificParameters() throws InvalidAlgorithmParameterException { - AlgorithmParameterSpec algSpecificSpec = mSpec.getAlgorithmParameterSpec(); - switch (mKeymasterAlgorithm) { - case KeymasterDefs.KM_ALGORITHM_RSA: - { - BigInteger publicExponent = null; - if (algSpecificSpec instanceof RSAKeyGenParameterSpec) { - RSAKeyGenParameterSpec rsaSpec = (RSAKeyGenParameterSpec) algSpecificSpec; - if (mKeySizeBits == -1) { - mKeySizeBits = rsaSpec.getKeysize(); - } else if (mKeySizeBits != rsaSpec.getKeysize()) { - throw new InvalidAlgorithmParameterException("RSA key size must match " - + " between " + mSpec + " and " + algSpecificSpec - + ": " + mKeySizeBits + " vs " + rsaSpec.getKeysize()); - } - publicExponent = rsaSpec.getPublicExponent(); - } else if (algSpecificSpec != null) { - throw new InvalidAlgorithmParameterException( - "RSA may only use RSAKeyGenParameterSpec"); - } - if (publicExponent == null) { - publicExponent = RSAKeyGenParameterSpec.F4; - } - if (publicExponent.compareTo(BigInteger.ZERO) < 1) { - throw new InvalidAlgorithmParameterException( - "RSA public exponent must be positive: " + publicExponent); - } - if (publicExponent.compareTo(KeymasterArguments.UINT64_MAX_VALUE) > 0) { - throw new InvalidAlgorithmParameterException( - "Unsupported RSA public exponent: " + publicExponent - + ". Maximum supported value: " + KeymasterArguments.UINT64_MAX_VALUE); - } - mRSAPublicExponent = publicExponent; - break; - } - case KeymasterDefs.KM_ALGORITHM_EC: - if (algSpecificSpec instanceof ECGenParameterSpec) { - ECGenParameterSpec ecSpec = (ECGenParameterSpec) algSpecificSpec; - String curveName = ecSpec.getName(); - Integer ecSpecKeySizeBits = SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.get( - curveName.toLowerCase(Locale.US)); - if (ecSpecKeySizeBits == null) { - throw new InvalidAlgorithmParameterException( - "Unsupported EC curve name: " + curveName - + ". Supported: " + SUPPORTED_EC_NIST_CURVE_NAMES); - } - if (mKeySizeBits == -1) { - mKeySizeBits = ecSpecKeySizeBits; - } else if (mKeySizeBits != ecSpecKeySizeBits) { - throw new InvalidAlgorithmParameterException("EC key size must match " - + " between " + mSpec + " and " + algSpecificSpec - + ": " + mKeySizeBits + " vs " + ecSpecKeySizeBits); - } - } else if (algSpecificSpec != null) { - throw new InvalidAlgorithmParameterException( - "EC may only use ECGenParameterSpec"); - } - break; - default: - throw new ProviderException("Unsupported algorithm: " + mKeymasterAlgorithm); - } - } - - @Override - public KeyPair generateKeyPair() { - if (mKeyStore == null || mSpec == null) { - throw new IllegalStateException("Not initialized"); - } - - int flags = (mEncryptionAtRestRequired) ? KeyStore.FLAG_ENCRYPTED : 0; - if (((flags & KeyStore.FLAG_ENCRYPTED) != 0) - && (mKeyStore.state() != KeyStore.State.UNLOCKED)) { - throw new IllegalStateException( - "Encryption at rest using secure lock screen credential requested for key pair" - + ", but the user has not yet entered the credential"); - } - - if (mSpec.isStrongBoxBacked()) { - flags |= KeyStore.FLAG_STRONGBOX; - } - if (mSpec.isCriticalToDeviceEncryption()) { - flags |= KeyStore.FLAG_CRITICAL_TO_DEVICE_ENCRYPTION; - } - - byte[] additionalEntropy = - KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng( - mRng, (mKeySizeBits + 7) / 8); - - Credentials.deleteAllTypesForAlias(mKeyStore, mEntryAlias, mEntryUid); - final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + mEntryAlias; - boolean success = false; - try { - generateKeystoreKeyPair( - privateKeyAlias, constructKeyGenerationArguments(), additionalEntropy, flags); - KeyPair keyPair = loadKeystoreKeyPair(privateKeyAlias); - - storeCertificateChain(flags, createCertificateChain(privateKeyAlias, keyPair)); - - success = true; - return keyPair; - } catch (ProviderException | IllegalArgumentException | DeviceIdAttestationException e) { - if ((mSpec.getPurposes() & KeyProperties.PURPOSE_WRAP_KEY) != 0) { - throw new SecureKeyImportUnavailableException(e); - } else { - throw new ProviderException(e); - } - } finally { - if (!success) { - Credentials.deleteAllTypesForAlias(mKeyStore, mEntryAlias, mEntryUid); - } - } - } - - private Iterable<byte[]> createCertificateChain(final String privateKeyAlias, KeyPair keyPair) - throws ProviderException, DeviceIdAttestationException { - byte[] challenge = mSpec.getAttestationChallenge(); - if (challenge != null) { - KeymasterArguments args = new KeymasterArguments(); - args.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_CHALLENGE, challenge); - - if (mSpec.isDevicePropertiesAttestationIncluded()) { - args.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_BRAND, - Build.BRAND.getBytes(StandardCharsets.UTF_8)); - args.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_DEVICE, - Build.DEVICE.getBytes(StandardCharsets.UTF_8)); - args.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_PRODUCT, - Build.PRODUCT.getBytes(StandardCharsets.UTF_8)); - args.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_MANUFACTURER, - Build.MANUFACTURER.getBytes(StandardCharsets.UTF_8)); - args.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_MODEL, - Build.MODEL.getBytes(StandardCharsets.UTF_8)); - } - - int[] idTypes = mSpec.getAttestationIds(); - if (idTypes != null) { - final Set<Integer> idTypesSet = new ArraySet<>(idTypes.length); - for (int idType : idTypes) { - idTypesSet.add(idType); - } - TelephonyManager telephonyService = null; - if (idTypesSet.contains(AttestationUtils.ID_TYPE_IMEI) - || idTypesSet.contains(AttestationUtils.ID_TYPE_MEID)) { - telephonyService = - (TelephonyManager) KeyStore.getApplicationContext().getSystemService( - Context.TELEPHONY_SERVICE); - if (telephonyService == null) { - throw new DeviceIdAttestationException( - "Unable to access telephony service"); - } - } - for (final Integer idType : idTypesSet) { - switch (idType) { - case AttestationUtils.ID_TYPE_SERIAL: - args.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_SERIAL, - Build.getSerial().getBytes(StandardCharsets.UTF_8) - ); - break; - case AttestationUtils.ID_TYPE_IMEI: { - final String imei = telephonyService.getImei(0); - if (imei == null) { - throw new DeviceIdAttestationException("Unable to retrieve IMEI"); - } - args.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_IMEI, - imei.getBytes(StandardCharsets.UTF_8) - ); - break; - } - case AttestationUtils.ID_TYPE_MEID: { - final String meid = telephonyService.getMeid(0); - if (meid == null) { - throw new DeviceIdAttestationException("Unable to retrieve MEID"); - } - args.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_MEID, - meid.getBytes(StandardCharsets.UTF_8) - ); - break; - } - case AttestationUtils.USE_INDIVIDUAL_ATTESTATION: { - args.addBoolean(KeymasterDefs.KM_TAG_DEVICE_UNIQUE_ATTESTATION); - break; - } - default: - throw new IllegalArgumentException("Unknown device ID type " + idType); - } - } - } - - return getAttestationChain(privateKeyAlias, keyPair, args); - } - - // Very short certificate chain in the non-attestation case. - return Collections.singleton(generateSelfSignedCertificateBytes(keyPair)); - } - - private void generateKeystoreKeyPair(final String privateKeyAlias, KeymasterArguments args, - byte[] additionalEntropy, final int flags) throws ProviderException { - KeyCharacteristics resultingKeyCharacteristics = new KeyCharacteristics(); - int errorCode = mKeyStore.generateKey(privateKeyAlias, args, additionalEntropy, - mEntryUid, flags, resultingKeyCharacteristics); - if (errorCode != KeyStore.NO_ERROR) { - if (errorCode == KeyStore.HARDWARE_TYPE_UNAVAILABLE) { - throw new StrongBoxUnavailableException("Failed to generate key pair"); - } else { - throw new ProviderException( - "Failed to generate key pair", KeyStore.getKeyStoreException(errorCode)); - } - } - } - - private KeyPair loadKeystoreKeyPair(final String privateKeyAlias) throws ProviderException { - try { - KeyPair result = AndroidKeyStoreProvider.loadAndroidKeyStoreKeyPairFromKeystore( - mKeyStore, privateKeyAlias, mEntryUid); - if (!mJcaKeyAlgorithm.equalsIgnoreCase(result.getPrivate().getAlgorithm())) { - throw new ProviderException( - "Generated key pair algorithm does not match requested algorithm: " - + result.getPrivate().getAlgorithm() + " vs " + mJcaKeyAlgorithm); - } - return result; - } catch (UnrecoverableKeyException | KeyPermanentlyInvalidatedException e) { - throw new ProviderException("Failed to load generated key pair from keystore", e); - } - } - - private KeymasterArguments constructKeyGenerationArguments() - throws IllegalArgumentException, DeviceIdAttestationException { - KeymasterArguments args = new KeymasterArguments(); - args.addUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, mKeySizeBits); - args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, mKeymasterAlgorithm); - args.addEnums(KeymasterDefs.KM_TAG_PURPOSE, mKeymasterPurposes); - args.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE, mKeymasterBlockModes); - args.addEnums(KeymasterDefs.KM_TAG_PADDING, mKeymasterEncryptionPaddings); - args.addEnums(KeymasterDefs.KM_TAG_PADDING, mKeymasterSignaturePaddings); - args.addEnums(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigests); - - KeymasterUtils.addUserAuthArgs(args, mSpec); - args.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME, mSpec.getKeyValidityStart()); - args.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME, - mSpec.getKeyValidityForOriginationEnd()); - args.addDateIfNotNull(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME, - mSpec.getKeyValidityForConsumptionEnd()); - addAlgorithmSpecificParameters(args); - - if (mSpec.isUniqueIdIncluded()) { - args.addBoolean(KeymasterDefs.KM_TAG_INCLUDE_UNIQUE_ID); - } - return args; - } - - private void storeCertificateChain(final int flags, Iterable<byte[]> iterable) - throws ProviderException { - Iterator<byte[]> iter = iterable.iterator(); - storeCertificate( - Credentials.USER_CERTIFICATE, iter.next(), flags, "Failed to store certificate"); - - if (!iter.hasNext()) { - return; - } - - ByteArrayOutputStream certificateConcatenationStream = new ByteArrayOutputStream(); - while (iter.hasNext()) { - byte[] data = iter.next(); - certificateConcatenationStream.write(data, 0, data.length); - } - - storeCertificate(Credentials.CA_CERTIFICATE, certificateConcatenationStream.toByteArray(), - flags, "Failed to store attestation CA certificate"); - } - - private void storeCertificate(String prefix, byte[] certificateBytes, final int flags, - String failureMessage) throws ProviderException { - int insertErrorCode = mKeyStore.insert( - prefix + mEntryAlias, - certificateBytes, - mEntryUid, - flags); - if (insertErrorCode != KeyStore.NO_ERROR) { - throw new ProviderException(failureMessage, - KeyStore.getKeyStoreException(insertErrorCode)); - } - } - - private byte[] generateSelfSignedCertificateBytes(KeyPair keyPair) throws ProviderException { - try { - return generateSelfSignedCertificate(keyPair.getPrivate(), keyPair.getPublic()) - .getEncoded(); - } catch (IOException | CertificateParsingException e) { - throw new ProviderException("Failed to generate self-signed certificate", e); - } catch (CertificateEncodingException e) { - throw new ProviderException( - "Failed to obtain encoded form of self-signed certificate", e); - } - } - - private Iterable<byte[]> getAttestationChain(String privateKeyAlias, - KeyPair keyPair, KeymasterArguments args) - throws ProviderException { - final KeymasterCertificateChain outChain = new KeymasterCertificateChain(); - final int errorCode; - if (mSpec.isDevicePropertiesAttestationIncluded() - && mSpec.getAttestationChallenge() == null) { - throw new ProviderException("An attestation challenge must be provided when requesting " - + "device properties attestation."); - } - errorCode = mKeyStore.attestKey(privateKeyAlias, args, outChain); - if (errorCode != KeyStore.NO_ERROR) { - throw new ProviderException("Failed to generate attestation certificate chain", - KeyStore.getKeyStoreException(errorCode)); - } - Collection<byte[]> chain = outChain.getCertificates(); - if (chain.size() < 2) { - throw new ProviderException("Attestation certificate chain contained " - + chain.size() + " entries. At least two are required."); - } - return chain; - } - - private void addAlgorithmSpecificParameters(KeymasterArguments keymasterArgs) { - switch (mKeymasterAlgorithm) { - case KeymasterDefs.KM_ALGORITHM_RSA: - keymasterArgs.addUnsignedLong( - KeymasterDefs.KM_TAG_RSA_PUBLIC_EXPONENT, mRSAPublicExponent); - break; - case KeymasterDefs.KM_ALGORITHM_EC: - break; - default: - throw new ProviderException("Unsupported algorithm: " + mKeymasterAlgorithm); - } - } - - private X509Certificate generateSelfSignedCertificate(PrivateKey privateKey, - PublicKey publicKey) throws CertificateParsingException, IOException { - String signatureAlgorithm = - getCertificateSignatureAlgorithm(mKeymasterAlgorithm, mKeySizeBits, mSpec); - if (signatureAlgorithm == null) { - // Key cannot be used to sign a certificate - return generateSelfSignedCertificateWithFakeSignature(publicKey); - } else { - // Key can be used to sign a certificate - try { - return generateSelfSignedCertificateWithValidSignature( - privateKey, publicKey, signatureAlgorithm); - } catch (Exception e) { - // Failed to generate the self-signed certificate with valid signature. Fall back - // to generating a self-signed certificate with a fake signature. This is done for - // all exception types because we prefer key pair generation to succeed and end up - // producing a self-signed certificate with an invalid signature to key pair - // generation failing. - return generateSelfSignedCertificateWithFakeSignature(publicKey); - } - } - } - - @SuppressWarnings("deprecation") - private X509Certificate generateSelfSignedCertificateWithValidSignature( - PrivateKey privateKey, PublicKey publicKey, String signatureAlgorithm) throws Exception { - final X509V3CertificateGenerator certGen = new X509V3CertificateGenerator(); - certGen.setPublicKey(publicKey); - certGen.setSerialNumber(mSpec.getCertificateSerialNumber()); - certGen.setSubjectDN(mSpec.getCertificateSubject()); - certGen.setIssuerDN(mSpec.getCertificateSubject()); - certGen.setNotBefore(mSpec.getCertificateNotBefore()); - certGen.setNotAfter(mSpec.getCertificateNotAfter()); - certGen.setSignatureAlgorithm(signatureAlgorithm); - return certGen.generate(privateKey); - } - - @SuppressWarnings("deprecation") - private X509Certificate generateSelfSignedCertificateWithFakeSignature( - PublicKey publicKey) throws IOException, CertificateParsingException { - V3TBSCertificateGenerator tbsGenerator = new V3TBSCertificateGenerator(); - ASN1ObjectIdentifier sigAlgOid; - AlgorithmIdentifier sigAlgId; - byte[] signature; - switch (mKeymasterAlgorithm) { - case KeymasterDefs.KM_ALGORITHM_EC: - sigAlgOid = X9ObjectIdentifiers.ecdsa_with_SHA256; - sigAlgId = new AlgorithmIdentifier(sigAlgOid); - ASN1EncodableVector v = new ASN1EncodableVector(); - v.add(new ASN1Integer(BigInteger.valueOf(0))); - v.add(new ASN1Integer(BigInteger.valueOf(0))); - signature = new DERSequence().getEncoded(); - break; - case KeymasterDefs.KM_ALGORITHM_RSA: - sigAlgOid = PKCSObjectIdentifiers.sha256WithRSAEncryption; - sigAlgId = new AlgorithmIdentifier(sigAlgOid, DERNull.INSTANCE); - signature = new byte[1]; - break; - default: - throw new ProviderException("Unsupported key algorithm: " + mKeymasterAlgorithm); - } - - try (ASN1InputStream publicKeyInfoIn = new ASN1InputStream(publicKey.getEncoded())) { - tbsGenerator.setSubjectPublicKeyInfo( - SubjectPublicKeyInfo.getInstance(publicKeyInfoIn.readObject())); - } - tbsGenerator.setSerialNumber(new ASN1Integer(mSpec.getCertificateSerialNumber())); - X509Principal subject = - new X509Principal(mSpec.getCertificateSubject().getEncoded()); - tbsGenerator.setSubject(subject); - tbsGenerator.setIssuer(subject); - tbsGenerator.setStartDate(new Time(mSpec.getCertificateNotBefore())); - tbsGenerator.setEndDate(new Time(mSpec.getCertificateNotAfter())); - tbsGenerator.setSignature(sigAlgId); - TBSCertificate tbsCertificate = tbsGenerator.generateTBSCertificate(); - - ASN1EncodableVector result = new ASN1EncodableVector(); - result.add(tbsCertificate); - result.add(sigAlgId); - result.add(new DERBitString(signature)); - return new X509CertificateObject(Certificate.getInstance(new DERSequence(result))); - } - - private static int getDefaultKeySize(int keymasterAlgorithm) { - switch (keymasterAlgorithm) { - case KeymasterDefs.KM_ALGORITHM_EC: - return EC_DEFAULT_KEY_SIZE; - case KeymasterDefs.KM_ALGORITHM_RSA: - return RSA_DEFAULT_KEY_SIZE; - default: - throw new ProviderException("Unsupported algorithm: " + keymasterAlgorithm); - } - } - - private static void checkValidKeySize( - int keymasterAlgorithm, - int keySize, - boolean isStrongBoxBacked) - throws InvalidAlgorithmParameterException { - switch (keymasterAlgorithm) { - case KeymasterDefs.KM_ALGORITHM_EC: - if (isStrongBoxBacked && keySize != 256) { - throw new InvalidAlgorithmParameterException( - "Unsupported StrongBox EC key size: " - + keySize + " bits. Supported: 256"); - } - if (!SUPPORTED_EC_NIST_CURVE_SIZES.contains(keySize)) { - throw new InvalidAlgorithmParameterException("Unsupported EC key size: " - + keySize + " bits. Supported: " + SUPPORTED_EC_NIST_CURVE_SIZES); - } - break; - case KeymasterDefs.KM_ALGORITHM_RSA: - if (keySize < RSA_MIN_KEY_SIZE || keySize > RSA_MAX_KEY_SIZE) { - throw new InvalidAlgorithmParameterException("RSA key size must be >= " - + RSA_MIN_KEY_SIZE + " and <= " + RSA_MAX_KEY_SIZE); - } - break; - default: - throw new ProviderException("Unsupported algorithm: " + keymasterAlgorithm); - } - } - - /** - * Returns the {@code Signature} algorithm to be used for signing a certificate using the - * specified key or {@code null} if the key cannot be used for signing a certificate. - */ - @Nullable - private static String getCertificateSignatureAlgorithm( - int keymasterAlgorithm, - int keySizeBits, - KeyGenParameterSpec spec) { - // Constraints: - // 1. Key must be authorized for signing without user authentication. - // 2. Signature digest must be one of key's authorized digests. - // 3. For RSA keys, the digest output size must not exceed modulus size minus space overhead - // of RSA PKCS#1 signature padding scheme (about 30 bytes). - // 4. For EC keys, the there is no point in using a digest whose output size is longer than - // key/field size because the digest will be truncated to that size. - - if ((spec.getPurposes() & KeyProperties.PURPOSE_SIGN) == 0) { - // Key not authorized for signing - return null; - } - if (spec.isUserAuthenticationRequired()) { - // Key not authorized for use without user authentication - return null; - } - if (!spec.isDigestsSpecified()) { - // Key not authorized for any digests -- can't sign - return null; - } - switch (keymasterAlgorithm) { - case KeymasterDefs.KM_ALGORITHM_EC: - { - Set<Integer> availableKeymasterDigests = getAvailableKeymasterSignatureDigests( - spec.getDigests(), - AndroidKeyStoreBCWorkaroundProvider.getSupportedEcdsaSignatureDigests()); - - int bestKeymasterDigest = -1; - int bestDigestOutputSizeBits = -1; - for (int keymasterDigest : availableKeymasterDigests) { - int outputSizeBits = KeymasterUtils.getDigestOutputSizeBits(keymasterDigest); - if (outputSizeBits == keySizeBits) { - // Perfect match -- use this digest - bestKeymasterDigest = keymasterDigest; - bestDigestOutputSizeBits = outputSizeBits; - break; - } - // Not a perfect match -- check against the best digest so far - if (bestKeymasterDigest == -1) { - // First digest tested -- definitely the best so far - bestKeymasterDigest = keymasterDigest; - bestDigestOutputSizeBits = outputSizeBits; - } else { - // Prefer output size to be as close to key size as possible, with output - // sizes larger than key size preferred to those smaller than key size. - if (bestDigestOutputSizeBits < keySizeBits) { - // Output size of the best digest so far is smaller than key size. - // Anything larger is a win. - if (outputSizeBits > bestDigestOutputSizeBits) { - bestKeymasterDigest = keymasterDigest; - bestDigestOutputSizeBits = outputSizeBits; - } - } else { - // Output size of the best digest so far is larger than key size. - // Anything smaller is a win, as long as it's not smaller than key size. - if ((outputSizeBits < bestDigestOutputSizeBits) - && (outputSizeBits >= keySizeBits)) { - bestKeymasterDigest = keymasterDigest; - bestDigestOutputSizeBits = outputSizeBits; - } - } - } - } - if (bestKeymasterDigest == -1) { - return null; - } - return KeyProperties.Digest.fromKeymasterToSignatureAlgorithmDigest( - bestKeymasterDigest) + "WithECDSA"; - } - case KeymasterDefs.KM_ALGORITHM_RSA: - { - // Check whether this key is authorized for PKCS#1 signature padding. - // We use Bouncy Castle to generate self-signed RSA certificates. Bouncy Castle - // only supports RSA certificates signed using PKCS#1 padding scheme. The key needs - // to be authorized for PKCS#1 padding or padding NONE which means any padding. - boolean pkcs1SignaturePaddingSupported = - com.android.internal.util.ArrayUtils.contains( - KeyProperties.SignaturePadding.allToKeymaster( - spec.getSignaturePaddings()), - KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN); - if (!pkcs1SignaturePaddingSupported) { - // Key not authorized for PKCS#1 signature padding -- can't sign - return null; - } - - Set<Integer> availableKeymasterDigests = getAvailableKeymasterSignatureDigests( - spec.getDigests(), - AndroidKeyStoreBCWorkaroundProvider.getSupportedEcdsaSignatureDigests()); - - // The amount of space available for the digest is less than modulus size by about - // 30 bytes because padding must be at least 11 bytes long (00 || 01 || PS || 00, - // where PS must be at least 8 bytes long), and then there's also the 15--19 bytes - // overhead (depending the on chosen digest) for encoding digest OID and digest - // value in DER. - int maxDigestOutputSizeBits = keySizeBits - 30 * 8; - int bestKeymasterDigest = -1; - int bestDigestOutputSizeBits = -1; - for (int keymasterDigest : availableKeymasterDigests) { - int outputSizeBits = KeymasterUtils.getDigestOutputSizeBits(keymasterDigest); - if (outputSizeBits > maxDigestOutputSizeBits) { - // Digest too long (signature generation will fail) -- skip - continue; - } - if (bestKeymasterDigest == -1) { - // First digest tested -- definitely the best so far - bestKeymasterDigest = keymasterDigest; - bestDigestOutputSizeBits = outputSizeBits; - } else { - // The longer the better - if (outputSizeBits > bestDigestOutputSizeBits) { - bestKeymasterDigest = keymasterDigest; - bestDigestOutputSizeBits = outputSizeBits; - } - } - } - if (bestKeymasterDigest == -1) { - return null; - } - return KeyProperties.Digest.fromKeymasterToSignatureAlgorithmDigest( - bestKeymasterDigest) + "WithRSA"; - } - default: - throw new ProviderException("Unsupported algorithm: " + keymasterAlgorithm); - } - } - - private static Set<Integer> getAvailableKeymasterSignatureDigests( - @KeyProperties.DigestEnum String[] authorizedKeyDigests, - @KeyProperties.DigestEnum String[] supportedSignatureDigests) { - Set<Integer> authorizedKeymasterKeyDigests = new HashSet<Integer>(); - for (int keymasterDigest : KeyProperties.Digest.allToKeymaster(authorizedKeyDigests)) { - authorizedKeymasterKeyDigests.add(keymasterDigest); - } - Set<Integer> supportedKeymasterSignatureDigests = new HashSet<Integer>(); - for (int keymasterDigest - : KeyProperties.Digest.allToKeymaster(supportedSignatureDigests)) { - supportedKeymasterSignatureDigests.add(keymasterDigest); - } - Set<Integer> result = new HashSet<Integer>(supportedKeymasterSignatureDigests); - result.retainAll(authorizedKeymasterKeyDigests); - return result; - } -} diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreLoadStoreParameter.java b/keystore/java/android/security/keystore/AndroidKeyStoreLoadStoreParameter.java deleted file mode 100644 index 45d579e371c6..000000000000 --- a/keystore/java/android/security/keystore/AndroidKeyStoreLoadStoreParameter.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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.keystore; - -import java.security.KeyStore; -import java.security.KeyStore.ProtectionParameter; - -class AndroidKeyStoreLoadStoreParameter implements KeyStore.LoadStoreParameter { - - private final int mUid; - - AndroidKeyStoreLoadStoreParameter(int uid) { - mUid = uid; - } - - @Override - public ProtectionParameter getProtectionParameter() { - return null; - } - - int getUid() { - return mUid; - } -} diff --git a/keystore/java/android/security/keystore/AndroidKeyStorePrivateKey.java b/keystore/java/android/security/keystore/AndroidKeyStorePrivateKey.java deleted file mode 100644 index 06e4c88fa632..000000000000 --- a/keystore/java/android/security/keystore/AndroidKeyStorePrivateKey.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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.keystore; - -import java.security.PrivateKey; - -/** - * {@link PrivateKey} backed by Android Keystore. - * - * @hide - */ -public class AndroidKeyStorePrivateKey extends AndroidKeyStoreKey implements PrivateKey { - - public AndroidKeyStorePrivateKey(String alias, int uid, String algorithm) { - super(alias, uid, algorithm); - } -} diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java index 087151711138..ecb082e71321 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java @@ -20,30 +20,13 @@ import android.annotation.NonNull; import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.security.KeyStore; -import android.security.keymaster.ExportResult; -import android.security.keymaster.KeyCharacteristics; -import android.security.keymaster.KeymasterDefs; import java.io.IOException; -import java.security.KeyFactory; -import java.security.KeyPair; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.Provider; -import java.security.ProviderException; -import java.security.PublicKey; -import java.security.Security; -import java.security.Signature; -import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; -import java.security.interfaces.ECKey; -import java.security.interfaces.ECPublicKey; -import java.security.interfaces.RSAKey; -import java.security.interfaces.RSAPublicKey; -import java.security.spec.InvalidKeySpecException; -import java.security.spec.X509EncodedKeySpec; -import java.util.List; import javax.crypto.Cipher; import javax.crypto.Mac; @@ -57,117 +40,10 @@ import javax.crypto.Mac; public class AndroidKeyStoreProvider extends Provider { private static final String PROVIDER_NAME = "AndroidKeyStore"; - // IMPLEMENTATION NOTE: Class names are hard-coded in this provider to avoid loading these - // classes when this provider is instantiated and installed early on during each app's - // initialization process. - // - // Crypto operations operating on the AndroidKeyStore keys must not be offered by this provider. - // Instead, they need to be offered by AndroidKeyStoreBCWorkaroundProvider. See its Javadoc - // for details. - - private static final String PACKAGE_NAME = "android.security.keystore"; - - private static final String DESEDE_SYSTEM_PROPERTY = - "ro.hardware.keystore_desede"; - /** @hide */ - public AndroidKeyStoreProvider() { - this(PROVIDER_NAME); - } - - /** @hide **/ - public AndroidKeyStoreProvider(String providerName) { - super(providerName, 1.0, "Android KeyStore security provider"); - - boolean supports3DES = "true".equals(android.os.SystemProperties.get(DESEDE_SYSTEM_PROPERTY)); - - // java.security.KeyStore - put("KeyStore." + providerName, PACKAGE_NAME + ".AndroidKeyStoreSpi"); - - // java.security.KeyPairGenerator - put("KeyPairGenerator.EC", PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$EC"); - put("KeyPairGenerator.RSA", PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$RSA"); - - // java.security.KeyFactory - putKeyFactoryImpl("EC"); - putKeyFactoryImpl("RSA"); - - // javax.crypto.KeyGenerator - put("KeyGenerator.AES", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$AES"); - put("KeyGenerator.HmacSHA1", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA1"); - put("KeyGenerator.HmacSHA224", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA224"); - put("KeyGenerator.HmacSHA256", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA256"); - put("KeyGenerator.HmacSHA384", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA384"); - put("KeyGenerator.HmacSHA512", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA512"); - - if (supports3DES) { - put("KeyGenerator.DESede", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$DESede"); - } - - // java.security.SecretKeyFactory - putSecretKeyFactoryImpl("AES"); - if (supports3DES) { - putSecretKeyFactoryImpl("DESede"); - } - putSecretKeyFactoryImpl("HmacSHA1"); - putSecretKeyFactoryImpl("HmacSHA224"); - putSecretKeyFactoryImpl("HmacSHA256"); - putSecretKeyFactoryImpl("HmacSHA384"); - putSecretKeyFactoryImpl("HmacSHA512"); - } - - /** - * This function indicates whether or not Keystore 2.0 is enabled. Some parts of the - * Keystore SPI must behave subtly differently when Keystore 2.0 is enabled. However, - * the platform property that indicates that Keystore 2.0 is enabled is not readable - * by applications. So we set this value when {@code install()} is called because it - * is called by zygote, which can access Keystore2Properties. - * - * This function can be removed once the transition to Keystore 2.0 is complete. - * b/171305684 - * - * @return true if Keystore 2.0 is enabled. - * @hide - */ - public static boolean isKeystore2Enabled() { - return android.security.keystore2.AndroidKeyStoreProvider.isInstalled(); - } - - /** - * Installs a new instance of this provider (and the - * {@link AndroidKeyStoreBCWorkaroundProvider}). - * @hide - */ - public static void install() { - Provider[] providers = Security.getProviders(); - int bcProviderIndex = -1; - for (int i = 0; i < providers.length; i++) { - Provider provider = providers[i]; - if ("BC".equals(provider.getName())) { - bcProviderIndex = i; - break; - } - } - - Security.addProvider(new AndroidKeyStoreProvider()); - Provider workaroundProvider = new AndroidKeyStoreBCWorkaroundProvider(); - if (bcProviderIndex != -1) { - // Bouncy Castle provider found -- install the workaround provider above it. - // insertProviderAt uses 1-based positions. - Security.insertProviderAt(workaroundProvider, bcProviderIndex + 1); - } else { - // Bouncy Castle provider not found -- install the workaround provider at lowest - // priority. - Security.addProvider(workaroundProvider); - } - } - - private void putSecretKeyFactoryImpl(String algorithm) { - put("SecretKeyFactory." + algorithm, PACKAGE_NAME + ".AndroidKeyStoreSecretKeyFactorySpi"); - } - - private void putKeyFactoryImpl(String algorithm) { - put("KeyFactory." + algorithm, PACKAGE_NAME + ".AndroidKeyStoreKeyFactorySpi"); + public AndroidKeyStoreProvider(@NonNull String name) { + super(name, 1.0, "Android KeyStore security provider"); + throw new IllegalStateException("Should not be instantiated."); } /** @@ -189,229 +65,7 @@ public class AndroidKeyStoreProvider extends Provider { if (cryptoPrimitive == null) { throw new NullPointerException(); } - Object spi; - if (cryptoPrimitive instanceof Signature) { - spi = ((Signature) cryptoPrimitive).getCurrentSpi(); - } else if (cryptoPrimitive instanceof Mac) { - spi = ((Mac) cryptoPrimitive).getCurrentSpi(); - } else if (cryptoPrimitive instanceof Cipher) { - spi = ((Cipher) cryptoPrimitive).getCurrentSpi(); - } else { - throw new IllegalArgumentException("Unsupported crypto primitive: " + cryptoPrimitive - + ". Supported: Signature, Mac, Cipher"); - } - if (spi == null) { - throw new IllegalStateException("Crypto primitive not initialized"); - } else if (!(spi instanceof KeyStoreCryptoOperation)) { - throw new IllegalArgumentException( - "Crypto primitive not backed by AndroidKeyStore provider: " + cryptoPrimitive - + ", spi: " + spi); - } - return ((KeyStoreCryptoOperation) spi).getOperationHandle(); - } - - /** @hide **/ - @NonNull - public static AndroidKeyStorePublicKey getAndroidKeyStorePublicKey( - @NonNull String alias, - int uid, - @NonNull @KeyProperties.KeyAlgorithmEnum String keyAlgorithm, - @NonNull byte[] x509EncodedForm) { - PublicKey publicKey; - try { - KeyFactory keyFactory = KeyFactory.getInstance(keyAlgorithm); - publicKey = keyFactory.generatePublic(new X509EncodedKeySpec(x509EncodedForm)); - } catch (NoSuchAlgorithmException e) { - throw new ProviderException( - "Failed to obtain " + keyAlgorithm + " KeyFactory", e); - } catch (InvalidKeySpecException e) { - throw new ProviderException("Invalid X.509 encoding of public key", e); - } - if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyAlgorithm)) { - return new AndroidKeyStoreECPublicKey(alias, uid, (ECPublicKey) publicKey); - } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) { - return new AndroidKeyStoreRSAPublicKey(alias, uid, (RSAPublicKey) publicKey); - } else { - throw new ProviderException("Unsupported Android Keystore public key algorithm: " - + keyAlgorithm); - } - } - - @NonNull - private static AndroidKeyStorePrivateKey getAndroidKeyStorePrivateKey( - @NonNull AndroidKeyStorePublicKey publicKey) { - String keyAlgorithm = publicKey.getAlgorithm(); - if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyAlgorithm)) { - return new AndroidKeyStoreECPrivateKey( - publicKey.getAlias(), publicKey.getUid(), ((ECKey) publicKey).getParams()); - } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) { - return new AndroidKeyStoreRSAPrivateKey( - publicKey.getAlias(), publicKey.getUid(), ((RSAKey) publicKey).getModulus()); - } else { - throw new ProviderException("Unsupported Android Keystore public key algorithm: " - + keyAlgorithm); - } - } - - @NonNull - private static KeyCharacteristics getKeyCharacteristics(@NonNull KeyStore keyStore, - @NonNull String alias, int uid) - throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException { - KeyCharacteristics keyCharacteristics = new KeyCharacteristics(); - int errorCode = keyStore.getKeyCharacteristics( - alias, null, null, uid, keyCharacteristics); - if (errorCode == KeyStore.KEY_PERMANENTLY_INVALIDATED) { - throw (KeyPermanentlyInvalidatedException) - new KeyPermanentlyInvalidatedException( - "User changed or deleted their auth credentials", - KeyStore.getKeyStoreException(errorCode)); - } - if (errorCode != KeyStore.NO_ERROR) { - throw (UnrecoverableKeyException) - new UnrecoverableKeyException("Failed to obtain information about key") - .initCause(KeyStore.getKeyStoreException(errorCode)); - } - return keyCharacteristics; - } - - @NonNull - private static AndroidKeyStorePublicKey loadAndroidKeyStorePublicKeyFromKeystore( - @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid, - KeyCharacteristics keyCharacteristics) - throws UnrecoverableKeyException { - ExportResult exportResult = keyStore.exportKey( - privateKeyAlias, KeymasterDefs.KM_KEY_FORMAT_X509, null, null, uid); - if (exportResult.resultCode != KeyStore.NO_ERROR) { - throw (UnrecoverableKeyException) - new UnrecoverableKeyException("Failed to obtain X.509 form of public key") - .initCause(KeyStore.getKeyStoreException(exportResult.resultCode)); - } - final byte[] x509EncodedPublicKey = exportResult.exportData; - - Integer keymasterAlgorithm = keyCharacteristics.getEnum(KeymasterDefs.KM_TAG_ALGORITHM); - if (keymasterAlgorithm == null) { - throw new UnrecoverableKeyException("Key algorithm unknown"); - } - - String jcaKeyAlgorithm; - try { - jcaKeyAlgorithm = KeyProperties.KeyAlgorithm.fromKeymasterAsymmetricKeyAlgorithm( - keymasterAlgorithm); - } catch (IllegalArgumentException e) { - throw (UnrecoverableKeyException) - new UnrecoverableKeyException("Failed to load private key") - .initCause(e); - } - - return AndroidKeyStoreProvider.getAndroidKeyStorePublicKey( - privateKeyAlias, uid, jcaKeyAlgorithm, x509EncodedPublicKey); - } - - /** @hide **/ - @NonNull - public static AndroidKeyStorePublicKey loadAndroidKeyStorePublicKeyFromKeystore( - @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid) - throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException { - return loadAndroidKeyStorePublicKeyFromKeystore(keyStore, privateKeyAlias, uid, - getKeyCharacteristics(keyStore, privateKeyAlias, uid)); - } - - @NonNull - private static KeyPair loadAndroidKeyStoreKeyPairFromKeystore( - @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid, - @NonNull KeyCharacteristics keyCharacteristics) - throws UnrecoverableKeyException { - AndroidKeyStorePublicKey publicKey = - loadAndroidKeyStorePublicKeyFromKeystore(keyStore, privateKeyAlias, uid, - keyCharacteristics); - AndroidKeyStorePrivateKey privateKey = - AndroidKeyStoreProvider.getAndroidKeyStorePrivateKey(publicKey); - return new KeyPair(publicKey, privateKey); - } - - /** @hide **/ - @NonNull - public static KeyPair loadAndroidKeyStoreKeyPairFromKeystore( - @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid) - throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException { - return loadAndroidKeyStoreKeyPairFromKeystore(keyStore, privateKeyAlias, uid, - getKeyCharacteristics(keyStore, privateKeyAlias, uid)); - } - - @NonNull - private static AndroidKeyStorePrivateKey loadAndroidKeyStorePrivateKeyFromKeystore( - @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid, - @NonNull KeyCharacteristics keyCharacteristics) - throws UnrecoverableKeyException { - KeyPair keyPair = loadAndroidKeyStoreKeyPairFromKeystore(keyStore, privateKeyAlias, uid, - keyCharacteristics); - return (AndroidKeyStorePrivateKey) keyPair.getPrivate(); - } - - /** @hide **/ - @NonNull - public static AndroidKeyStorePrivateKey loadAndroidKeyStorePrivateKeyFromKeystore( - @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid) - throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException { - return loadAndroidKeyStorePrivateKeyFromKeystore(keyStore, privateKeyAlias, uid, - getKeyCharacteristics(keyStore, privateKeyAlias, uid)); - } - - @NonNull - private static AndroidKeyStoreSecretKey loadAndroidKeyStoreSecretKeyFromKeystore( - @NonNull String secretKeyAlias, int uid, @NonNull KeyCharacteristics keyCharacteristics) - throws UnrecoverableKeyException { - Integer keymasterAlgorithm = keyCharacteristics.getEnum(KeymasterDefs.KM_TAG_ALGORITHM); - if (keymasterAlgorithm == null) { - throw new UnrecoverableKeyException("Key algorithm unknown"); - } - - List<Integer> keymasterDigests = keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_DIGEST); - int keymasterDigest; - if (keymasterDigests.isEmpty()) { - keymasterDigest = -1; - } else { - // More than one digest can be permitted for this key. Use the first one to form the - // JCA key algorithm name. - keymasterDigest = keymasterDigests.get(0); - } - - @KeyProperties.KeyAlgorithmEnum String keyAlgorithmString; - try { - keyAlgorithmString = KeyProperties.KeyAlgorithm.fromKeymasterSecretKeyAlgorithm( - keymasterAlgorithm, keymasterDigest); - } catch (IllegalArgumentException e) { - throw (UnrecoverableKeyException) - new UnrecoverableKeyException("Unsupported secret key type").initCause(e); - } - - return new AndroidKeyStoreSecretKey(secretKeyAlias, uid, keyAlgorithmString); - } - - /** @hide **/ - @NonNull - public static AndroidKeyStoreKey loadAndroidKeyStoreKeyFromKeystore( - @NonNull KeyStore keyStore, @NonNull String userKeyAlias, int uid) - throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException { - KeyCharacteristics keyCharacteristics = getKeyCharacteristics(keyStore, userKeyAlias, uid); - - Integer keymasterAlgorithm = keyCharacteristics.getEnum(KeymasterDefs.KM_TAG_ALGORITHM); - if (keymasterAlgorithm == null) { - throw new UnrecoverableKeyException("Key algorithm unknown"); - } - - if (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC || - keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_AES || - keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_3DES) { - return loadAndroidKeyStoreSecretKeyFromKeystore(userKeyAlias, uid, - keyCharacteristics); - } else if (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_RSA || - keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_EC) { - return loadAndroidKeyStorePrivateKeyFromKeystore(keyStore, userKeyAlias, uid, - keyCharacteristics); - } else { - throw new UnrecoverableKeyException("Key algorithm unknown"); - } + return 0; } /** @@ -434,13 +88,9 @@ public class AndroidKeyStoreProvider extends Provider { @NonNull public static java.security.KeyStore getKeyStoreForUid(int uid) throws KeyStoreException, NoSuchProviderException { - final java.security.KeyStore.LoadStoreParameter loadParameter; - if (android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) { - loadParameter = new android.security.keystore2.AndroidKeyStoreLoadStoreParameter( - KeyProperties.legacyUidToNamespace(uid)); - } else { - loadParameter = new AndroidKeyStoreLoadStoreParameter(uid); - } + final java.security.KeyStore.LoadStoreParameter loadParameter = + new android.security.keystore2.AndroidKeyStoreLoadStoreParameter( + KeyProperties.legacyUidToNamespace(uid)); java.security.KeyStore result = java.security.KeyStore.getInstance(PROVIDER_NAME); try { result.load(loadParameter); diff --git a/keystore/java/android/security/keystore/AndroidKeyStorePublicKey.java b/keystore/java/android/security/keystore/AndroidKeyStorePublicKey.java deleted file mode 100644 index 4194780906b7..000000000000 --- a/keystore/java/android/security/keystore/AndroidKeyStorePublicKey.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * 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.keystore; - -import java.security.PublicKey; -import java.util.Arrays; - -/** - * {@link PublicKey} backed by Android Keystore. - * - * @hide - */ -public class AndroidKeyStorePublicKey extends AndroidKeyStoreKey implements PublicKey { - - private final byte[] mEncoded; - - public AndroidKeyStorePublicKey(String alias, int uid, String algorithm, byte[] x509EncodedForm) { - super(alias, uid, algorithm); - mEncoded = ArrayUtils.cloneIfNotEmpty(x509EncodedForm); - } - - @Override - public String getFormat() { - return "X.509"; - } - - @Override - public byte[] getEncoded() { - return ArrayUtils.cloneIfNotEmpty(mEncoded); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + Arrays.hashCode(mEncoded); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!super.equals(obj)) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - AndroidKeyStorePublicKey other = (AndroidKeyStorePublicKey) obj; - if (!Arrays.equals(mEncoded, other.mEncoded)) { - return false; - } - return true; - } -} diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java deleted file mode 100644 index 2ae68fa80117..000000000000 --- a/keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java +++ /dev/null @@ -1,515 +0,0 @@ -/* - * 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.keystore; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.security.KeyStore; -import android.security.keymaster.KeyCharacteristics; -import android.security.keymaster.KeymasterArguments; -import android.security.keymaster.KeymasterDefs; - -import java.security.AlgorithmParameters; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.Key; -import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.ProviderException; -import java.security.spec.AlgorithmParameterSpec; -import java.security.spec.InvalidParameterSpecException; -import java.security.spec.MGF1ParameterSpec; - -import javax.crypto.Cipher; -import javax.crypto.CipherSpi; -import javax.crypto.spec.OAEPParameterSpec; -import javax.crypto.spec.PSource; - -/** - * Base class for {@link CipherSpi} providing Android KeyStore backed RSA encryption/decryption. - * - * @hide - */ -abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase { - - /** - * Raw RSA cipher without any padding. - */ - public static final class NoPadding extends AndroidKeyStoreRSACipherSpi { - public NoPadding() { - super(KeymasterDefs.KM_PAD_NONE); - } - - @Override - protected boolean adjustConfigForEncryptingWithPrivateKey() { - // RSA encryption with no padding using private key is a way to implement raw RSA - // signatures which JCA does not expose via Signature. We thus have to support this. - setKeymasterPurposeOverride(KeymasterDefs.KM_PURPOSE_SIGN); - return true; - } - - @Override - protected void initAlgorithmSpecificParameters() throws InvalidKeyException {} - - @Override - protected void initAlgorithmSpecificParameters(@Nullable AlgorithmParameterSpec params) - throws InvalidAlgorithmParameterException { - if (params != null) { - throw new InvalidAlgorithmParameterException( - "Unexpected parameters: " + params + ". No parameters supported"); - } - } - - @Override - protected void initAlgorithmSpecificParameters(@Nullable AlgorithmParameters params) - throws InvalidAlgorithmParameterException { - - if (params != null) { - throw new InvalidAlgorithmParameterException( - "Unexpected parameters: " + params + ". No parameters supported"); - } - } - - @Override - protected AlgorithmParameters engineGetParameters() { - return null; - } - - @Override - protected final int getAdditionalEntropyAmountForBegin() { - return 0; - } - - @Override - protected final int getAdditionalEntropyAmountForFinish() { - return 0; - } - } - - /** - * RSA cipher with PKCS#1 v1.5 encryption padding. - */ - public static final class PKCS1Padding extends AndroidKeyStoreRSACipherSpi { - public PKCS1Padding() { - super(KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT); - } - - @Override - protected boolean adjustConfigForEncryptingWithPrivateKey() { - // RSA encryption with PCKS#1 padding using private key is a way to implement RSA - // signatures with PKCS#1 padding. We have to support this for legacy reasons. - setKeymasterPurposeOverride(KeymasterDefs.KM_PURPOSE_SIGN); - setKeymasterPaddingOverride(KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN); - return true; - } - - @Override - protected void initAlgorithmSpecificParameters() throws InvalidKeyException {} - - @Override - protected void initAlgorithmSpecificParameters(@Nullable AlgorithmParameterSpec params) - throws InvalidAlgorithmParameterException { - if (params != null) { - throw new InvalidAlgorithmParameterException( - "Unexpected parameters: " + params + ". No parameters supported"); - } - } - - @Override - protected void initAlgorithmSpecificParameters(@Nullable AlgorithmParameters params) - throws InvalidAlgorithmParameterException { - - if (params != null) { - throw new InvalidAlgorithmParameterException( - "Unexpected parameters: " + params + ". No parameters supported"); - } - } - - @Override - protected AlgorithmParameters engineGetParameters() { - return null; - } - - @Override - protected final int getAdditionalEntropyAmountForBegin() { - return 0; - } - - @Override - protected final int getAdditionalEntropyAmountForFinish() { - return (isEncrypting()) ? getModulusSizeBytes() : 0; - } - } - - /** - * RSA cipher with OAEP encryption padding. Only SHA-1 based MGF1 is supported as MGF. - */ - abstract static class OAEPWithMGF1Padding extends AndroidKeyStoreRSACipherSpi { - - private static final String MGF_ALGORITGM_MGF1 = "MGF1"; - - private int mKeymasterDigest = -1; - private int mDigestOutputSizeBytes; - - OAEPWithMGF1Padding(int keymasterDigest) { - super(KeymasterDefs.KM_PAD_RSA_OAEP); - mKeymasterDigest = keymasterDigest; - mDigestOutputSizeBytes = - (KeymasterUtils.getDigestOutputSizeBits(keymasterDigest) + 7) / 8; - } - - @Override - protected final void initAlgorithmSpecificParameters() throws InvalidKeyException {} - - @Override - protected final void initAlgorithmSpecificParameters( - @Nullable AlgorithmParameterSpec params) throws InvalidAlgorithmParameterException { - if (params == null) { - return; - } - - if (!(params instanceof OAEPParameterSpec)) { - throw new InvalidAlgorithmParameterException( - "Unsupported parameter spec: " + params - + ". Only OAEPParameterSpec supported"); - } - OAEPParameterSpec spec = (OAEPParameterSpec) params; - if (!MGF_ALGORITGM_MGF1.equalsIgnoreCase(spec.getMGFAlgorithm())) { - throw new InvalidAlgorithmParameterException( - "Unsupported MGF: " + spec.getMGFAlgorithm() - + ". Only " + MGF_ALGORITGM_MGF1 + " supported"); - } - String jcaDigest = spec.getDigestAlgorithm(); - int keymasterDigest; - try { - keymasterDigest = KeyProperties.Digest.toKeymaster(jcaDigest); - } catch (IllegalArgumentException e) { - throw new InvalidAlgorithmParameterException( - "Unsupported digest: " + jcaDigest, e); - } - switch (keymasterDigest) { - case KeymasterDefs.KM_DIGEST_SHA1: - case KeymasterDefs.KM_DIGEST_SHA_2_224: - case KeymasterDefs.KM_DIGEST_SHA_2_256: - case KeymasterDefs.KM_DIGEST_SHA_2_384: - case KeymasterDefs.KM_DIGEST_SHA_2_512: - // Permitted. - break; - default: - throw new InvalidAlgorithmParameterException( - "Unsupported digest: " + jcaDigest); - } - AlgorithmParameterSpec mgfParams = spec.getMGFParameters(); - if (mgfParams == null) { - throw new InvalidAlgorithmParameterException("MGF parameters must be provided"); - } - // Check whether MGF parameters match the OAEPParameterSpec - if (!(mgfParams instanceof MGF1ParameterSpec)) { - throw new InvalidAlgorithmParameterException("Unsupported MGF parameters" - + ": " + mgfParams + ". Only MGF1ParameterSpec supported"); - } - MGF1ParameterSpec mgfSpec = (MGF1ParameterSpec) mgfParams; - String mgf1JcaDigest = mgfSpec.getDigestAlgorithm(); - if (!KeyProperties.DIGEST_SHA1.equalsIgnoreCase(mgf1JcaDigest)) { - throw new InvalidAlgorithmParameterException( - "Unsupported MGF1 digest: " + mgf1JcaDigest - + ". Only " + KeyProperties.DIGEST_SHA1 + " supported"); - } - PSource pSource = spec.getPSource(); - if (!(pSource instanceof PSource.PSpecified)) { - throw new InvalidAlgorithmParameterException( - "Unsupported source of encoding input P: " + pSource - + ". Only pSpecifiedEmpty (PSource.PSpecified.DEFAULT) supported"); - } - PSource.PSpecified pSourceSpecified = (PSource.PSpecified) pSource; - byte[] pSourceValue = pSourceSpecified.getValue(); - if ((pSourceValue != null) && (pSourceValue.length > 0)) { - throw new InvalidAlgorithmParameterException( - "Unsupported source of encoding input P: " + pSource - + ". Only pSpecifiedEmpty (PSource.PSpecified.DEFAULT) supported"); - } - mKeymasterDigest = keymasterDigest; - mDigestOutputSizeBytes = - (KeymasterUtils.getDigestOutputSizeBits(keymasterDigest) + 7) / 8; - } - - @Override - protected final void initAlgorithmSpecificParameters(@Nullable AlgorithmParameters params) - throws InvalidAlgorithmParameterException { - if (params == null) { - return; - } - - OAEPParameterSpec spec; - try { - spec = params.getParameterSpec(OAEPParameterSpec.class); - } catch (InvalidParameterSpecException e) { - throw new InvalidAlgorithmParameterException("OAEP parameters required" - + ", but not found in parameters: " + params, e); - } - if (spec == null) { - throw new InvalidAlgorithmParameterException("OAEP parameters required" - + ", but not provided in parameters: " + params); - } - initAlgorithmSpecificParameters(spec); - } - - @Override - protected final AlgorithmParameters engineGetParameters() { - OAEPParameterSpec spec = - new OAEPParameterSpec( - KeyProperties.Digest.fromKeymaster(mKeymasterDigest), - MGF_ALGORITGM_MGF1, - MGF1ParameterSpec.SHA1, - PSource.PSpecified.DEFAULT); - try { - AlgorithmParameters params = AlgorithmParameters.getInstance("OAEP"); - params.init(spec); - return params; - } catch (NoSuchAlgorithmException e) { - throw new ProviderException( - "Failed to obtain OAEP AlgorithmParameters", e); - } catch (InvalidParameterSpecException e) { - throw new ProviderException( - "Failed to initialize OAEP AlgorithmParameters with an IV", - e); - } - } - - @Override - protected final void addAlgorithmSpecificParametersToBegin( - KeymasterArguments keymasterArgs) { - super.addAlgorithmSpecificParametersToBegin(keymasterArgs); - keymasterArgs.addEnum(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest); - } - - @Override - protected final void loadAlgorithmSpecificParametersFromBeginResult( - @NonNull KeymasterArguments keymasterArgs) { - super.loadAlgorithmSpecificParametersFromBeginResult(keymasterArgs); - } - - @Override - protected final int getAdditionalEntropyAmountForBegin() { - return 0; - } - - @Override - protected final int getAdditionalEntropyAmountForFinish() { - return (isEncrypting()) ? mDigestOutputSizeBytes : 0; - } - } - - public static class OAEPWithSHA1AndMGF1Padding extends OAEPWithMGF1Padding { - public OAEPWithSHA1AndMGF1Padding() { - super(KeymasterDefs.KM_DIGEST_SHA1); - } - } - - public static class OAEPWithSHA224AndMGF1Padding extends OAEPWithMGF1Padding { - public OAEPWithSHA224AndMGF1Padding() { - super(KeymasterDefs.KM_DIGEST_SHA_2_224); - } - } - - public static class OAEPWithSHA256AndMGF1Padding extends OAEPWithMGF1Padding { - public OAEPWithSHA256AndMGF1Padding() { - super(KeymasterDefs.KM_DIGEST_SHA_2_256); - } - } - - public static class OAEPWithSHA384AndMGF1Padding extends OAEPWithMGF1Padding { - public OAEPWithSHA384AndMGF1Padding() { - super(KeymasterDefs.KM_DIGEST_SHA_2_384); - } - } - - public static class OAEPWithSHA512AndMGF1Padding extends OAEPWithMGF1Padding { - public OAEPWithSHA512AndMGF1Padding() { - super(KeymasterDefs.KM_DIGEST_SHA_2_512); - } - } - - private final int mKeymasterPadding; - private int mKeymasterPaddingOverride; - - private int mModulusSizeBytes = -1; - - AndroidKeyStoreRSACipherSpi(int keymasterPadding) { - mKeymasterPadding = keymasterPadding; - } - - @Override - protected final void initKey(int opmode, Key key) throws InvalidKeyException { - if (key == null) { - throw new InvalidKeyException("Unsupported key: null"); - } - if (!KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(key.getAlgorithm())) { - throw new InvalidKeyException("Unsupported key algorithm: " + key.getAlgorithm() - + ". Only " + KeyProperties.KEY_ALGORITHM_RSA + " supported"); - } - AndroidKeyStoreKey keystoreKey; - if (key instanceof AndroidKeyStorePrivateKey) { - keystoreKey = (AndroidKeyStoreKey) key; - } else if (key instanceof AndroidKeyStorePublicKey) { - keystoreKey = (AndroidKeyStoreKey) key; - } else { - throw new InvalidKeyException("Unsupported key type: " + key); - } - - if (keystoreKey instanceof PrivateKey) { - // Private key - switch (opmode) { - case Cipher.DECRYPT_MODE: - case Cipher.UNWRAP_MODE: - // Permitted - break; - case Cipher.ENCRYPT_MODE: - case Cipher.WRAP_MODE: - if (!adjustConfigForEncryptingWithPrivateKey()) { - throw new InvalidKeyException( - "RSA private keys cannot be used with " + opmodeToString(opmode) - + " and padding " - + KeyProperties.EncryptionPadding.fromKeymaster(mKeymasterPadding) - + ". Only RSA public keys supported for this mode"); - } - break; - default: - throw new InvalidKeyException( - "RSA private keys cannot be used with opmode: " + opmode); - } - } else { - // Public key - switch (opmode) { - case Cipher.ENCRYPT_MODE: - case Cipher.WRAP_MODE: - // Permitted - break; - case Cipher.DECRYPT_MODE: - case Cipher.UNWRAP_MODE: - throw new InvalidKeyException( - "RSA public keys cannot be used with " + opmodeToString(opmode) - + " and padding " - + KeyProperties.EncryptionPadding.fromKeymaster(mKeymasterPadding) - + ". Only RSA private keys supported for this opmode."); - // break; - default: - throw new InvalidKeyException( - "RSA public keys cannot be used with " + opmodeToString(opmode)); - } - } - - KeyCharacteristics keyCharacteristics = new KeyCharacteristics(); - int errorCode = getKeyStore().getKeyCharacteristics( - keystoreKey.getAlias(), null, null, keystoreKey.getUid(), keyCharacteristics); - if (errorCode != KeyStore.NO_ERROR) { - throw getKeyStore().getInvalidKeyException( - keystoreKey.getAlias(), keystoreKey.getUid(), errorCode); - } - long keySizeBits = keyCharacteristics.getUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, -1); - if (keySizeBits == -1) { - throw new InvalidKeyException("Size of key not known"); - } else if (keySizeBits > Integer.MAX_VALUE) { - throw new InvalidKeyException("Key too large: " + keySizeBits + " bits"); - } - mModulusSizeBytes = (int) ((keySizeBits + 7) / 8); - - setKey(keystoreKey); - } - - /** - * Adjusts the configuration of this cipher for encrypting using the private key. - * - * <p>The default implementation does nothing and refuses to adjust the configuration. - * - * @return {@code true} if the configuration has been adjusted, {@code false} if encrypting - * using private key is not permitted for this cipher. - */ - protected boolean adjustConfigForEncryptingWithPrivateKey() { - return false; - } - - @Override - protected final void resetAll() { - mModulusSizeBytes = -1; - mKeymasterPaddingOverride = -1; - super.resetAll(); - } - - @Override - protected final void resetWhilePreservingInitState() { - super.resetWhilePreservingInitState(); - } - - @Override - protected void addAlgorithmSpecificParametersToBegin( - @NonNull KeymasterArguments keymasterArgs) { - keymasterArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_RSA); - int keymasterPadding = getKeymasterPaddingOverride(); - if (keymasterPadding == -1) { - keymasterPadding = mKeymasterPadding; - } - keymasterArgs.addEnum(KeymasterDefs.KM_TAG_PADDING, keymasterPadding); - int purposeOverride = getKeymasterPurposeOverride(); - if ((purposeOverride != -1) - && ((purposeOverride == KeymasterDefs.KM_PURPOSE_SIGN) - || (purposeOverride == KeymasterDefs.KM_PURPOSE_VERIFY))) { - // Keymaster sign/verify requires digest to be specified. For raw sign/verify it's NONE. - keymasterArgs.addEnum(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_NONE); - } - } - - @Override - protected void loadAlgorithmSpecificParametersFromBeginResult( - @NonNull KeymasterArguments keymasterArgs) { - } - - @Override - protected final int engineGetBlockSize() { - // Not a block cipher, according to the RI - return 0; - } - - @Override - protected final byte[] engineGetIV() { - // IV never used - return null; - } - - @Override - protected final int engineGetOutputSize(int inputLen) { - return getModulusSizeBytes(); - } - - protected final int getModulusSizeBytes() { - if (mModulusSizeBytes == -1) { - throw new IllegalStateException("Not initialized"); - } - return mModulusSizeBytes; - } - - /** - * Overrides the default padding of the crypto operation. - */ - protected final void setKeymasterPaddingOverride(int keymasterPadding) { - mKeymasterPaddingOverride = keymasterPadding; - } - - protected final int getKeymasterPaddingOverride() { - return mKeymasterPaddingOverride; - } -} diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreRSAPrivateKey.java b/keystore/java/android/security/keystore/AndroidKeyStoreRSAPrivateKey.java deleted file mode 100644 index adb39222e076..000000000000 --- a/keystore/java/android/security/keystore/AndroidKeyStoreRSAPrivateKey.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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.keystore; - -import java.math.BigInteger; -import java.security.PrivateKey; -import java.security.interfaces.RSAKey; - -/** - * RSA private key (instance of {@link PrivateKey} and {@link RSAKey}) backed by keystore. - * - * @hide - */ -public class AndroidKeyStoreRSAPrivateKey extends AndroidKeyStorePrivateKey implements RSAKey { - - private final BigInteger mModulus; - - public AndroidKeyStoreRSAPrivateKey(String alias, int uid, BigInteger modulus) { - super(alias, uid, KeyProperties.KEY_ALGORITHM_RSA); - mModulus = modulus; - } - - @Override - public BigInteger getModulus() { - return mModulus; - } -} diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreRSAPublicKey.java b/keystore/java/android/security/keystore/AndroidKeyStoreRSAPublicKey.java deleted file mode 100644 index d85aaceb98e3..000000000000 --- a/keystore/java/android/security/keystore/AndroidKeyStoreRSAPublicKey.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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.keystore; - -import java.math.BigInteger; -import java.security.interfaces.RSAPublicKey; - -/** - * {@link RSAPublicKey} backed by Android Keystore. - * - * @hide - */ -public class AndroidKeyStoreRSAPublicKey extends AndroidKeyStorePublicKey implements RSAPublicKey { - private final BigInteger mModulus; - private final BigInteger mPublicExponent; - - public AndroidKeyStoreRSAPublicKey(String alias, int uid, byte[] x509EncodedForm, BigInteger modulus, - BigInteger publicExponent) { - super(alias, uid, KeyProperties.KEY_ALGORITHM_RSA, x509EncodedForm); - mModulus = modulus; - mPublicExponent = publicExponent; - } - - public AndroidKeyStoreRSAPublicKey(String alias, int uid, RSAPublicKey info) { - this(alias, uid, info.getEncoded(), info.getModulus(), info.getPublicExponent()); - if (!"X.509".equalsIgnoreCase(info.getFormat())) { - throw new IllegalArgumentException( - "Unsupported key export format: " + info.getFormat()); - } - } - - @Override - public BigInteger getModulus() { - return mModulus; - } - - @Override - public BigInteger getPublicExponent() { - return mPublicExponent; - } -} diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreRSASignatureSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreRSASignatureSpi.java deleted file mode 100644 index ecfc97ed7141..000000000000 --- a/keystore/java/android/security/keystore/AndroidKeyStoreRSASignatureSpi.java +++ /dev/null @@ -1,164 +0,0 @@ -/* - * 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.keystore; - -import android.annotation.NonNull; -import android.security.keymaster.KeymasterArguments; -import android.security.keymaster.KeymasterDefs; - -import java.security.InvalidKeyException; -import java.security.SignatureSpi; - -/** - * Base class for {@link SignatureSpi} providing Android KeyStore backed RSA signatures. - * - * @hide - */ -abstract class AndroidKeyStoreRSASignatureSpi extends AndroidKeyStoreSignatureSpiBase { - - abstract static class PKCS1Padding extends AndroidKeyStoreRSASignatureSpi { - PKCS1Padding(int keymasterDigest) { - super(keymasterDigest, KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN); - } - - @Override - protected final int getAdditionalEntropyAmountForSign() { - // No entropy required for this deterministic signature scheme. - return 0; - } - } - - public static final class NONEWithPKCS1Padding extends PKCS1Padding { - public NONEWithPKCS1Padding() { - super(KeymasterDefs.KM_DIGEST_NONE); - } - } - - public static final class MD5WithPKCS1Padding extends PKCS1Padding { - public MD5WithPKCS1Padding() { - super(KeymasterDefs.KM_DIGEST_MD5); - } - } - - public static final class SHA1WithPKCS1Padding extends PKCS1Padding { - public SHA1WithPKCS1Padding() { - super(KeymasterDefs.KM_DIGEST_SHA1); - } - } - - public static final class SHA224WithPKCS1Padding extends PKCS1Padding { - public SHA224WithPKCS1Padding() { - super(KeymasterDefs.KM_DIGEST_SHA_2_224); - } - } - - public static final class SHA256WithPKCS1Padding extends PKCS1Padding { - public SHA256WithPKCS1Padding() { - super(KeymasterDefs.KM_DIGEST_SHA_2_256); - } - } - - public static final class SHA384WithPKCS1Padding extends PKCS1Padding { - public SHA384WithPKCS1Padding() { - super(KeymasterDefs.KM_DIGEST_SHA_2_384); - } - } - - public static final class SHA512WithPKCS1Padding extends PKCS1Padding { - public SHA512WithPKCS1Padding() { - super(KeymasterDefs.KM_DIGEST_SHA_2_512); - } - } - - abstract static class PSSPadding extends AndroidKeyStoreRSASignatureSpi { - private static final int SALT_LENGTH_BYTES = 20; - - PSSPadding(int keymasterDigest) { - super(keymasterDigest, KeymasterDefs.KM_PAD_RSA_PSS); - } - - @Override - protected final int getAdditionalEntropyAmountForSign() { - return SALT_LENGTH_BYTES; - } - } - - public static final class SHA1WithPSSPadding extends PSSPadding { - public SHA1WithPSSPadding() { - super(KeymasterDefs.KM_DIGEST_SHA1); - } - } - - public static final class SHA224WithPSSPadding extends PSSPadding { - public SHA224WithPSSPadding() { - super(KeymasterDefs.KM_DIGEST_SHA_2_224); - } - } - - public static final class SHA256WithPSSPadding extends PSSPadding { - public SHA256WithPSSPadding() { - super(KeymasterDefs.KM_DIGEST_SHA_2_256); - } - } - - public static final class SHA384WithPSSPadding extends PSSPadding { - public SHA384WithPSSPadding() { - super(KeymasterDefs.KM_DIGEST_SHA_2_384); - } - } - - public static final class SHA512WithPSSPadding extends PSSPadding { - public SHA512WithPSSPadding() { - super(KeymasterDefs.KM_DIGEST_SHA_2_512); - } - } - - private final int mKeymasterDigest; - private final int mKeymasterPadding; - - AndroidKeyStoreRSASignatureSpi(int keymasterDigest, int keymasterPadding) { - mKeymasterDigest = keymasterDigest; - mKeymasterPadding = keymasterPadding; - } - - @Override - protected final void initKey(AndroidKeyStoreKey key) throws InvalidKeyException { - if (!KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(key.getAlgorithm())) { - throw new InvalidKeyException("Unsupported key algorithm: " + key.getAlgorithm() - + ". Only" + KeyProperties.KEY_ALGORITHM_RSA + " supported"); - } - super.initKey(key); - } - - @Override - protected final void resetAll() { - super.resetAll(); - } - - @Override - protected final void resetWhilePreservingInitState() { - super.resetWhilePreservingInitState(); - } - - @Override - protected final void addAlgorithmSpecificParametersToBegin( - @NonNull KeymasterArguments keymasterArgs) { - keymasterArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_RSA); - keymasterArgs.addEnum(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest); - keymasterArgs.addEnum(KeymasterDefs.KM_TAG_PADDING, mKeymasterPadding); - } -} diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKey.java b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKey.java deleted file mode 100644 index b8e6af7d936e..000000000000 --- a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKey.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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.keystore; - -import javax.crypto.SecretKey; - -/** - * {@link SecretKey} backed by Android Keystore. - * - * @hide - */ -public class AndroidKeyStoreSecretKey extends AndroidKeyStoreKey implements SecretKey { - - public AndroidKeyStoreSecretKey(String alias, int uid, String algorithm) { - super(alias, uid, algorithm); - } -} diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java deleted file mode 100644 index d2678c71813c..000000000000 --- a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java +++ /dev/null @@ -1,248 +0,0 @@ -/* - * 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.keystore; - -import android.security.Credentials; -import android.security.GateKeeper; -import android.security.KeyStore; -import android.security.keymaster.KeyCharacteristics; -import android.security.keymaster.KeymasterDefs; - -import java.math.BigInteger; -import java.security.InvalidKeyException; -import java.security.ProviderException; -import java.security.spec.InvalidKeySpecException; -import java.security.spec.KeySpec; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -import javax.crypto.SecretKey; -import javax.crypto.SecretKeyFactorySpi; -import javax.crypto.spec.SecretKeySpec; - -/** - * {@link SecretKeyFactorySpi} backed by Android Keystore. - * - * @hide - */ -public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi { - - private final KeyStore mKeyStore = KeyStore.getInstance(); - - @Override - protected KeySpec engineGetKeySpec(SecretKey key, - @SuppressWarnings("rawtypes") Class keySpecClass) throws InvalidKeySpecException { - if (keySpecClass == null) { - throw new InvalidKeySpecException("keySpecClass == null"); - } - if (!(key instanceof AndroidKeyStoreSecretKey)) { - throw new InvalidKeySpecException("Only Android KeyStore secret keys supported: " + - ((key != null) ? key.getClass().getName() : "null")); - } - if (SecretKeySpec.class.isAssignableFrom(keySpecClass)) { - throw new InvalidKeySpecException( - "Key material export of Android KeyStore keys is not supported"); - } - if (!KeyInfo.class.equals(keySpecClass)) { - throw new InvalidKeySpecException("Unsupported key spec: " + keySpecClass.getName()); - } - AndroidKeyStoreKey keystoreKey = (AndroidKeyStoreKey) key; - String keyAliasInKeystore = keystoreKey.getAlias(); - String entryAlias; - if (keyAliasInKeystore.startsWith(Credentials.USER_PRIVATE_KEY)) { - entryAlias = keyAliasInKeystore.substring(Credentials.USER_PRIVATE_KEY.length()); - } else if (keyAliasInKeystore.startsWith(Credentials.USER_SECRET_KEY)){ - // key has legacy prefix - entryAlias = keyAliasInKeystore.substring(Credentials.USER_SECRET_KEY.length()); - } else { - throw new InvalidKeySpecException("Invalid key alias: " + keyAliasInKeystore); - } - - return getKeyInfo(mKeyStore, entryAlias, keyAliasInKeystore, keystoreKey.getUid()); - } - - static KeyInfo getKeyInfo(KeyStore keyStore, String entryAlias, String keyAliasInKeystore, - int keyUid) { - KeyCharacteristics keyCharacteristics = new KeyCharacteristics(); - int errorCode = keyStore.getKeyCharacteristics( - keyAliasInKeystore, null, null, keyUid, keyCharacteristics); - if (errorCode != KeyStore.NO_ERROR) { - throw new ProviderException("Failed to obtain information about key." - + " Keystore error: " + errorCode); - } - - boolean insideSecureHardware; - @KeyProperties.OriginEnum int origin; - int keySize; - @KeyProperties.PurposeEnum int purposes; - String[] encryptionPaddings; - String[] signaturePaddings; - @KeyProperties.DigestEnum String[] digests; - @KeyProperties.BlockModeEnum String[] blockModes; - int keymasterSwEnforcedUserAuthenticators; - int keymasterHwEnforcedUserAuthenticators; - List<BigInteger> keymasterSecureUserIds; - try { - if (keyCharacteristics.hwEnforced.containsTag(KeymasterDefs.KM_TAG_ORIGIN)) { - insideSecureHardware = true; - origin = KeyProperties.Origin.fromKeymaster( - keyCharacteristics.hwEnforced.getEnum(KeymasterDefs.KM_TAG_ORIGIN, -1)); - } else if (keyCharacteristics.swEnforced.containsTag(KeymasterDefs.KM_TAG_ORIGIN)) { - insideSecureHardware = false; - origin = KeyProperties.Origin.fromKeymaster( - keyCharacteristics.swEnforced.getEnum(KeymasterDefs.KM_TAG_ORIGIN, -1)); - } else { - throw new ProviderException("Key origin not available"); - } - long keySizeUnsigned = - keyCharacteristics.getUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, -1); - if (keySizeUnsigned == -1) { - throw new ProviderException("Key size not available"); - } else if (keySizeUnsigned > Integer.MAX_VALUE) { - throw new ProviderException("Key too large: " + keySizeUnsigned + " bits"); - } - keySize = (int) keySizeUnsigned; - purposes = KeyProperties.Purpose.allFromKeymaster( - keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_PURPOSE)); - - List<String> encryptionPaddingsList = new ArrayList<String>(); - List<String> signaturePaddingsList = new ArrayList<String>(); - // Keymaster stores both types of paddings in the same array -- we split it into two. - for (int keymasterPadding : keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_PADDING)) { - try { - @KeyProperties.EncryptionPaddingEnum String jcaPadding = - KeyProperties.EncryptionPadding.fromKeymaster(keymasterPadding); - encryptionPaddingsList.add(jcaPadding); - } catch (IllegalArgumentException e) { - try { - @KeyProperties.SignaturePaddingEnum String padding = - KeyProperties.SignaturePadding.fromKeymaster(keymasterPadding); - signaturePaddingsList.add(padding); - } catch (IllegalArgumentException e2) { - throw new ProviderException( - "Unsupported encryption padding: " + keymasterPadding); - } - } - - } - encryptionPaddings = - encryptionPaddingsList.toArray(new String[encryptionPaddingsList.size()]); - signaturePaddings = - signaturePaddingsList.toArray(new String[signaturePaddingsList.size()]); - - digests = KeyProperties.Digest.allFromKeymaster( - keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_DIGEST)); - blockModes = KeyProperties.BlockMode.allFromKeymaster( - keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_BLOCK_MODE)); - keymasterSwEnforcedUserAuthenticators = - keyCharacteristics.swEnforced.getEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0); - keymasterHwEnforcedUserAuthenticators = - keyCharacteristics.hwEnforced.getEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0); - keymasterSecureUserIds = - keyCharacteristics.getUnsignedLongs(KeymasterDefs.KM_TAG_USER_SECURE_ID); - } catch (IllegalArgumentException e) { - throw new ProviderException("Unsupported key characteristic", e); - } - - Date keyValidityStart = keyCharacteristics.getDate(KeymasterDefs.KM_TAG_ACTIVE_DATETIME); - Date keyValidityForOriginationEnd = - keyCharacteristics.getDate(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME); - Date keyValidityForConsumptionEnd = - keyCharacteristics.getDate(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME); - boolean userAuthenticationRequired = - !keyCharacteristics.getBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED); - long userAuthenticationValidityDurationSeconds = - keyCharacteristics.getUnsignedInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT, 0); - if (userAuthenticationValidityDurationSeconds > Integer.MAX_VALUE) { - throw new ProviderException("User authentication timeout validity too long: " - + userAuthenticationValidityDurationSeconds + " seconds"); - } - boolean userAuthenticationRequirementEnforcedBySecureHardware = (userAuthenticationRequired) - && (keymasterHwEnforcedUserAuthenticators != 0) - && (keymasterSwEnforcedUserAuthenticators == 0); - boolean userAuthenticationValidWhileOnBody = - keyCharacteristics.hwEnforced.getBoolean(KeymasterDefs.KM_TAG_ALLOW_WHILE_ON_BODY); - boolean trustedUserPresenceRequired = - keyCharacteristics.hwEnforced.getBoolean( - KeymasterDefs.KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED); - - boolean invalidatedByBiometricEnrollment = false; - if (keymasterSwEnforcedUserAuthenticators == KeymasterDefs.HW_AUTH_BIOMETRIC - || keymasterHwEnforcedUserAuthenticators == KeymasterDefs.HW_AUTH_BIOMETRIC) { - // Fingerprint-only key; will be invalidated if the root SID isn't in the SID list. - invalidatedByBiometricEnrollment = keymasterSecureUserIds != null - && !keymasterSecureUserIds.isEmpty() - && !keymasterSecureUserIds.contains(getGateKeeperSecureUserId()); - } - - boolean userConfirmationRequired = keyCharacteristics.hwEnforced.getBoolean(KeymasterDefs.KM_TAG_TRUSTED_CONFIRMATION_REQUIRED); - - return new KeyInfo(entryAlias, - insideSecureHardware, - origin, - keySize, - keyValidityStart, - keyValidityForOriginationEnd, - keyValidityForConsumptionEnd, - purposes, - encryptionPaddings, - signaturePaddings, - digests, - blockModes, - userAuthenticationRequired, - (int) userAuthenticationValidityDurationSeconds, - keymasterHwEnforcedUserAuthenticators, - userAuthenticationRequirementEnforcedBySecureHardware, - userAuthenticationValidWhileOnBody, - trustedUserPresenceRequired, - invalidatedByBiometricEnrollment, - userConfirmationRequired, - // Keystore 1.0 does not tell us the exact security level of the key - // so we report an unknown but secure security level. - insideSecureHardware ? KeyProperties.SECURITY_LEVEL_UNKNOWN_SECURE - : KeyProperties.SECURITY_LEVEL_SOFTWARE, - KeyProperties.UNRESTRICTED_USAGE_COUNT); - } - - private static BigInteger getGateKeeperSecureUserId() throws ProviderException { - try { - return BigInteger.valueOf(GateKeeper.getSecureUserId()); - } catch (IllegalStateException e) { - throw new ProviderException("Failed to get GateKeeper secure user ID", e); - } - } - - @Override - protected SecretKey engineGenerateSecret(KeySpec keySpec) throws InvalidKeySpecException { - throw new InvalidKeySpecException( - "To generate secret key in Android Keystore, use KeyGenerator initialized with " - + KeyGenParameterSpec.class.getName()); - } - - @Override - protected SecretKey engineTranslateKey(SecretKey key) throws InvalidKeyException { - if (key == null) { - throw new InvalidKeyException("key == null"); - } else if (!(key instanceof AndroidKeyStoreSecretKey)) { - throw new InvalidKeyException( - "To import a secret key into Android Keystore, use KeyStore.setEntry"); - } - - return key; - } -} diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSignatureSpiBase.java b/keystore/java/android/security/keystore/AndroidKeyStoreSignatureSpiBase.java deleted file mode 100644 index da47b6bde69a..000000000000 --- a/keystore/java/android/security/keystore/AndroidKeyStoreSignatureSpiBase.java +++ /dev/null @@ -1,431 +0,0 @@ -/* - * 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.keystore; - -import android.annotation.CallSuper; -import android.annotation.NonNull; -import android.os.IBinder; -import android.security.KeyStore; -import android.security.KeyStoreException; -import android.security.keymaster.KeymasterArguments; -import android.security.keymaster.KeymasterDefs; -import android.security.keymaster.OperationResult; - -import libcore.util.EmptyArray; - -import java.nio.ByteBuffer; -import java.security.InvalidKeyException; -import java.security.InvalidParameterException; -import java.security.PrivateKey; -import java.security.ProviderException; -import java.security.PublicKey; -import java.security.SecureRandom; -import java.security.SignatureException; -import java.security.SignatureSpi; - -/** - * Base class for {@link SignatureSpi} implementations of Android KeyStore backed ciphers. - * - * @hide - */ -abstract class AndroidKeyStoreSignatureSpiBase extends SignatureSpi - implements KeyStoreCryptoOperation { - private final KeyStore mKeyStore; - - // Fields below are populated by SignatureSpi.engineInitSign/engineInitVerify and KeyStore.begin - // and should be preserved after SignatureSpi.engineSign/engineVerify finishes. - private boolean mSigning; - private AndroidKeyStoreKey mKey; - - /** - * Token referencing this operation inside keystore service. It is initialized by - * {@code engineInitSign}/{@code engineInitVerify} and is invalidated when - * {@code engineSign}/{@code engineVerify} succeeds and on some error conditions in between. - */ - private IBinder mOperationToken; - private long mOperationHandle; - private KeyStoreCryptoOperationStreamer mMessageStreamer; - - /** - * 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 engineSign} or {@code engineVerify}. Once such an exception is encountered, - * {@code engineUpdate} starts ignoring input data. - */ - private Exception mCachedException; - - AndroidKeyStoreSignatureSpiBase() { - mKeyStore = KeyStore.getInstance(); - } - - @Override - protected final void engineInitSign(PrivateKey key) throws InvalidKeyException { - engineInitSign(key, null); - } - - @Override - protected final void engineInitSign(PrivateKey privateKey, SecureRandom random) - throws InvalidKeyException { - resetAll(); - - boolean success = false; - try { - if (privateKey == null) { - throw new InvalidKeyException("Unsupported key: null"); - } - AndroidKeyStoreKey keystoreKey; - if (privateKey instanceof AndroidKeyStorePrivateKey) { - keystoreKey = (AndroidKeyStoreKey) privateKey; - } else { - throw new InvalidKeyException("Unsupported private key type: " + privateKey); - } - mSigning = true; - initKey(keystoreKey); - appRandom = random; - ensureKeystoreOperationInitialized(); - success = true; - } finally { - if (!success) { - resetAll(); - } - } - } - - @Override - protected final void engineInitVerify(PublicKey publicKey) throws InvalidKeyException { - resetAll(); - - boolean success = false; - try { - if (publicKey == null) { - throw new InvalidKeyException("Unsupported key: null"); - } - AndroidKeyStoreKey keystoreKey; - if (publicKey instanceof AndroidKeyStorePublicKey) { - keystoreKey = (AndroidKeyStorePublicKey) publicKey; - } else { - throw new InvalidKeyException("Unsupported public key type: " + publicKey); - } - mSigning = false; - initKey(keystoreKey); - appRandom = null; - ensureKeystoreOperationInitialized(); - success = true; - } finally { - if (!success) { - resetAll(); - } - } - } - - /** - * Configures this signature instance to use the provided key. - * - * @throws InvalidKeyException if the {@code key} is not suitable. - */ - @CallSuper - protected void initKey(AndroidKeyStoreKey key) throws InvalidKeyException { - mKey = key; - } - - /** - * Resets this cipher to its pristine pre-init state. This must be equivalent to obtaining a new - * cipher instance. - * - * <p>Subclasses storing additional state should override this method, reset the additional - * state, and then chain to superclass. - */ - @CallSuper - protected void resetAll() { - IBinder operationToken = mOperationToken; - if (operationToken != null) { - mOperationToken = null; - mKeyStore.abort(operationToken); - } - mSigning = false; - mKey = null; - appRandom = null; - mOperationToken = null; - mOperationHandle = 0; - mMessageStreamer = null; - mCachedException = null; - } - - /** - * Resets this cipher while preserving the initialized state. This must be equivalent to - * rolling back the cipher's state to just after the most recent {@code engineInit} completed - * successfully. - * - * <p>Subclasses storing additional post-init state should override this method, reset the - * additional state, and then chain to superclass. - */ - @CallSuper - protected void resetWhilePreservingInitState() { - IBinder operationToken = mOperationToken; - if (operationToken != null) { - mOperationToken = null; - mKeyStore.abort(operationToken); - } - mOperationHandle = 0; - mMessageStreamer = null; - mCachedException = null; - } - - private void ensureKeystoreOperationInitialized() throws InvalidKeyException { - if (mMessageStreamer != null) { - return; - } - if (mCachedException != null) { - return; - } - if (mKey == null) { - throw new IllegalStateException("Not initialized"); - } - - KeymasterArguments keymasterInputArgs = new KeymasterArguments(); - addAlgorithmSpecificParametersToBegin(keymasterInputArgs); - - OperationResult opResult = mKeyStore.begin( - mKey.getAlias(), - mSigning ? KeymasterDefs.KM_PURPOSE_SIGN : KeymasterDefs.KM_PURPOSE_VERIFY, - true, // permit aborting this operation if keystore runs out of resources - keymasterInputArgs, - null, // no additional entropy for begin -- only finish might need some - mKey.getUid()); - if (opResult == null) { - throw new KeyStoreConnectException(); - } - - // Store operation token and handle regardless of the error code returned by KeyStore to - // ensure that the operation gets aborted immediately if the code below throws an exception. - mOperationToken = opResult.token; - mOperationHandle = opResult.operationHandle; - - // If necessary, throw an exception due to KeyStore operation having failed. - InvalidKeyException e = KeyStoreCryptoOperationUtils.getInvalidKeyExceptionForInit( - mKeyStore, mKey, opResult.resultCode); - if (e != null) { - throw e; - } - - if (mOperationToken == null) { - throw new ProviderException("Keystore returned null operation token"); - } - if (mOperationHandle == 0) { - throw new ProviderException("Keystore returned invalid operation handle"); - } - - mMessageStreamer = createMainDataStreamer(mKeyStore, opResult.token); - } - - /** - * Creates a streamer which sends the message to be signed/verified into the provided KeyStore - * - * <p>This implementation returns a working streamer. - */ - @NonNull - protected KeyStoreCryptoOperationStreamer createMainDataStreamer( - KeyStore keyStore, IBinder operationToken) { - return new KeyStoreCryptoOperationChunkedStreamer( - new KeyStoreCryptoOperationChunkedStreamer.MainDataStream( - keyStore, operationToken)); - } - - @Override - public final long getOperationHandle() { - return mOperationHandle; - } - - @Override - protected final void engineUpdate(byte[] b, int off, int len) throws SignatureException { - if (mCachedException != null) { - throw new SignatureException(mCachedException); - } - - try { - ensureKeystoreOperationInitialized(); - } catch (InvalidKeyException e) { - throw new SignatureException(e); - } - - if (len == 0) { - return; - } - - byte[] output; - try { - output = mMessageStreamer.update(b, off, len); - } catch (KeyStoreException e) { - throw new SignatureException(e); - } - - if (output.length != 0) { - throw new ProviderException( - "Update operation unexpectedly produced output: " + output.length + " bytes"); - } - } - - @Override - protected final void engineUpdate(byte b) throws SignatureException { - engineUpdate(new byte[] {b}, 0, 1); - } - - @Override - protected final void engineUpdate(ByteBuffer input) { - byte[] b; - int off; - int len = input.remaining(); - if (input.hasArray()) { - b = input.array(); - off = input.arrayOffset() + input.position(); - input.position(input.limit()); - } else { - b = new byte[len]; - off = 0; - input.get(b); - } - - try { - engineUpdate(b, off, len); - } catch (SignatureException e) { - mCachedException = e; - } - } - - @Override - protected final int engineSign(byte[] out, int outOffset, int outLen) - throws SignatureException { - return super.engineSign(out, outOffset, outLen); - } - - @Override - protected final byte[] engineSign() throws SignatureException { - if (mCachedException != null) { - throw new SignatureException(mCachedException); - } - - byte[] signature; - try { - ensureKeystoreOperationInitialized(); - - byte[] additionalEntropy = - KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng( - appRandom, getAdditionalEntropyAmountForSign()); - signature = mMessageStreamer.doFinal( - EmptyArray.BYTE, 0, 0, - null, // no signature provided -- it'll be generated by this invocation - additionalEntropy); - } catch (InvalidKeyException | KeyStoreException e) { - throw new SignatureException(e); - } - - resetWhilePreservingInitState(); - return signature; - } - - @Override - protected final boolean engineVerify(byte[] signature) throws SignatureException { - if (mCachedException != null) { - throw new SignatureException(mCachedException); - } - - try { - ensureKeystoreOperationInitialized(); - } catch (InvalidKeyException e) { - throw new SignatureException(e); - } - - boolean verified; - try { - byte[] output = mMessageStreamer.doFinal( - EmptyArray.BYTE, 0, 0, - signature, - null // no additional entropy needed -- verification is deterministic - ); - if (output.length != 0) { - throw new ProviderException( - "Signature verification unexpected produced output: " + output.length - + " bytes"); - } - verified = true; - } catch (KeyStoreException e) { - switch (e.getErrorCode()) { - case KeymasterDefs.KM_ERROR_VERIFICATION_FAILED: - verified = false; - break; - default: - throw new SignatureException(e); - } - } - - resetWhilePreservingInitState(); - return verified; - } - - @Override - protected final boolean engineVerify(byte[] sigBytes, int offset, int len) - throws SignatureException { - return engineVerify(ArrayUtils.subarray(sigBytes, offset, len)); - } - - @Deprecated - @Override - protected final Object engineGetParameter(String param) throws InvalidParameterException { - throw new InvalidParameterException(); - } - - @Deprecated - @Override - protected final void engineSetParameter(String param, Object value) - throws InvalidParameterException { - throw new InvalidParameterException(); - } - - protected final KeyStore getKeyStore() { - return mKeyStore; - } - - /** - * Returns {@code true} if this signature is initialized for signing, {@code false} if this - * signature is initialized for verification. - */ - protected final boolean isSigning() { - return mSigning; - } - - // The methods below need to be implemented by subclasses. - - /** - * Returns the amount of additional entropy (in bytes) to be provided to the KeyStore's - * {@code finish} operation when generating a signature. - * - * <p>This value should match (or exceed) the amount of Shannon entropy of the produced - * signature assuming the key and the message are known. For example, for ECDSA signature this - * should be the size of {@code R}, whereas for the RSA signature with PKCS#1 padding this - * should be {@code 0}. - */ - protected abstract int getAdditionalEntropyAmountForSign(); - - /** - * Invoked to add algorithm-specific parameters for the KeyStore's {@code begin} operation. - * - * @param keymasterArgs keystore/keymaster arguments to be populated with algorithm-specific - * parameters. - */ - protected abstract void addAlgorithmSpecificParametersToBegin( - @NonNull KeymasterArguments keymasterArgs); -} diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java deleted file mode 100644 index 51c42520ccc9..000000000000 --- a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java +++ /dev/null @@ -1,1112 +0,0 @@ -/* - * Copyright (C) 2012 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.keystore; - -import android.security.Credentials; -import android.security.GateKeeper; -import android.security.KeyStore; -import android.security.KeyStoreParameter; -import android.security.keymaster.KeyCharacteristics; -import android.security.keymaster.KeymasterArguments; -import android.security.keymaster.KeymasterDefs; -import android.security.keystore.KeyPermanentlyInvalidatedException; -import android.security.keystore.KeyProperties; -import android.security.keystore.KeyProtection; -import android.security.keystore.SecureKeyImportUnavailableException; -import android.security.keystore.WrappedKeyEntry; -import android.util.Log; - -import libcore.util.EmptyArray; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.security.Key; -import java.security.KeyStore.Entry; -import java.security.KeyStore.LoadStoreParameter; -import java.security.KeyStore.PrivateKeyEntry; -import java.security.KeyStore.ProtectionParameter; -import java.security.KeyStore.SecretKeyEntry; -import java.security.KeyStoreException; -import java.security.KeyStoreSpi; -import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.ProviderException; -import java.security.PublicKey; -import java.security.UnrecoverableKeyException; -import java.security.cert.Certificate; -import java.security.cert.CertificateEncodingException; -import java.security.cert.CertificateException; -import java.security.cert.CertificateFactory; -import java.security.cert.X509Certificate; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.Enumeration; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Set; - -import javax.crypto.SecretKey; - -/** - * A java.security.KeyStore interface for the Android KeyStore. An instance of - * it can be created via the {@link java.security.KeyStore#getInstance(String) - * KeyStore.getInstance("AndroidKeyStore")} interface. This returns a - * java.security.KeyStore backed by this "AndroidKeyStore" implementation. - * <p> - * This is built on top of Android's keystore daemon. The convention of alias - * use is: - * <p> - * PrivateKeyEntry will have a Credentials.USER_PRIVATE_KEY as the private key, - * Credentials.USER_CERTIFICATE as the first certificate in the chain (the one - * that corresponds to the private key), and then a Credentials.CA_CERTIFICATE - * entry which will have the rest of the chain concatenated in BER format. - * <p> - * TrustedCertificateEntry will just have a Credentials.CA_CERTIFICATE entry - * with a single certificate. - * - * @hide - */ -public class AndroidKeyStoreSpi extends KeyStoreSpi { - public static final String NAME = "AndroidKeyStore"; - - private KeyStore mKeyStore; - private int mUid = KeyStore.UID_SELF; - - @Override - public Key engineGetKey(String alias, char[] password) throws NoSuchAlgorithmException, - UnrecoverableKeyException { - String userKeyAlias = Credentials.USER_PRIVATE_KEY + alias; - AndroidKeyStoreKey key; - if (!mKeyStore.contains(userKeyAlias, mUid)) { - // try legacy prefix for backward compatibility - userKeyAlias = Credentials.USER_SECRET_KEY + alias; - if (!mKeyStore.contains(userKeyAlias, mUid)) return null; - } - try { - key = AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore(mKeyStore, - userKeyAlias, - mUid); - } catch (KeyPermanentlyInvalidatedException e) { - throw new UnrecoverableKeyException(e.getMessage()); - } - return key; - } - - @Override - public Certificate[] engineGetCertificateChain(String alias) { - if (alias == null) { - throw new NullPointerException("alias == null"); - } - - final X509Certificate leaf = (X509Certificate) engineGetCertificate(alias); - if (leaf == null) { - return null; - } - - final Certificate[] caList; - - // Suppress the key not found warning for this call. It seems that this error is exclusively - // being thrown when there is a self signed certificate chain, so when the keystore service - // attempts to query for the CA details, it obviously fails to find them and returns a - // key not found exception. This is WAI, and throwing a stack trace here can be very - // misleading since the trace is not clear. - final byte[] caBytes = mKeyStore.get(Credentials.CA_CERTIFICATE + alias, - mUid, - true /* suppressKeyNotFoundWarning */); - if (caBytes != null) { - final Collection<X509Certificate> caChain = toCertificates(caBytes); - - caList = new Certificate[caChain.size() + 1]; - - final Iterator<X509Certificate> it = caChain.iterator(); - int i = 1; - while (it.hasNext()) { - caList[i++] = it.next(); - } - } else { - caList = new Certificate[1]; - } - - caList[0] = leaf; - - return caList; - } - - @Override - public Certificate engineGetCertificate(String alias) { - if (alias == null) { - throw new NullPointerException("alias == null"); - } - - byte[] encodedCert = mKeyStore.get(Credentials.USER_CERTIFICATE + alias, mUid); - if (encodedCert != null) { - return getCertificateForPrivateKeyEntry(alias, encodedCert); - } - - encodedCert = mKeyStore.get(Credentials.CA_CERTIFICATE + alias, mUid); - if (encodedCert != null) { - return getCertificateForTrustedCertificateEntry(encodedCert); - } - - // This entry/alias does not contain a certificate. - return null; - } - - private Certificate getCertificateForTrustedCertificateEntry(byte[] encodedCert) { - // For this certificate there shouldn't be a private key in this KeyStore entry. Thus, - // there's no need to wrap this certificate as opposed to the certificate associated with - // a private key entry. - return toCertificate(encodedCert); - } - - private Certificate getCertificateForPrivateKeyEntry(String alias, byte[] encodedCert) { - // All crypto algorithms offered by Android Keystore for its private keys must also - // be offered for the corresponding public keys stored in the Android Keystore. The - // complication is that the underlying keystore service operates only on full key pairs, - // rather than just public keys or private keys. As a result, Android Keystore-backed - // crypto can only be offered for public keys for which keystore contains the - // corresponding private key. This is not the case for certificate-only entries (e.g., - // trusted certificates). - // - // getCertificate().getPublicKey() is the only way to obtain the public key - // corresponding to the private key stored in the KeyStore. Thus, we need to make sure - // that the returned public key points to the underlying key pair / private key - // when available. - - X509Certificate cert = toCertificate(encodedCert); - if (cert == null) { - // Failed to parse the certificate. - return null; - } - - String privateKeyAlias = Credentials.USER_PRIVATE_KEY + alias; - if (mKeyStore.contains(privateKeyAlias, mUid)) { - // As expected, keystore contains the private key corresponding to this public key. Wrap - // the certificate so that its getPublicKey method returns an Android Keystore - // PublicKey. This key will delegate crypto operations involving this public key to - // Android Keystore when higher-priority providers do not offer these crypto - // operations for this key. - return wrapIntoKeyStoreCertificate(privateKeyAlias, mUid, cert); - } else { - // This KeyStore entry/alias is supposed to contain the private key corresponding to - // the public key in this certificate, but it does not for some reason. It's probably a - // bug. Let other providers handle crypto operations involving the public key returned - // by this certificate's getPublicKey. - return cert; - } - } - - /** - * Wraps the provided cerificate into {@link KeyStoreX509Certificate} so that the public key - * returned by the certificate contains information about the alias of the private key in - * keystore. This is needed so that Android Keystore crypto operations using public keys can - * find out which key alias to use. These operations cannot work without an alias. - */ - private static KeyStoreX509Certificate wrapIntoKeyStoreCertificate( - String privateKeyAlias, int uid, X509Certificate certificate) { - return (certificate != null) - ? new KeyStoreX509Certificate(privateKeyAlias, uid, certificate) : null; - } - - private static X509Certificate toCertificate(byte[] bytes) { - try { - final CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); - return (X509Certificate) certFactory.generateCertificate( - new ByteArrayInputStream(bytes)); - } catch (CertificateException e) { - Log.w(NAME, "Couldn't parse certificate in keystore", e); - return null; - } - } - - @SuppressWarnings("unchecked") - private static Collection<X509Certificate> toCertificates(byte[] bytes) { - try { - final CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); - return (Collection<X509Certificate>) certFactory.generateCertificates( - new ByteArrayInputStream(bytes)); - } catch (CertificateException e) { - Log.w(NAME, "Couldn't parse certificates in keystore", e); - return new ArrayList<X509Certificate>(); - } - } - - private Date getModificationDate(String alias) { - final long epochMillis = mKeyStore.getmtime(alias, mUid); - if (epochMillis == -1L) { - return null; - } - - return new Date(epochMillis); - } - - @Override - public Date engineGetCreationDate(String alias) { - if (alias == null) { - throw new NullPointerException("alias == null"); - } - - Date d = getModificationDate(Credentials.USER_PRIVATE_KEY + alias); - if (d != null) { - return d; - } - - d = getModificationDate(Credentials.USER_SECRET_KEY + alias); - if (d != null) { - return d; - } - - d = getModificationDate(Credentials.USER_CERTIFICATE + alias); - if (d != null) { - return d; - } - - return getModificationDate(Credentials.CA_CERTIFICATE + alias); - } - - @Override - public void engineSetKeyEntry(String alias, Key key, char[] password, Certificate[] chain) - throws KeyStoreException { - if ((password != null) && (password.length > 0)) { - throw new KeyStoreException("entries cannot be protected with passwords"); - } - - if (key instanceof PrivateKey) { - setPrivateKeyEntry(alias, (PrivateKey) key, chain, null); - } else if (key instanceof SecretKey) { - setSecretKeyEntry(alias, (SecretKey) key, null); - } else { - throw new KeyStoreException("Only PrivateKey and SecretKey are supported"); - } - } - - private static KeyProtection getLegacyKeyProtectionParameter(PrivateKey key) - throws KeyStoreException { - String keyAlgorithm = key.getAlgorithm(); - KeyProtection.Builder specBuilder; - if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyAlgorithm)) { - specBuilder = - new KeyProtection.Builder( - KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY); - // Authorized to be used with any digest (including no digest). - // MD5 was never offered for Android Keystore for ECDSA. - specBuilder.setDigests( - KeyProperties.DIGEST_NONE, - KeyProperties.DIGEST_SHA1, - KeyProperties.DIGEST_SHA224, - KeyProperties.DIGEST_SHA256, - KeyProperties.DIGEST_SHA384, - KeyProperties.DIGEST_SHA512); - } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) { - specBuilder = - new KeyProtection.Builder( - KeyProperties.PURPOSE_ENCRYPT - | KeyProperties.PURPOSE_DECRYPT - | KeyProperties.PURPOSE_SIGN - | KeyProperties.PURPOSE_VERIFY); - // Authorized to be used with any digest (including no digest). - specBuilder.setDigests( - KeyProperties.DIGEST_NONE, - KeyProperties.DIGEST_MD5, - KeyProperties.DIGEST_SHA1, - KeyProperties.DIGEST_SHA224, - KeyProperties.DIGEST_SHA256, - KeyProperties.DIGEST_SHA384, - KeyProperties.DIGEST_SHA512); - // Authorized to be used with any encryption and signature padding - // schemes (including no padding). - specBuilder.setEncryptionPaddings( - KeyProperties.ENCRYPTION_PADDING_NONE, - KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1, - KeyProperties.ENCRYPTION_PADDING_RSA_OAEP); - specBuilder.setSignaturePaddings( - KeyProperties.SIGNATURE_PADDING_RSA_PKCS1, - KeyProperties.SIGNATURE_PADDING_RSA_PSS); - // Disable randomized encryption requirement to support encryption - // padding NONE above. - specBuilder.setRandomizedEncryptionRequired(false); - } else { - throw new KeyStoreException("Unsupported key algorithm: " + keyAlgorithm); - } - specBuilder.setUserAuthenticationRequired(false); - - return specBuilder.build(); - } - - private void setPrivateKeyEntry(String alias, PrivateKey key, Certificate[] chain, - java.security.KeyStore.ProtectionParameter param) throws KeyStoreException { - int flags = 0; - KeyProtection spec; - if (param == null) { - spec = getLegacyKeyProtectionParameter(key); - } else if (param instanceof KeyStoreParameter) { - spec = getLegacyKeyProtectionParameter(key); - KeyStoreParameter legacySpec = (KeyStoreParameter) param; - if (legacySpec.isEncryptionRequired()) { - flags = KeyStore.FLAG_ENCRYPTED; - } - } else if (param instanceof KeyProtection) { - spec = (KeyProtection) param; - if (spec.isCriticalToDeviceEncryption()) { - flags |= KeyStore.FLAG_CRITICAL_TO_DEVICE_ENCRYPTION; - } - - if (spec.isStrongBoxBacked()) { - flags |= KeyStore.FLAG_STRONGBOX; - } - } else { - throw new KeyStoreException( - "Unsupported protection parameter class:" + param.getClass().getName() - + ". Supported: " + KeyProtection.class.getName() + ", " - + KeyStoreParameter.class.getName()); - } - - // Make sure the chain exists since this is a PrivateKey - if ((chain == null) || (chain.length == 0)) { - throw new KeyStoreException("Must supply at least one Certificate with PrivateKey"); - } - - // Do chain type checking. - X509Certificate[] x509chain = new X509Certificate[chain.length]; - for (int i = 0; i < chain.length; i++) { - if (!"X.509".equals(chain[i].getType())) { - throw new KeyStoreException("Certificates must be in X.509 format: invalid cert #" - + i); - } - - if (!(chain[i] instanceof X509Certificate)) { - throw new KeyStoreException("Certificates must be in X.509 format: invalid cert #" - + i); - } - - x509chain[i] = (X509Certificate) chain[i]; - } - - final byte[] userCertBytes; - try { - userCertBytes = x509chain[0].getEncoded(); - } catch (CertificateEncodingException e) { - throw new KeyStoreException("Failed to encode certificate #0", e); - } - - /* - * If we have a chain, store it in the CA certificate slot for this - * alias as concatenated DER-encoded certificates. These can be - * deserialized by {@link CertificateFactory#generateCertificates}. - */ - final byte[] chainBytes; - if (chain.length > 1) { - /* - * The chain is passed in as {user_cert, ca_cert_1, ca_cert_2, ...} - * so we only need the certificates starting at index 1. - */ - final byte[][] certsBytes = new byte[x509chain.length - 1][]; - int totalCertLength = 0; - for (int i = 0; i < certsBytes.length; i++) { - try { - certsBytes[i] = x509chain[i + 1].getEncoded(); - totalCertLength += certsBytes[i].length; - } catch (CertificateEncodingException e) { - throw new KeyStoreException("Failed to encode certificate #" + i, e); - } - } - - /* - * Serialize this into one byte array so we can later call - * CertificateFactory#generateCertificates to recover them. - */ - chainBytes = new byte[totalCertLength]; - int outputOffset = 0; - for (int i = 0; i < certsBytes.length; i++) { - final int certLength = certsBytes[i].length; - System.arraycopy(certsBytes[i], 0, chainBytes, outputOffset, certLength); - outputOffset += certLength; - certsBytes[i] = null; - } - } else { - chainBytes = null; - } - - final String pkeyAlias; - if (key instanceof AndroidKeyStorePrivateKey) { - pkeyAlias = ((AndroidKeyStoreKey) key).getAlias(); - } else { - pkeyAlias = null; - } - - byte[] pkcs8EncodedPrivateKeyBytes; - KeymasterArguments importArgs; - final boolean shouldReplacePrivateKey; - if (pkeyAlias != null && pkeyAlias.startsWith(Credentials.USER_PRIVATE_KEY)) { - final String keySubalias = pkeyAlias.substring(Credentials.USER_PRIVATE_KEY.length()); - if (!alias.equals(keySubalias)) { - throw new KeyStoreException("Can only replace keys with same alias: " + alias - + " != " + keySubalias); - } - shouldReplacePrivateKey = false; - importArgs = null; - pkcs8EncodedPrivateKeyBytes = null; - } else { - shouldReplacePrivateKey = true; - // Make sure the PrivateKey format is the one we support. - final String keyFormat = key.getFormat(); - if ((keyFormat == null) || (!"PKCS#8".equals(keyFormat))) { - throw new KeyStoreException( - "Unsupported private key export format: " + keyFormat - + ". Only private keys which export their key material in PKCS#8 format are" - + " supported."); - } - - // Make sure we can actually encode the key. - pkcs8EncodedPrivateKeyBytes = key.getEncoded(); - if (pkcs8EncodedPrivateKeyBytes == null) { - throw new KeyStoreException("Private key did not export any key material"); - } - - importArgs = new KeymasterArguments(); - try { - importArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, - KeyProperties.KeyAlgorithm.toKeymasterAsymmetricKeyAlgorithm( - key.getAlgorithm())); - @KeyProperties.PurposeEnum int purposes = spec.getPurposes(); - importArgs.addEnums(KeymasterDefs.KM_TAG_PURPOSE, - KeyProperties.Purpose.allToKeymaster(purposes)); - if (spec.isDigestsSpecified()) { - importArgs.addEnums(KeymasterDefs.KM_TAG_DIGEST, - KeyProperties.Digest.allToKeymaster(spec.getDigests())); - } - - importArgs.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE, - KeyProperties.BlockMode.allToKeymaster(spec.getBlockModes())); - int[] keymasterEncryptionPaddings = - KeyProperties.EncryptionPadding.allToKeymaster( - spec.getEncryptionPaddings()); - if (((purposes & KeyProperties.PURPOSE_ENCRYPT) != 0) - && (spec.isRandomizedEncryptionRequired())) { - for (int keymasterPadding : keymasterEncryptionPaddings) { - if (!KeymasterUtils - .isKeymasterPaddingSchemeIndCpaCompatibleWithAsymmetricCrypto( - keymasterPadding)) { - throw new KeyStoreException( - "Randomized encryption (IND-CPA) required but is violated by" - + " encryption padding mode: " - + KeyProperties.EncryptionPadding.fromKeymaster( - keymasterPadding) - + ". See KeyProtection documentation."); - } - } - } - importArgs.addEnums(KeymasterDefs.KM_TAG_PADDING, keymasterEncryptionPaddings); - importArgs.addEnums(KeymasterDefs.KM_TAG_PADDING, - KeyProperties.SignaturePadding.allToKeymaster(spec.getSignaturePaddings())); - KeymasterUtils.addUserAuthArgs(importArgs, spec); - importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME, - spec.getKeyValidityStart()); - importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME, - spec.getKeyValidityForOriginationEnd()); - importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME, - spec.getKeyValidityForConsumptionEnd()); - } catch (IllegalArgumentException | IllegalStateException e) { - throw new KeyStoreException(e); - } - } - - - boolean success = false; - try { - // Store the private key, if necessary - if (shouldReplacePrivateKey) { - // Delete the stored private key and any related entries before importing the - // provided key - Credentials.deleteAllTypesForAlias(mKeyStore, alias, mUid); - KeyCharacteristics resultingKeyCharacteristics = new KeyCharacteristics(); - int errorCode = mKeyStore.importKey( - Credentials.USER_PRIVATE_KEY + alias, - importArgs, - KeymasterDefs.KM_KEY_FORMAT_PKCS8, - pkcs8EncodedPrivateKeyBytes, - mUid, - flags, - resultingKeyCharacteristics); - if (errorCode != KeyStore.NO_ERROR) { - throw new KeyStoreException("Failed to store private key", - KeyStore.getKeyStoreException(errorCode)); - } - } else { - // Keep the stored private key around -- delete all other entry types - Credentials.deleteCertificateTypesForAlias(mKeyStore, alias, mUid); - Credentials.deleteLegacyKeyForAlias(mKeyStore, alias, mUid); - } - - // Store the leaf certificate - int errorCode = mKeyStore.insert(Credentials.USER_CERTIFICATE + alias, userCertBytes, - mUid, flags); - if (errorCode != KeyStore.NO_ERROR) { - throw new KeyStoreException("Failed to store certificate #0", - KeyStore.getKeyStoreException(errorCode)); - } - - // Store the certificate chain - errorCode = mKeyStore.insert(Credentials.CA_CERTIFICATE + alias, chainBytes, - mUid, flags); - if (errorCode != KeyStore.NO_ERROR) { - throw new KeyStoreException("Failed to store certificate chain", - KeyStore.getKeyStoreException(errorCode)); - } - success = true; - } finally { - if (!success) { - if (shouldReplacePrivateKey) { - Credentials.deleteAllTypesForAlias(mKeyStore, alias, mUid); - } else { - Credentials.deleteCertificateTypesForAlias(mKeyStore, alias, mUid); - Credentials.deleteLegacyKeyForAlias(mKeyStore, alias, mUid); - } - } - } - } - - private void setSecretKeyEntry(String entryAlias, SecretKey key, - java.security.KeyStore.ProtectionParameter param) - throws KeyStoreException { - if ((param != null) && (!(param instanceof KeyProtection))) { - throw new KeyStoreException( - "Unsupported protection parameter class: " + param.getClass().getName() - + ". Supported: " + KeyProtection.class.getName()); - } - KeyProtection params = (KeyProtection) param; - - if (key instanceof AndroidKeyStoreSecretKey) { - // KeyStore-backed secret key. It cannot be duplicated into another entry and cannot - // overwrite its own entry. - String keyAliasInKeystore = ((AndroidKeyStoreSecretKey) key).getAlias(); - if (keyAliasInKeystore == null) { - throw new KeyStoreException("KeyStore-backed secret key does not have an alias"); - } - String keyAliasPrefix = Credentials.USER_PRIVATE_KEY; - if (!keyAliasInKeystore.startsWith(keyAliasPrefix)) { - // try legacy prefix - keyAliasPrefix = Credentials.USER_SECRET_KEY; - if (!keyAliasInKeystore.startsWith(keyAliasPrefix)) { - throw new KeyStoreException("KeyStore-backed secret key has invalid alias: " - + keyAliasInKeystore); - } - } - String keyEntryAlias = - keyAliasInKeystore.substring(keyAliasPrefix.length()); - if (!entryAlias.equals(keyEntryAlias)) { - throw new KeyStoreException("Can only replace KeyStore-backed keys with same" - + " alias: " + entryAlias + " != " + keyEntryAlias); - } - // This is the entry where this key is already stored. No need to do anything. - if (params != null) { - throw new KeyStoreException("Modifying KeyStore-backed key using protection" - + " parameters not supported"); - } - return; - } - - if (params == null) { - throw new KeyStoreException( - "Protection parameters must be specified when importing a symmetric key"); - } - - // Not a KeyStore-backed secret key -- import its key material into keystore. - String keyExportFormat = key.getFormat(); - if (keyExportFormat == null) { - throw new KeyStoreException( - "Only secret keys that export their key material are supported"); - } else if (!"RAW".equals(keyExportFormat)) { - throw new KeyStoreException( - "Unsupported secret key material export format: " + keyExportFormat); - } - byte[] keyMaterial = key.getEncoded(); - if (keyMaterial == null) { - throw new KeyStoreException("Key did not export its key material despite supporting" - + " RAW format export"); - } - - KeymasterArguments args = new KeymasterArguments(); - try { - int keymasterAlgorithm = - KeyProperties.KeyAlgorithm.toKeymasterSecretKeyAlgorithm(key.getAlgorithm()); - args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, keymasterAlgorithm); - - int[] keymasterDigests; - if (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) { - // JCA HMAC key algorithm implies a digest (e.g., HmacSHA256 key algorithm - // implies SHA-256 digest). Because keymaster HMAC key is authorized only for one - // digest, we don't let import parameters override the digest implied by the key. - // If the parameters specify digests at all, they must specify only one digest, the - // only implied by key algorithm. - int keymasterImpliedDigest = - KeyProperties.KeyAlgorithm.toKeymasterDigest(key.getAlgorithm()); - if (keymasterImpliedDigest == -1) { - throw new ProviderException( - "HMAC key algorithm digest unknown for key algorithm " - + key.getAlgorithm()); - } - keymasterDigests = new int[] {keymasterImpliedDigest}; - if (params.isDigestsSpecified()) { - // Digest(s) explicitly specified in params -- check that the list consists of - // exactly one digest, the one implied by key algorithm. - int[] keymasterDigestsFromParams = - KeyProperties.Digest.allToKeymaster(params.getDigests()); - if ((keymasterDigestsFromParams.length != 1) - || (keymasterDigestsFromParams[0] != keymasterImpliedDigest)) { - throw new KeyStoreException( - "Unsupported digests specification: " - + Arrays.asList(params.getDigests()) + ". Only " - + KeyProperties.Digest.fromKeymaster(keymasterImpliedDigest) - + " supported for HMAC key algorithm " + key.getAlgorithm()); - } - } - } else { - // Key algorithm does not imply a digest. - if (params.isDigestsSpecified()) { - keymasterDigests = KeyProperties.Digest.allToKeymaster(params.getDigests()); - } else { - keymasterDigests = EmptyArray.INT; - } - } - args.addEnums(KeymasterDefs.KM_TAG_DIGEST, keymasterDigests); - - @KeyProperties.PurposeEnum int purposes = params.getPurposes(); - int[] keymasterBlockModes = - KeyProperties.BlockMode.allToKeymaster(params.getBlockModes()); - if (((purposes & KeyProperties.PURPOSE_ENCRYPT) != 0) - && (params.isRandomizedEncryptionRequired())) { - for (int keymasterBlockMode : keymasterBlockModes) { - if (!KeymasterUtils.isKeymasterBlockModeIndCpaCompatibleWithSymmetricCrypto( - keymasterBlockMode)) { - throw new KeyStoreException( - "Randomized encryption (IND-CPA) required but may be violated by" - + " block mode: " - + KeyProperties.BlockMode.fromKeymaster(keymasterBlockMode) - + ". See KeyProtection documentation."); - } - } - } - args.addEnums(KeymasterDefs.KM_TAG_PURPOSE, - KeyProperties.Purpose.allToKeymaster(purposes)); - args.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockModes); - if (params.getSignaturePaddings().length > 0) { - throw new KeyStoreException("Signature paddings not supported for symmetric keys"); - } - int[] keymasterPaddings = KeyProperties.EncryptionPadding.allToKeymaster( - params.getEncryptionPaddings()); - args.addEnums(KeymasterDefs.KM_TAG_PADDING, keymasterPaddings); - KeymasterUtils.addUserAuthArgs(args, params); - KeymasterUtils.addMinMacLengthAuthorizationIfNecessary( - args, - keymasterAlgorithm, - keymasterBlockModes, - keymasterDigests); - args.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME, - params.getKeyValidityStart()); - args.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME, - params.getKeyValidityForOriginationEnd()); - args.addDateIfNotNull(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME, - params.getKeyValidityForConsumptionEnd()); - - if (((purposes & KeyProperties.PURPOSE_ENCRYPT) != 0) - && (!params.isRandomizedEncryptionRequired())) { - // Permit caller-provided IV when encrypting with this key - args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE); - } - } catch (IllegalArgumentException | IllegalStateException e) { - throw new KeyStoreException(e); - } - int flags = 0; - if (params.isCriticalToDeviceEncryption()) { - flags |= KeyStore.FLAG_CRITICAL_TO_DEVICE_ENCRYPTION; - } - if (params.isStrongBoxBacked()) { - flags |= KeyStore.FLAG_STRONGBOX; - } - - Credentials.deleteAllTypesForAlias(mKeyStore, entryAlias, mUid); - String keyAliasInKeystore = Credentials.USER_PRIVATE_KEY + entryAlias; - int errorCode = mKeyStore.importKey( - keyAliasInKeystore, - args, - KeymasterDefs.KM_KEY_FORMAT_RAW, - keyMaterial, - mUid, - flags, - new KeyCharacteristics()); - if (errorCode != KeyStore.NO_ERROR) { - throw new KeyStoreException("Failed to import secret key. Keystore error code: " - + errorCode); - } - } - - private void setWrappedKeyEntry(String alias, WrappedKeyEntry entry, - java.security.KeyStore.ProtectionParameter param) throws KeyStoreException { - if (param != null) { - throw new KeyStoreException("Protection parameters are specified inside wrapped keys"); - } - - byte[] maskingKey = new byte[32]; - - - KeymasterArguments args = new KeymasterArguments(); - String[] parts = entry.getTransformation().split("/"); - - String algorithm = parts[0]; - if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(algorithm)) { - args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_RSA); - } else if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(algorithm)) { - args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_RSA); - } - - if (parts.length > 1) { - String mode = parts[1]; - if (KeyProperties.BLOCK_MODE_ECB.equalsIgnoreCase(mode)) { - args.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_ECB); - } else if (KeyProperties.BLOCK_MODE_CBC.equalsIgnoreCase(mode)) { - args.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_CBC); - } else if (KeyProperties.BLOCK_MODE_CTR.equalsIgnoreCase(mode)) { - args.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_CTR); - } else if (KeyProperties.BLOCK_MODE_GCM.equalsIgnoreCase(mode)) { - args.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_GCM); - } - } - - if (parts.length > 2) { - String padding = parts[2]; - if (KeyProperties.ENCRYPTION_PADDING_NONE.equalsIgnoreCase(padding)) { - // Noop - } else if (KeyProperties.ENCRYPTION_PADDING_PKCS7.equalsIgnoreCase(padding)) { - args.addEnums(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_PKCS7); - } else if (KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1.equalsIgnoreCase(padding)) { - args.addEnums(KeymasterDefs.KM_TAG_PADDING, - KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT); - } else if (KeyProperties.ENCRYPTION_PADDING_RSA_OAEP.equalsIgnoreCase(padding)) { - args.addEnums(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_RSA_OAEP); - } - } - - KeyGenParameterSpec spec = (KeyGenParameterSpec) entry.getAlgorithmParameterSpec(); - if (spec.isDigestsSpecified()) { - String digest = spec.getDigests()[0]; - if (KeyProperties.DIGEST_NONE.equalsIgnoreCase(digest)) { - // Noop - } else if (KeyProperties.DIGEST_MD5.equalsIgnoreCase(digest)) { - args.addEnums(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_MD5); - } else if (KeyProperties.DIGEST_SHA1.equalsIgnoreCase(digest)) { - args.addEnums(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_SHA1); - } else if (KeyProperties.DIGEST_SHA224.equalsIgnoreCase(digest)) { - args.addEnums(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_SHA_2_224); - } else if (KeyProperties.DIGEST_SHA256.equalsIgnoreCase(digest)) { - args.addEnums(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_SHA_2_256); - } else if (KeyProperties.DIGEST_SHA384.equalsIgnoreCase(digest)) { - args.addEnums(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_SHA_2_384); - } else if (KeyProperties.DIGEST_SHA512.equalsIgnoreCase(digest)) { - args.addEnums(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_SHA_2_512); - } - } - - int errorCode = mKeyStore.importWrappedKey( - Credentials.USER_PRIVATE_KEY + alias, - entry.getWrappedKeyBytes(), - Credentials.USER_PRIVATE_KEY + entry.getWrappingKeyAlias(), - maskingKey, - args, - GateKeeper.getSecureUserId(), - 0, // FIXME fingerprint id? - mUid, - new KeyCharacteristics()); - if (errorCode == KeymasterDefs.KM_ERROR_UNIMPLEMENTED) { - throw new SecureKeyImportUnavailableException("Could not import wrapped key"); - } else if (errorCode != KeyStore.NO_ERROR) { - throw new KeyStoreException("Failed to import wrapped key. Keystore error code: " - + errorCode); - } - } - - @Override - public void engineSetKeyEntry(String alias, byte[] userKey, Certificate[] chain) - throws KeyStoreException { - throw new KeyStoreException("Operation not supported because key encoding is unknown"); - } - - @Override - public void engineSetCertificateEntry(String alias, Certificate cert) throws KeyStoreException { - if (isKeyEntry(alias)) { - throw new KeyStoreException("Entry exists and is not a trusted certificate"); - } - - // We can't set something to null. - if (cert == null) { - throw new NullPointerException("cert == null"); - } - - final byte[] encoded; - try { - encoded = cert.getEncoded(); - } catch (CertificateEncodingException e) { - throw new KeyStoreException(e); - } - - if (!mKeyStore.put(Credentials.CA_CERTIFICATE + alias, encoded, mUid, KeyStore.FLAG_NONE)) { - throw new KeyStoreException("Couldn't insert certificate; is KeyStore initialized?"); - } - } - - @Override - public void engineDeleteEntry(String alias) throws KeyStoreException { - if (!Credentials.deleteAllTypesForAlias(mKeyStore, alias, mUid)) { - throw new KeyStoreException("Failed to delete entry: " + alias); - } - } - - private Set<String> getUniqueAliases() { - final String[] rawAliases = mKeyStore.list("", mUid); - if (rawAliases == null) { - return new HashSet<String>(); - } - - final Set<String> aliases = new HashSet<String>(rawAliases.length); - for (String alias : rawAliases) { - final int idx = alias.indexOf('_'); - if ((idx == -1) || (alias.length() <= idx)) { - Log.e(NAME, "invalid alias: " + alias); - continue; - } - - aliases.add(new String(alias.substring(idx + 1))); - } - - return aliases; - } - - @Override - public Enumeration<String> engineAliases() { - return Collections.enumeration(getUniqueAliases()); - } - - @Override - public boolean engineContainsAlias(String alias) { - if (alias == null) { - throw new NullPointerException("alias == null"); - } - - return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias, mUid) - || mKeyStore.contains(Credentials.USER_SECRET_KEY + alias, mUid) - || mKeyStore.contains(Credentials.USER_CERTIFICATE + alias, mUid) - || mKeyStore.contains(Credentials.CA_CERTIFICATE + alias, mUid); - } - - @Override - public int engineSize() { - return getUniqueAliases().size(); - } - - @Override - public boolean engineIsKeyEntry(String alias) { - return isKeyEntry(alias); - } - - private boolean isKeyEntry(String alias) { - return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias, mUid) || - mKeyStore.contains(Credentials.USER_SECRET_KEY + alias, mUid); - } - - - private boolean isCertificateEntry(String alias) { - if (alias == null) { - throw new NullPointerException("alias == null"); - } - - return mKeyStore.contains(Credentials.CA_CERTIFICATE + alias, mUid); - } - - @Override - public boolean engineIsCertificateEntry(String alias) { - return !isKeyEntry(alias) && isCertificateEntry(alias); - } - - @Override - public String engineGetCertificateAlias(Certificate cert) { - if (cert == null) { - return null; - } - if (!"X.509".equalsIgnoreCase(cert.getType())) { - // Only X.509 certificates supported - return null; - } - byte[] targetCertBytes; - try { - targetCertBytes = cert.getEncoded(); - } catch (CertificateEncodingException e) { - return null; - } - if (targetCertBytes == null) { - return null; - } - - final Set<String> nonCaEntries = new HashSet<String>(); - - /* - * First scan the PrivateKeyEntry types. The KeyStoreSpi documentation - * says to only compare the first certificate in the chain which is - * equivalent to the USER_CERTIFICATE prefix for the Android keystore - * convention. - */ - final String[] certAliases = mKeyStore.list(Credentials.USER_CERTIFICATE, mUid); - if (certAliases != null) { - for (String alias : certAliases) { - final byte[] certBytes = mKeyStore.get(Credentials.USER_CERTIFICATE + alias, mUid); - if (certBytes == null) { - continue; - } - - nonCaEntries.add(alias); - - if (Arrays.equals(certBytes, targetCertBytes)) { - return alias; - } - } - } - - /* - * Look at all the TrustedCertificateEntry types. Skip all the - * PrivateKeyEntry we looked at above. - */ - final String[] caAliases = mKeyStore.list(Credentials.CA_CERTIFICATE, mUid); - if (certAliases != null) { - for (String alias : caAliases) { - if (nonCaEntries.contains(alias)) { - continue; - } - - final byte[] certBytes = mKeyStore.get(Credentials.CA_CERTIFICATE + alias, mUid); - if (certBytes == null) { - continue; - } - - if (Arrays.equals(certBytes, targetCertBytes)) { - return alias; - } - } - } - - return null; - } - - @Override - public void engineStore(OutputStream stream, char[] password) throws IOException, - NoSuchAlgorithmException, CertificateException { - throw new UnsupportedOperationException("Can not serialize AndroidKeyStore to OutputStream"); - } - - @Override - public void engineLoad(InputStream stream, char[] password) throws IOException, - NoSuchAlgorithmException, CertificateException { - if (stream != null) { - throw new IllegalArgumentException("InputStream not supported"); - } - - if (password != null) { - throw new IllegalArgumentException("password not supported"); - } - - // Unfortunate name collision. - mKeyStore = KeyStore.getInstance(); - mUid = KeyStore.UID_SELF; - } - - @Override - public void engineLoad(LoadStoreParameter param) throws IOException, - NoSuchAlgorithmException, CertificateException { - int uid = KeyStore.UID_SELF; - if (param != null) { - if (param instanceof AndroidKeyStoreLoadStoreParameter) { - uid = ((AndroidKeyStoreLoadStoreParameter) param).getUid(); - } else { - throw new IllegalArgumentException( - "Unsupported param type: " + param.getClass()); - } - } - mKeyStore = KeyStore.getInstance(); - mUid = uid; - } - - @Override - public void engineSetEntry(String alias, Entry entry, ProtectionParameter param) - throws KeyStoreException { - if (entry == null) { - throw new KeyStoreException("entry == null"); - } - - Credentials.deleteAllTypesForAlias(mKeyStore, alias, mUid); - - if (entry instanceof java.security.KeyStore.TrustedCertificateEntry) { - java.security.KeyStore.TrustedCertificateEntry trE = - (java.security.KeyStore.TrustedCertificateEntry) entry; - engineSetCertificateEntry(alias, trE.getTrustedCertificate()); - return; - } - - if (entry instanceof PrivateKeyEntry) { - PrivateKeyEntry prE = (PrivateKeyEntry) entry; - setPrivateKeyEntry(alias, prE.getPrivateKey(), prE.getCertificateChain(), param); - } else if (entry instanceof SecretKeyEntry) { - SecretKeyEntry secE = (SecretKeyEntry) entry; - setSecretKeyEntry(alias, secE.getSecretKey(), param); - } else if (entry instanceof WrappedKeyEntry) { - WrappedKeyEntry wke = (WrappedKeyEntry) entry; - setWrappedKeyEntry(alias, wke, param); - } else { - throw new KeyStoreException( - "Entry must be a PrivateKeyEntry, SecretKeyEntry or TrustedCertificateEntry" - + "; was " + entry); - } - } - - /** - * {@link X509Certificate} which returns {@link AndroidKeyStorePublicKey} from - * {@link #getPublicKey()}. This is so that crypto operations on these public keys contain - * can find out which keystore private key entry to use. This is needed so that Android Keystore - * crypto operations using public keys can find out which key alias to use. These operations - * require an alias. - */ - static class KeyStoreX509Certificate extends DelegatingX509Certificate { - private final String mPrivateKeyAlias; - private final int mPrivateKeyUid; - KeyStoreX509Certificate(String privateKeyAlias, int privateKeyUid, - X509Certificate delegate) { - super(delegate); - mPrivateKeyAlias = privateKeyAlias; - mPrivateKeyUid = privateKeyUid; - } - - @Override - public PublicKey getPublicKey() { - PublicKey original = super.getPublicKey(); - return AndroidKeyStoreProvider.getAndroidKeyStorePublicKey( - mPrivateKeyAlias, mPrivateKeyUid, - original.getAlgorithm(), original.getEncoded()); - } - } -} diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreUnauthenticatedAESCipherSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreUnauthenticatedAESCipherSpi.java deleted file mode 100644 index 1f1d36fa4405..000000000000 --- a/keystore/java/android/security/keystore/AndroidKeyStoreUnauthenticatedAESCipherSpi.java +++ /dev/null @@ -1,319 +0,0 @@ -/* - * 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.keystore; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.security.keymaster.KeymasterArguments; -import android.security.keymaster.KeymasterDefs; - -import java.security.AlgorithmParameters; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.Key; -import java.security.NoSuchAlgorithmException; -import java.security.ProviderException; -import java.security.spec.AlgorithmParameterSpec; -import java.security.spec.InvalidParameterSpecException; -import java.util.Arrays; - -import javax.crypto.CipherSpi; -import javax.crypto.spec.IvParameterSpec; - -/** - * Base class for Android Keystore unauthenticated AES {@link CipherSpi} implementations. - * - * @hide - */ -class AndroidKeyStoreUnauthenticatedAESCipherSpi extends AndroidKeyStoreCipherSpiBase { - - abstract static class ECB extends AndroidKeyStoreUnauthenticatedAESCipherSpi { - protected ECB(int keymasterPadding) { - super(KeymasterDefs.KM_MODE_ECB, keymasterPadding, false); - } - - public static class NoPadding extends ECB { - public NoPadding() { - super(KeymasterDefs.KM_PAD_NONE); - } - } - - public static class PKCS7Padding extends ECB { - public PKCS7Padding() { - super(KeymasterDefs.KM_PAD_PKCS7); - } - } - } - - abstract static class CBC extends AndroidKeyStoreUnauthenticatedAESCipherSpi { - protected CBC(int keymasterPadding) { - super(KeymasterDefs.KM_MODE_CBC, keymasterPadding, true); - } - - public static class NoPadding extends CBC { - public NoPadding() { - super(KeymasterDefs.KM_PAD_NONE); - } - } - - public static class PKCS7Padding extends CBC { - public PKCS7Padding() { - super(KeymasterDefs.KM_PAD_PKCS7); - } - } - } - - abstract static class CTR extends AndroidKeyStoreUnauthenticatedAESCipherSpi { - protected CTR(int keymasterPadding) { - super(KeymasterDefs.KM_MODE_CTR, keymasterPadding, true); - } - - public static class NoPadding extends CTR { - public NoPadding() { - super(KeymasterDefs.KM_PAD_NONE); - } - } - } - - private static final int BLOCK_SIZE_BYTES = 16; - - private final int mKeymasterBlockMode; - private final int mKeymasterPadding; - /** Whether this transformation requires an IV. */ - private final boolean mIvRequired; - - private byte[] mIv; - - /** Whether the current {@code #mIv} has been used by the underlying crypto operation. */ - private boolean mIvHasBeenUsed; - - AndroidKeyStoreUnauthenticatedAESCipherSpi( - int keymasterBlockMode, - int keymasterPadding, - boolean ivRequired) { - mKeymasterBlockMode = keymasterBlockMode; - mKeymasterPadding = keymasterPadding; - mIvRequired = ivRequired; - } - - @Override - protected final void resetAll() { - mIv = null; - mIvHasBeenUsed = false; - super.resetAll(); - } - - @Override - protected final void resetWhilePreservingInitState() { - super.resetWhilePreservingInitState(); - } - - @Override - protected final void initKey(int opmode, Key key) throws InvalidKeyException { - if (!(key instanceof AndroidKeyStoreSecretKey)) { - throw new InvalidKeyException( - "Unsupported key: " + ((key != null) ? key.getClass().getName() : "null")); - } - if (!KeyProperties.KEY_ALGORITHM_AES.equalsIgnoreCase(key.getAlgorithm())) { - throw new InvalidKeyException( - "Unsupported key algorithm: " + key.getAlgorithm() + ". Only " + - KeyProperties.KEY_ALGORITHM_AES + " supported"); - } - setKey((AndroidKeyStoreSecretKey) key); - } - - @Override - protected final void initAlgorithmSpecificParameters() throws InvalidKeyException { - if (!mIvRequired) { - return; - } - - // IV is used - if (!isEncrypting()) { - throw new InvalidKeyException("IV required when decrypting" - + ". Use IvParameterSpec or AlgorithmParameters to provide it."); - } - } - - @Override - protected final void initAlgorithmSpecificParameters(AlgorithmParameterSpec params) - throws InvalidAlgorithmParameterException { - if (!mIvRequired) { - if (params != null) { - throw new InvalidAlgorithmParameterException("Unsupported parameters: " + params); - } - return; - } - - // IV is used - if (params == null) { - if (!isEncrypting()) { - // IV must be provided by the caller - throw new InvalidAlgorithmParameterException( - "IvParameterSpec must be provided when decrypting"); - } - return; - } - if (!(params instanceof IvParameterSpec)) { - throw new InvalidAlgorithmParameterException("Only IvParameterSpec supported"); - } - mIv = ((IvParameterSpec) params).getIV(); - if (mIv == null) { - throw new InvalidAlgorithmParameterException("Null IV in IvParameterSpec"); - } - } - - @Override - protected final void initAlgorithmSpecificParameters(AlgorithmParameters params) - throws InvalidAlgorithmParameterException { - if (!mIvRequired) { - if (params != null) { - throw new InvalidAlgorithmParameterException("Unsupported parameters: " + params); - } - return; - } - - // IV is used - if (params == null) { - if (!isEncrypting()) { - // IV must be provided by the caller - throw new InvalidAlgorithmParameterException("IV required when decrypting" - + ". Use IvParameterSpec or AlgorithmParameters to provide it."); - } - return; - } - - if (!"AES".equalsIgnoreCase(params.getAlgorithm())) { - throw new InvalidAlgorithmParameterException( - "Unsupported AlgorithmParameters algorithm: " + params.getAlgorithm() - + ". Supported: AES"); - } - - IvParameterSpec ivSpec; - try { - ivSpec = params.getParameterSpec(IvParameterSpec.class); - } catch (InvalidParameterSpecException e) { - if (!isEncrypting()) { - // IV must be provided by the caller - throw new InvalidAlgorithmParameterException("IV required when decrypting" - + ", but not found in parameters: " + params, e); - } - mIv = null; - return; - } - mIv = ivSpec.getIV(); - if (mIv == null) { - throw new InvalidAlgorithmParameterException("Null IV in AlgorithmParameters"); - } - } - - @Override - protected final int getAdditionalEntropyAmountForBegin() { - if ((mIvRequired) && (mIv == null) && (isEncrypting())) { - // IV will need to be generated - return BLOCK_SIZE_BYTES; - } - - return 0; - } - - @Override - protected final int getAdditionalEntropyAmountForFinish() { - return 0; - } - - @Override - protected final void addAlgorithmSpecificParametersToBegin( - @NonNull KeymasterArguments keymasterArgs) { - if ((isEncrypting()) && (mIvRequired) && (mIvHasBeenUsed)) { - // IV is being reused for encryption: this violates security best practices. - throw new IllegalStateException( - "IV has already been used. Reusing IV in encryption mode violates security best" - + " practices."); - } - - keymasterArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES); - keymasterArgs.addEnum(KeymasterDefs.KM_TAG_BLOCK_MODE, mKeymasterBlockMode); - keymasterArgs.addEnum(KeymasterDefs.KM_TAG_PADDING, mKeymasterPadding); - if ((mIvRequired) && (mIv != null)) { - keymasterArgs.addBytes(KeymasterDefs.KM_TAG_NONCE, mIv); - } - } - - @Override - protected final void loadAlgorithmSpecificParametersFromBeginResult( - @NonNull KeymasterArguments keymasterArgs) { - mIvHasBeenUsed = true; - - // NOTE: Keymaster doesn't always return an IV, even if it's used. - byte[] returnedIv = keymasterArgs.getBytes(KeymasterDefs.KM_TAG_NONCE, null); - if ((returnedIv != null) && (returnedIv.length == 0)) { - returnedIv = null; - } - - if (mIvRequired) { - if (mIv == null) { - mIv = returnedIv; - } else if ((returnedIv != null) && (!Arrays.equals(returnedIv, mIv))) { - throw new ProviderException("IV in use differs from provided IV"); - } - } else { - if (returnedIv != null) { - throw new ProviderException( - "IV in use despite IV not being used by this transformation"); - } - } - } - - @Override - protected final int engineGetBlockSize() { - return BLOCK_SIZE_BYTES; - } - - @Override - protected final int engineGetOutputSize(int inputLen) { - return inputLen + 3 * BLOCK_SIZE_BYTES; - } - - @Override - protected final byte[] engineGetIV() { - return ArrayUtils.cloneIfNotEmpty(mIv); - } - - @Nullable - @Override - protected final AlgorithmParameters engineGetParameters() { - if (!mIvRequired) { - return null; - } - if ((mIv != null) && (mIv.length > 0)) { - try { - AlgorithmParameters params = AlgorithmParameters.getInstance("AES"); - params.init(new IvParameterSpec(mIv)); - return params; - } catch (NoSuchAlgorithmException e) { - throw new ProviderException( - "Failed to obtain AES AlgorithmParameters", e); - } catch (InvalidParameterSpecException e) { - throw new ProviderException( - "Failed to initialize AES AlgorithmParameters with an IV", - e); - } - } - return null; - } -} diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java index 9ca551b26aab..1f9022b4ad3d 100644 --- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java +++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java @@ -446,13 +446,6 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu @UnsupportedAppUsage @Deprecated public int getUid() { - if (!AndroidKeyStoreProvider.isKeystore2Enabled()) { - // If Keystore2 has not been enabled we have to behave as if mNamespace is actually - // a UID, because we are still being used with the old Keystore SPI. - // TODO This if statement and body can be removed when the Keystore 2 migration is - // complete. b/171563717 - return mNamespace; - } try { return KeyProperties.namespaceToLegacyUid(mNamespace); } catch (IllegalArgumentException e) { @@ -1021,14 +1014,6 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu @NonNull @Deprecated public Builder setUid(int uid) { - if (!AndroidKeyStoreProvider.isKeystore2Enabled()) { - // If Keystore2 has not been enabled we have to behave as if mNamespace is actually - // a UID, because we are still being used with the old Keystore SPI. - // TODO This if statement and body can be removed when the Keystore 2 migration is - // complete. b/171563717 - mNamespace = uid; - return this; - } mNamespace = KeyProperties.legacyUidToNamespace(uid); return this; } @@ -1666,9 +1651,10 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu * Set whether this key is critical to the device encryption flow * * This is a special flag only available to system servers to indicate the current key - * is part of the device encryption flow. + * is part of the device encryption flow. Setting this flag causes the key to not + * be cryptographically bound to the LSKF even if the key is otherwise authentication + * bound. * - * @see android.security.KeyStore#FLAG_CRITICAL_TO_DEVICE_ENCRYPTION * @hide */ public Builder setCriticalToDeviceEncryption(boolean critical) { diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java index fe92270ca508..c14c3c534cf4 100644 --- a/keystore/java/android/security/keystore/KeyProtection.java +++ b/keystore/java/android/security/keystore/KeyProtection.java @@ -24,6 +24,7 @@ import android.app.KeyguardManager; import android.hardware.biometrics.BiometricManager; import android.hardware.biometrics.BiometricPrompt; import android.security.GateKeeper; +import android.security.keystore2.KeymasterUtils; import java.security.Key; import java.security.KeyStore.ProtectionParameter; diff --git a/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java b/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java deleted file mode 100644 index 2c0f40d528d2..000000000000 --- a/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java +++ /dev/null @@ -1,225 +0,0 @@ -/* - * 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.keystore; - -import android.os.IBinder; -import android.security.KeyStore; -import android.security.KeyStoreException; -import android.security.keymaster.KeymasterDefs; -import android.security.keymaster.OperationResult; - -import libcore.util.EmptyArray; - -/** - * Helper for streaming a crypto operation's input and output via {@link KeyStore} service's - * {@code update} and {@code finish} operations. - * - * <p>The helper abstracts away issues that need to be solved in most code that uses KeyStore's - * update and finish operations. Firstly, KeyStore's update operation can consume only a limited - * amount of data in one go because the operations are marshalled via Binder. Secondly, the update - * operation may consume less data than provided, in which case the caller has to buffer the - * remainder for next time. Thirdly, when the input is smaller than a threshold, skipping update - * and passing input data directly to final improves performance. This threshold is configurable; - * using a threshold <= 1 causes the helper act eagerly, which may be required for some types of - * operations (e.g. ciphers). - * - * <p>The helper exposes {@link #update(byte[], int, int) update} and - * {@link #doFinal(byte[], int, int, byte[], byte[]) doFinal} operations which can be used to - * conveniently implement various JCA crypto primitives. - * - * <p>Bidirectional chunked streaming of data via a KeyStore crypto operation is abstracted away as - * a {@link Stream} to avoid having this class deal with operation tokens and occasional additional - * parameters to {@code update} and {@code final} operations. - * - * @hide - */ -class KeyStoreCryptoOperationChunkedStreamer implements KeyStoreCryptoOperationStreamer { - - /** - * Bidirectional chunked data stream over a KeyStore crypto operation. - */ - interface Stream { - /** - * Returns the result of the KeyStore {@code update} operation or null if keystore couldn't - * be reached. - */ - OperationResult update(byte[] input); - - /** - * Returns the result of the KeyStore {@code finish} operation or null if keystore couldn't - * be reached. - */ - OperationResult finish(byte[] input, byte[] siganture, byte[] additionalEntropy); - } - - // Binder buffer is about 1MB, but it's shared between all active transactions of the process. - // Thus, it's safer to use a much smaller upper bound. - private static final int DEFAULT_CHUNK_SIZE_MAX = 64 * 1024; - // The chunk buffer will be sent to update until its size under this threshold. - // This threshold should be <= the max input allowed for finish. - // Setting this threshold <= 1 will effectivley disable buffering between updates. - private static final int DEFAULT_CHUNK_SIZE_THRESHOLD = 2 * 1024; - - private final Stream mKeyStoreStream; - private final int mChunkSizeMax; - private final int mChunkSizeThreshold; - private final byte[] mChunk; - private int mChunkLength = 0; - private long mConsumedInputSizeBytes; - private long mProducedOutputSizeBytes; - - KeyStoreCryptoOperationChunkedStreamer(Stream operation) { - this(operation, DEFAULT_CHUNK_SIZE_THRESHOLD, DEFAULT_CHUNK_SIZE_MAX); - } - - KeyStoreCryptoOperationChunkedStreamer(Stream operation, int chunkSizeThreshold) { - this(operation, chunkSizeThreshold, DEFAULT_CHUNK_SIZE_MAX); - } - - KeyStoreCryptoOperationChunkedStreamer(Stream operation, int chunkSizeThreshold, - int chunkSizeMax) { - mKeyStoreStream = operation; - mChunkSizeMax = chunkSizeMax; - if (chunkSizeThreshold <= 0) { - mChunkSizeThreshold = 1; - } else if (chunkSizeThreshold > chunkSizeMax) { - mChunkSizeThreshold = chunkSizeMax; - } else { - mChunkSizeThreshold = chunkSizeThreshold; - } - mChunk = new byte[mChunkSizeMax]; - } - - public byte[] update(byte[] input, int inputOffset, int inputLength) throws KeyStoreException { - if (inputLength == 0 || input == null) { - // No input provided - return EmptyArray.BYTE; - } - if (inputLength < 0 || inputOffset < 0 || (inputOffset + inputLength) > input.length) { - throw new KeyStoreException(KeymasterDefs.KM_ERROR_UNKNOWN_ERROR, - "Input offset and length out of bounds of input array"); - } - - byte[] output = EmptyArray.BYTE; - - while (inputLength > 0 || mChunkLength >= mChunkSizeThreshold) { - int inputConsumed = ArrayUtils.copy(input, inputOffset, mChunk, mChunkLength, - inputLength); - inputLength -= inputConsumed; - inputOffset += inputConsumed; - mChunkLength += inputConsumed; - mConsumedInputSizeBytes += inputConsumed; - - if (mChunkLength > mChunkSizeMax) { - throw new KeyStoreException(KeymasterDefs.KM_ERROR_INVALID_INPUT_LENGTH, - "Chunk size exceeded max chunk size. Max: " + mChunkSizeMax - + " Actual: " + mChunkLength); - } - - if (mChunkLength >= mChunkSizeThreshold) { - OperationResult opResult = mKeyStoreStream.update( - ArrayUtils.subarray(mChunk, 0, mChunkLength)); - - if (opResult == null) { - throw new KeyStoreConnectException(); - } else if (opResult.resultCode != KeyStore.NO_ERROR) { - throw KeyStore.getKeyStoreException(opResult.resultCode); - } - if (opResult.inputConsumed <= 0) { - throw new KeyStoreException(KeymasterDefs.KM_ERROR_INVALID_INPUT_LENGTH, - "Keystore consumed 0 of " + mChunkLength + " bytes provided."); - } else if (opResult.inputConsumed > mChunkLength) { - throw new KeyStoreException(KeymasterDefs.KM_ERROR_UNKNOWN_ERROR, - "Keystore consumed more input than provided. Provided: " - + mChunkLength + ", consumed: " + opResult.inputConsumed); - } - mChunkLength -= opResult.inputConsumed; - - if (mChunkLength > 0) { - // Partialy consumed, shift chunk contents - ArrayUtils.copy(mChunk, opResult.inputConsumed, mChunk, 0, mChunkLength); - } - - if ((opResult.output != null) && (opResult.output.length > 0)) { - // Output was produced - mProducedOutputSizeBytes += opResult.output.length; - output = ArrayUtils.concat(output, opResult.output); - } - } - } - return output; - } - - public byte[] doFinal(byte[] input, int inputOffset, int inputLength, - byte[] signature, byte[] additionalEntropy) throws KeyStoreException { - byte[] output = update(input, inputOffset, inputLength); - byte[] finalChunk = ArrayUtils.subarray(mChunk, 0, mChunkLength); - OperationResult opResult = mKeyStoreStream.finish(finalChunk, signature, additionalEntropy); - - if (opResult == null) { - throw new KeyStoreConnectException(); - } else if (opResult.resultCode != KeyStore.NO_ERROR) { - throw KeyStore.getKeyStoreException(opResult.resultCode); - } - // If no error, assume all input consumed - mConsumedInputSizeBytes += finalChunk.length; - - if ((opResult.output != null) && (opResult.output.length > 0)) { - mProducedOutputSizeBytes += opResult.output.length; - output = ArrayUtils.concat(output, opResult.output); - } - - return output; - } - - @Override - public long getConsumedInputSizeBytes() { - return mConsumedInputSizeBytes; - } - - @Override - public long getProducedOutputSizeBytes() { - return mProducedOutputSizeBytes; - } - - /** - * Main data stream via a KeyStore streaming operation. - * - * <p>For example, for an encryption operation, this is the stream through which plaintext is - * provided and ciphertext is obtained. - */ - public static class MainDataStream implements Stream { - - private final KeyStore mKeyStore; - private final IBinder mOperationToken; - - public MainDataStream(KeyStore keyStore, IBinder operationToken) { - mKeyStore = keyStore; - mOperationToken = operationToken; - } - - @Override - public OperationResult update(byte[] input) { - return mKeyStore.update(mOperationToken, null, input); - } - - @Override - public OperationResult finish(byte[] input, byte[] signature, byte[] additionalEntropy) { - return mKeyStore.finish(mOperationToken, null, input, signature, additionalEntropy); - } - } -} diff --git a/keystore/java/android/security/keystore/KeyStoreCryptoOperationStreamer.java b/keystore/java/android/security/keystore/KeyStoreCryptoOperationStreamer.java deleted file mode 100644 index 062c2d42b3b5..000000000000 --- a/keystore/java/android/security/keystore/KeyStoreCryptoOperationStreamer.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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.keystore; - -import android.security.KeyStore; -import android.security.KeyStoreException; - -/** - * Helper for streaming a crypto operation's input and output via {@link KeyStore} service's - * {@code update} and {@code finish} operations. - * - * <p>The helper abstracts away to issues that need to be solved in most code that uses KeyStore's - * update and finish operations. Firstly, KeyStore's update operation can consume only a limited - * amount of data in one go because the operations are marshalled via Binder. Secondly, the update - * operation may consume less data than provided, in which case the caller has to buffer the - * remainder for next time. The helper exposes {@link #update(byte[], int, int) update} and - * {@link #doFinal(byte[], int, int, byte[], byte[]) doFinal} operations which can be used to - * conveniently implement various JCA crypto primitives. - * - * @hide - */ -interface KeyStoreCryptoOperationStreamer { - byte[] update(byte[] input, int inputOffset, int inputLength) throws KeyStoreException; - byte[] doFinal(byte[] input, int inputOffset, int inputLength, byte[] signature, - byte[] additionalEntropy) throws KeyStoreException; - long getConsumedInputSizeBytes(); - long getProducedOutputSizeBytes(); -} diff --git a/keystore/java/android/security/keystore/KeyStoreCryptoOperationUtils.java b/keystore/java/android/security/keystore/KeyStoreCryptoOperationUtils.java deleted file mode 100644 index c82b6e6bc6fe..000000000000 --- a/keystore/java/android/security/keystore/KeyStoreCryptoOperationUtils.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * 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.keystore; - -import android.security.KeyStore; -import android.security.keymaster.KeymasterDefs; - -import libcore.util.EmptyArray; - -import java.security.GeneralSecurityException; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.SecureRandom; - -/** - * Assorted utility methods for implementing crypto operations on top of KeyStore. - * - * @hide - */ -abstract class KeyStoreCryptoOperationUtils { - - private static volatile SecureRandom sRng; - - private KeyStoreCryptoOperationUtils() {} - - /** - * Returns the {@link InvalidKeyException} to be thrown by the {@code init} method of - * the crypto operation in response to {@code KeyStore.begin} operation or {@code null} if - * the {@code init} method should succeed. - */ - static InvalidKeyException getInvalidKeyExceptionForInit( - KeyStore keyStore, AndroidKeyStoreKey key, int beginOpResultCode) { - if (beginOpResultCode == KeyStore.NO_ERROR) { - return null; - } - - // An error occurred. However, some errors should not lead to init throwing an exception. - // See below. - InvalidKeyException e = - keyStore.getInvalidKeyException(key.getAlias(), key.getUid(), beginOpResultCode); - switch (beginOpResultCode) { - case KeyStore.OP_AUTH_NEEDED: - // Operation needs to be authorized by authenticating the user. Don't throw an - // exception is such authentication is possible for this key - // (UserNotAuthenticatedException). An example of when it's not possible is where - // the key is permanently invalidated (KeyPermanentlyInvalidatedException). - if (e instanceof UserNotAuthenticatedException) { - return null; - } - break; - } - return e; - } - - /** - * Returns the exception to be thrown by the {@code Cipher.init} method of the crypto operation - * in response to {@code KeyStore.begin} operation or {@code null} if the {@code init} method - * should succeed. - */ - public static GeneralSecurityException getExceptionForCipherInit( - KeyStore keyStore, AndroidKeyStoreKey key, int beginOpResultCode) { - if (beginOpResultCode == KeyStore.NO_ERROR) { - return null; - } - - // Cipher-specific cases - switch (beginOpResultCode) { - case KeymasterDefs.KM_ERROR_INVALID_NONCE: - return new InvalidAlgorithmParameterException("Invalid IV"); - case KeymasterDefs.KM_ERROR_CALLER_NONCE_PROHIBITED: - return new InvalidAlgorithmParameterException("Caller-provided IV not permitted"); - } - - // General cases - return getInvalidKeyExceptionForInit(keyStore, key, beginOpResultCode); - } - - /** - * Returns the requested number of random bytes to mix into keystore/keymaster RNG. - * - * @param rng RNG from which to obtain the random bytes or {@code null} for the platform-default - * RNG. - */ - static byte[] getRandomBytesToMixIntoKeystoreRng(SecureRandom rng, int sizeBytes) { - if (sizeBytes <= 0) { - return EmptyArray.BYTE; - } - if (rng == null) { - rng = getRng(); - } - byte[] result = new byte[sizeBytes]; - rng.nextBytes(result); - return result; - } - - private static SecureRandom getRng() { - // IMPLEMENTATION NOTE: It's OK to share a SecureRandom instance because SecureRandom is - // required to be thread-safe. - if (sRng == null) { - sRng = new SecureRandom(); - } - return sRng; - } -} diff --git a/keystore/java/android/security/keystore/KeymasterUtils.java b/keystore/java/android/security/keystore/KeymasterUtils.java deleted file mode 100644 index 670ef5e32f0c..000000000000 --- a/keystore/java/android/security/keystore/KeymasterUtils.java +++ /dev/null @@ -1,241 +0,0 @@ -/* - * 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.keystore; - -import android.hardware.biometrics.BiometricManager; -import android.security.GateKeeper; -import android.security.KeyStore; -import android.security.keymaster.KeymasterArguments; -import android.security.keymaster.KeymasterDefs; - -import java.security.ProviderException; -import java.util.ArrayList; -import java.util.List; - -/** - * @hide - */ -public abstract class KeymasterUtils { - - private KeymasterUtils() {} - - public static int getDigestOutputSizeBits(int keymasterDigest) { - switch (keymasterDigest) { - case KeymasterDefs.KM_DIGEST_NONE: - return -1; - case KeymasterDefs.KM_DIGEST_MD5: - return 128; - case KeymasterDefs.KM_DIGEST_SHA1: - return 160; - case KeymasterDefs.KM_DIGEST_SHA_2_224: - return 224; - case KeymasterDefs.KM_DIGEST_SHA_2_256: - return 256; - case KeymasterDefs.KM_DIGEST_SHA_2_384: - return 384; - case KeymasterDefs.KM_DIGEST_SHA_2_512: - return 512; - default: - throw new IllegalArgumentException("Unknown digest: " + keymasterDigest); - } - } - - public static boolean isKeymasterBlockModeIndCpaCompatibleWithSymmetricCrypto( - int keymasterBlockMode) { - switch (keymasterBlockMode) { - case KeymasterDefs.KM_MODE_ECB: - return false; - case KeymasterDefs.KM_MODE_CBC: - case KeymasterDefs.KM_MODE_CTR: - case KeymasterDefs.KM_MODE_GCM: - return true; - default: - throw new IllegalArgumentException("Unsupported block mode: " + keymasterBlockMode); - } - } - - public static boolean isKeymasterPaddingSchemeIndCpaCompatibleWithAsymmetricCrypto( - int keymasterPadding) { - switch (keymasterPadding) { - case KeymasterDefs.KM_PAD_NONE: - return false; - case KeymasterDefs.KM_PAD_RSA_OAEP: - case KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT: - return true; - default: - throw new IllegalArgumentException( - "Unsupported asymmetric encryption padding scheme: " + keymasterPadding); - } - } - - private static void addSids(KeymasterArguments args, UserAuthArgs spec) { - // If both biometric and credential are accepted, then just use the root sid from gatekeeper - if (spec.getUserAuthenticationType() == (KeyProperties.AUTH_BIOMETRIC_STRONG - | KeyProperties.AUTH_DEVICE_CREDENTIAL)) { - if (spec.getBoundToSpecificSecureUserId() != GateKeeper.INVALID_SECURE_USER_ID) { - args.addUnsignedLong(KeymasterDefs.KM_TAG_USER_SECURE_ID, - KeymasterArguments.toUint64(spec.getBoundToSpecificSecureUserId())); - } else { - // The key is authorized for use for the specified amount of time after the user has - // authenticated. Whatever unlocks the secure lock screen should authorize this key. - args.addUnsignedLong(KeymasterDefs.KM_TAG_USER_SECURE_ID, - KeymasterArguments.toUint64(getRootSid())); - } - } else { - List<Long> sids = new ArrayList<>(); - if ((spec.getUserAuthenticationType() & KeyProperties.AUTH_BIOMETRIC_STRONG) != 0) { - final BiometricManager bm = KeyStore.getApplicationContext() - .getSystemService(BiometricManager.class); - - // TODO: Restore permission check in getAuthenticatorIds once the ID is no longer - // needed here. - - final long[] biometricSids = bm.getAuthenticatorIds(); - - if (biometricSids.length == 0) { - throw new IllegalStateException( - "At least one biometric must be enrolled to create keys requiring user" - + " authentication for every use"); - } - - if (spec.getBoundToSpecificSecureUserId() != GateKeeper.INVALID_SECURE_USER_ID) { - sids.add(spec.getBoundToSpecificSecureUserId()); - } else if (spec.isInvalidatedByBiometricEnrollment()) { - // The biometric-only SIDs will change on biometric enrollment or removal of all - // enrolled templates, invalidating the key. - for (long sid : biometricSids) { - sids.add(sid); - } - } else { - // The root SID will *not* change on fingerprint enrollment, or removal of all - // enrolled fingerprints, allowing the key to remain valid. - sids.add(getRootSid()); - } - } else if ((spec.getUserAuthenticationType() & KeyProperties.AUTH_DEVICE_CREDENTIAL) - != 0) { - sids.add(getRootSid()); - } else { - throw new IllegalStateException("Invalid or no authentication type specified."); - } - - for (int i = 0; i < sids.size(); i++) { - args.addUnsignedLong(KeymasterDefs.KM_TAG_USER_SECURE_ID, - KeymasterArguments.toUint64(sids.get(i))); - } - } - } - - /** - * Adds keymaster arguments to express the key's authorization policy supported by user - * authentication. - * - * @param args The arguments sent to keymaster that need to be populated from the spec - * @param spec The user authentication relevant portions of the spec passed in from the caller. - * This spec will be translated into the relevant keymaster tags to be loaded into args. - * @throws IllegalStateException if user authentication is required but the system is in a wrong - * state (e.g., secure lock screen not set up) for generating or importing keys that - * require user authentication. - */ - public static void addUserAuthArgs(KeymasterArguments args, UserAuthArgs spec) { - - if (spec.isUserConfirmationRequired()) { - args.addBoolean(KeymasterDefs.KM_TAG_TRUSTED_CONFIRMATION_REQUIRED); - } - - if (spec.isUserPresenceRequired()) { - args.addBoolean(KeymasterDefs.KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED); - } - - if (spec.isUnlockedDeviceRequired()) { - args.addBoolean(KeymasterDefs.KM_TAG_UNLOCKED_DEVICE_REQUIRED); - } - - if (!spec.isUserAuthenticationRequired()) { - args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED); - return; - } - - if (spec.getUserAuthenticationValidityDurationSeconds() == 0) { - // Every use of this key needs to be authorized by the user. - addSids(args, spec); - args.addEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, spec.getUserAuthenticationType()); - - if (spec.isUserAuthenticationValidWhileOnBody()) { - throw new ProviderException("Key validity extension while device is on-body is not " - + "supported for keys requiring fingerprint authentication"); - } - } else { - addSids(args, spec); - args.addEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, spec.getUserAuthenticationType()); - args.addUnsignedInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT, - spec.getUserAuthenticationValidityDurationSeconds()); - if (spec.isUserAuthenticationValidWhileOnBody()) { - args.addBoolean(KeymasterDefs.KM_TAG_ALLOW_WHILE_ON_BODY); - } - } - } - - /** - * Adds {@code KM_TAG_MIN_MAC_LENGTH} tag, if necessary, to the keymaster arguments for - * generating or importing a key. This tag may only be needed for symmetric keys (e.g., HMAC, - * AES-GCM). - */ - public static void addMinMacLengthAuthorizationIfNecessary(KeymasterArguments args, - int keymasterAlgorithm, - int[] keymasterBlockModes, - int[] keymasterDigests) { - switch (keymasterAlgorithm) { - case KeymasterDefs.KM_ALGORITHM_AES: - if (com.android.internal.util.ArrayUtils.contains( - keymasterBlockModes, KeymasterDefs.KM_MODE_GCM)) { - // AES GCM key needs the minimum length of AEAD tag specified. - args.addUnsignedInt(KeymasterDefs.KM_TAG_MIN_MAC_LENGTH, - AndroidKeyStoreAuthenticatedAESCipherSpi.GCM - .MIN_SUPPORTED_TAG_LENGTH_BITS); - } - break; - case KeymasterDefs.KM_ALGORITHM_HMAC: - // HMAC key needs the minimum length of MAC set to the output size of the associated - // digest. This is because we do not offer a way to generate shorter MACs and - // don't offer a way to verify MACs (other than by generating them). - if (keymasterDigests.length != 1) { - throw new ProviderException( - "Unsupported number of authorized digests for HMAC key: " - + keymasterDigests.length - + ". Exactly one digest must be authorized"); - } - int keymasterDigest = keymasterDigests[0]; - int digestOutputSizeBits = getDigestOutputSizeBits(keymasterDigest); - if (digestOutputSizeBits == -1) { - throw new ProviderException( - "HMAC key authorized for unsupported digest: " - + KeyProperties.Digest.fromKeymaster(keymasterDigest)); - } - args.addUnsignedInt(KeymasterDefs.KM_TAG_MIN_MAC_LENGTH, digestOutputSizeBits); - break; - } - } - - private static long getRootSid() { - long rootSid = GateKeeper.getSecureUserId(); - if (rootSid == 0) { - throw new IllegalStateException("Secure lock screen must be enabled" - + " to create keys requiring user authentication"); - } - return rootSid; - } -} diff --git a/keystore/java/android/security/keystore/SecureKeyImportUnavailableException.java b/keystore/java/android/security/keystore/SecureKeyImportUnavailableException.java index d1cc572bdaf7..c1842b4e6d1d 100644 --- a/keystore/java/android/security/keystore/SecureKeyImportUnavailableException.java +++ b/keystore/java/android/security/keystore/SecureKeyImportUnavailableException.java @@ -16,8 +16,8 @@ package android.security.keystore; -import android.security.KeyStore; import android.security.KeyStoreException; +import android.security.keymaster.KeymasterDefs; import java.security.ProviderException; @@ -31,7 +31,7 @@ public class SecureKeyImportUnavailableException extends ProviderException { } public SecureKeyImportUnavailableException(String message) { - super(message, new KeyStoreException(KeyStore.HARDWARE_TYPE_UNAVAILABLE, + super(message, new KeyStoreException(KeymasterDefs.KM_ERROR_HARDWARE_TYPE_UNAVAILABLE, "Secure Key Import not available")); } diff --git a/keystore/java/android/security/keystore/StrongBoxUnavailableException.java b/keystore/java/android/security/keystore/StrongBoxUnavailableException.java index 6c7e9a9521e0..1f4e12edb3bf 100644 --- a/keystore/java/android/security/keystore/StrongBoxUnavailableException.java +++ b/keystore/java/android/security/keystore/StrongBoxUnavailableException.java @@ -16,8 +16,8 @@ package android.security.keystore; -import android.security.KeyStore; import android.security.KeyStoreException; +import android.security.keymaster.KeymasterDefs; import java.security.ProviderException; @@ -33,7 +33,8 @@ public class StrongBoxUnavailableException extends ProviderException { public StrongBoxUnavailableException(String message) { super(message, - new KeyStoreException(KeyStore.HARDWARE_TYPE_UNAVAILABLE, "No StrongBox available") + new KeyStoreException(KeymasterDefs.KM_ERROR_HARDWARE_TYPE_UNAVAILABLE, + "No StrongBox available") ); } diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java index 0f777495a3fe..268b15bfee8c 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java @@ -21,7 +21,6 @@ import android.security.KeyStoreException; import android.security.KeyStoreOperation; import android.security.keymaster.KeymasterDefs; import android.security.keystore.KeyStoreCryptoOperation; -import android.security.keystore.KeymasterUtils; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java index 1575bb411562..f1681ec1f7d2 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java @@ -20,12 +20,10 @@ import android.hardware.security.keymint.KeyParameter; import android.hardware.security.keymint.SecurityLevel; import android.security.KeyStore2; import android.security.KeyStoreSecurityLevel; -import android.security.keymaster.KeymasterArguments; import android.security.keymaster.KeymasterDefs; import android.security.keystore.ArrayUtils; import android.security.keystore.KeyGenParameterSpec; import android.security.keystore.KeyProperties; -import android.security.keystore.KeymasterUtils; import android.security.keystore.StrongBoxUnavailableException; import android.system.keystore2.Domain; import android.system.keystore2.IKeystoreSecurityLevel; @@ -259,7 +257,7 @@ public abstract class AndroidKeyStoreKeyGeneratorSpi extends KeyGeneratorSpi { // Check that user authentication related parameters are acceptable. This method // will throw an IllegalStateException if there are issues (e.g., secure lock screen // not set up). - KeymasterUtils.addUserAuthArgs(new KeymasterArguments(), spec); + KeyStore2ParameterUtils.addUserAuthArgs(new ArrayList<>(), spec); } catch (IllegalStateException | IllegalArgumentException e) { throw new InvalidAlgorithmParameterException(e); } diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java index 2d8901a37c05..c26d9f583fd4 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java @@ -18,6 +18,7 @@ package android.security.keystore2; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.ActivityThread; import android.content.Context; import android.hardware.security.keymint.KeyParameter; import android.hardware.security.keymint.KeyPurpose; @@ -28,7 +29,6 @@ import android.os.RemoteException; import android.security.GenerateRkpKey; import android.security.GenerateRkpKeyException; import android.security.KeyPairGeneratorSpec; -import android.security.KeyStore; import android.security.KeyStore2; import android.security.KeyStoreException; import android.security.KeyStoreSecurityLevel; @@ -39,7 +39,6 @@ import android.security.keystore.AttestationUtils; import android.security.keystore.DeviceIdAttestationException; import android.security.keystore.KeyGenParameterSpec; import android.security.keystore.KeyProperties; -import android.security.keystore.KeymasterUtils; import android.security.keystore.SecureKeyImportUnavailableException; import android.security.keystore.StrongBoxUnavailableException; import android.system.keystore2.Authorization; @@ -270,7 +269,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato // Check that user authentication related parameters are acceptable. This method // will throw an IllegalStateException if there are issues (e.g., secure lock screen // not set up). - KeymasterUtils.addUserAuthArgs(new KeymasterArguments(), mSpec); + KeyStore2ParameterUtils.addUserAuthArgs(new ArrayList<>(), mSpec); } catch (IllegalArgumentException | IllegalStateException e) { throw new InvalidAlgorithmParameterException(e); } @@ -572,7 +571,8 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato AndroidKeyStorePublicKey publicKey = AndroidKeyStoreProvider.makeAndroidKeyStorePublicKeyFromKeyEntryResponse( descriptor, metadata, iSecurityLevel, mKeymasterAlgorithm); - GenerateRkpKey keyGen = new GenerateRkpKey(KeyStore.getApplicationContext()); + GenerateRkpKey keyGen = new GenerateRkpKey(ActivityThread + .currentApplication()); try { if (mSpec.getAttestationChallenge() != null) { keyGen.notifyKeyGenerated(securityLevel); @@ -589,7 +589,8 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato case KeymasterDefs.KM_ERROR_HARDWARE_TYPE_UNAVAILABLE: throw new StrongBoxUnavailableException("Failed to generated key pair.", e); case ResponseCode.OUT_OF_KEYS: - GenerateRkpKey keyGen = new GenerateRkpKey(KeyStore.getApplicationContext()); + GenerateRkpKey keyGen = new GenerateRkpKey(ActivityThread + .currentApplication()); try { keyGen.notifyEmpty(securityLevel); } catch (RemoteException f) { @@ -665,8 +666,8 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato if (idTypesSet.contains(AttestationUtils.ID_TYPE_IMEI) || idTypesSet.contains(AttestationUtils.ID_TYPE_MEID)) { telephonyService = - (TelephonyManager) KeyStore.getApplicationContext().getSystemService( - Context.TELEPHONY_SERVICE); + (TelephonyManager) android.app.AppGlobals.getInitialApplication() + .getSystemService(Context.TELEPHONY_SERVICE); if (telephonyService == null) { throw new DeviceIdAttestationException("Unable to access telephony service"); } diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java index ba6d22f681ce..89d2b743a619 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java @@ -110,23 +110,6 @@ public class AndroidKeyStoreProvider extends Provider { putSecretKeyFactoryImpl("HmacSHA512"); } - private static boolean sInstalled = false; - - /** - * This function indicates whether or not this provider was installed. This is manly used - * as indicator for - * {@link android.security.keystore.AndroidKeyStoreProvider#getKeyStoreForUid(int)} - * to whether or not to retrieve the Keystore provider by "AndroidKeyStoreLegacy". - * This function can be removed once the transition to Keystore 2.0 is complete. - * b/171305684 - * - * @return true if this provider was installed. - * @hide - */ - public static boolean isInstalled() { - return sInstalled; - } - /** * Installs a new instance of this provider (and the * {@link AndroidKeyStoreBCWorkaroundProvider}). @@ -142,7 +125,6 @@ public class AndroidKeyStoreProvider extends Provider { break; } } - sInstalled = true; Security.addProvider(new AndroidKeyStoreProvider()); Provider workaroundProvider = new AndroidKeyStoreBCWorkaroundProvider(); diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java index 6ff9432905ed..5848247809e7 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java @@ -21,7 +21,6 @@ import android.annotation.Nullable; import android.hardware.security.keymint.KeyParameter; import android.security.keymaster.KeymasterDefs; import android.security.keystore.KeyProperties; -import android.security.keystore.KeymasterUtils; import android.system.keystore2.Authorization; import java.security.AlgorithmParameters; diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java index 32f98a2538f3..3e2fb94f0387 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java @@ -30,7 +30,6 @@ import android.security.keystore.KeyGenParameterSpec; import android.security.keystore.KeyPermanentlyInvalidatedException; import android.security.keystore.KeyProperties; import android.security.keystore.KeyProtection; -import android.security.keystore.KeymasterUtils; import android.security.keystore.SecureKeyImportUnavailableException; import android.security.keystore.WrappedKeyEntry; import android.system.keystore2.AuthenticatorSpec; diff --git a/keystore/java/android/security/keystore2/KeymasterUtils.java b/keystore/java/android/security/keystore2/KeymasterUtils.java new file mode 100644 index 000000000000..de4696cea3ac --- /dev/null +++ b/keystore/java/android/security/keystore2/KeymasterUtils.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2021 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.keystore2; + +import android.security.keymaster.KeymasterArguments; +import android.security.keymaster.KeymasterDefs; +import android.security.keystore.KeyProperties; + +import java.security.ProviderException; + +/** + * @hide + */ +public abstract class KeymasterUtils { + + private KeymasterUtils() {} + + /** @hide */ + static int getDigestOutputSizeBits(int keymasterDigest) { + switch (keymasterDigest) { + case KeymasterDefs.KM_DIGEST_NONE: + return -1; + case KeymasterDefs.KM_DIGEST_MD5: + return 128; + case KeymasterDefs.KM_DIGEST_SHA1: + return 160; + case KeymasterDefs.KM_DIGEST_SHA_2_224: + return 224; + case KeymasterDefs.KM_DIGEST_SHA_2_256: + return 256; + case KeymasterDefs.KM_DIGEST_SHA_2_384: + return 384; + case KeymasterDefs.KM_DIGEST_SHA_2_512: + return 512; + default: + throw new IllegalArgumentException("Unknown digest: " + keymasterDigest); + } + } + + /** @hide */ + static boolean isKeymasterBlockModeIndCpaCompatibleWithSymmetricCrypto( + int keymasterBlockMode) { + switch (keymasterBlockMode) { + case KeymasterDefs.KM_MODE_ECB: + return false; + case KeymasterDefs.KM_MODE_CBC: + case KeymasterDefs.KM_MODE_CTR: + case KeymasterDefs.KM_MODE_GCM: + return true; + default: + throw new IllegalArgumentException("Unsupported block mode: " + keymasterBlockMode); + } + } + + /** @hide */ + static boolean isKeymasterPaddingSchemeIndCpaCompatibleWithAsymmetricCrypto( + int keymasterPadding) { + switch (keymasterPadding) { + case KeymasterDefs.KM_PAD_NONE: + return false; + case KeymasterDefs.KM_PAD_RSA_OAEP: + case KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT: + return true; + default: + throw new IllegalArgumentException( + "Unsupported asymmetric encryption padding scheme: " + keymasterPadding); + } + } + + /** + * Adds {@code KM_TAG_MIN_MAC_LENGTH} tag, if necessary, to the keymaster arguments for + * generating or importing a key. This tag may only be needed for symmetric keys (e.g., HMAC, + * AES-GCM). + */ + public static void addMinMacLengthAuthorizationIfNecessary(KeymasterArguments args, + int keymasterAlgorithm, + int[] keymasterBlockModes, + int[] keymasterDigests) { + switch (keymasterAlgorithm) { + case KeymasterDefs.KM_ALGORITHM_AES: + if (com.android.internal.util.ArrayUtils.contains( + keymasterBlockModes, KeymasterDefs.KM_MODE_GCM)) { + // AES GCM key needs the minimum length of AEAD tag specified. + args.addUnsignedInt(KeymasterDefs.KM_TAG_MIN_MAC_LENGTH, + AndroidKeyStoreAuthenticatedAESCipherSpi.GCM + .MIN_SUPPORTED_TAG_LENGTH_BITS); + } + break; + case KeymasterDefs.KM_ALGORITHM_HMAC: + // HMAC key needs the minimum length of MAC set to the output size of the associated + // digest. This is because we do not offer a way to generate shorter MACs and + // don't offer a way to verify MACs (other than by generating them). + if (keymasterDigests.length != 1) { + throw new ProviderException( + "Unsupported number of authorized digests for HMAC key: " + + keymasterDigests.length + + ". Exactly one digest must be authorized"); + } + int keymasterDigest = keymasterDigests[0]; + int digestOutputSizeBits = getDigestOutputSizeBits(keymasterDigest); + if (digestOutputSizeBits == -1) { + throw new ProviderException( + "HMAC key authorized for unsupported digest: " + + KeyProperties.Digest.fromKeymaster(keymasterDigest)); + } + args.addUnsignedInt(KeymasterDefs.KM_TAG_MIN_MAC_LENGTH, digestOutputSizeBits); + break; + } + } +} |