diff options
Diffstat (limited to 'keystore/java/android')
12 files changed, 248 insertions, 20 deletions
diff --git a/keystore/java/android/security/AndroidKeyStoreMaintenance.java b/keystore/java/android/security/AndroidKeyStoreMaintenance.java index 35b1c169f283..72cea0cacd12 100644 --- a/keystore/java/android/security/AndroidKeyStoreMaintenance.java +++ b/keystore/java/android/security/AndroidKeyStoreMaintenance.java @@ -139,4 +139,18 @@ public class AndroidKeyStoreMaintenance { return SYSTEM_ERROR; } } + + /** + * 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) { + // TODO This fails open. This is not a regression with respect to keystore1 but it + // should get fixed. + Log.e(TAG, "Error while reporting device off body event.", e); + } + } } diff --git a/keystore/java/android/security/GenerateRkpKey.java b/keystore/java/android/security/GenerateRkpKey.java new file mode 100644 index 000000000000..a1a7aa85519f --- /dev/null +++ b/keystore/java/android/security/GenerateRkpKey.java @@ -0,0 +1,99 @@ +/* + * 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; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.IBinder; +import android.os.RemoteException; + +/** + * GenerateKey is a helper class to handle interactions between Keystore and the RemoteProvisioner + * app. There are two cases where Keystore should use this class. + * + * (1) : An app generates a new attested key pair, so Keystore calls notifyKeyGenerated to let the + * RemoteProvisioner app check if the state of the attestation key pool is getting low enough + * to warrant provisioning more attestation certificates early. + * + * (2) : An app attempts to generate a new key pair, but the keystore service discovers it is out of + * attestation key pairs and cannot provide one for the given application. Keystore can then + * make a blocking call on notifyEmpty to allow the RemoteProvisioner app to get another + * attestation certificate chain provisioned. + * + * In most cases, the proper usage of (1) should preclude the need for (2). + * + * @hide + */ +public class GenerateRkpKey { + + private IGenerateRkpKeyService mBinder; + private Context mContext; + + private ServiceConnection mConnection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName className, IBinder service) { + mBinder = IGenerateRkpKeyService.Stub.asInterface(service); + } + + @Override + public void onServiceDisconnected(ComponentName className) { + mBinder = null; + } + }; + + /** + * Constructor which takes a Context object. + */ + public GenerateRkpKey(Context context) { + mContext = context; + } + + /** + * Fulfills the use case of (2) described in the class documentation. Blocks until the + * RemoteProvisioner application can get new attestation keys signed by the server. + */ + public void notifyEmpty(int securityLevel) throws RemoteException { + Intent intent = new Intent(IGenerateRkpKeyService.class.getName()); + ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); + intent.setComponent(comp); + if (comp == null || !mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE)) { + throw new RemoteException("Failed to bind to GenerateKeyService"); + } + if (mBinder != null) { + mBinder.generateKey(securityLevel); + } + mContext.unbindService(mConnection); + } + + /** + * FUlfills the use case of (1) described in the class documentation. Non blocking call. + */ + public void notifyKeyGenerated(int securityLevel) throws RemoteException { + Intent intent = new Intent(IGenerateRkpKeyService.class.getName()); + ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); + intent.setComponent(comp); + if (comp == null || !mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE)) { + throw new RemoteException("Failed to bind to GenerateKeyService"); + } + if (mBinder != null) { + mBinder.notifyKeyGenerated(securityLevel); + } + mContext.unbindService(mConnection); + } +} diff --git a/keystore/java/android/security/GenerateRkpKeyException.java b/keystore/java/android/security/GenerateRkpKeyException.java new file mode 100644 index 000000000000..a2d65e4e7119 --- /dev/null +++ b/keystore/java/android/security/GenerateRkpKeyException.java @@ -0,0 +1,31 @@ +/* + * 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; + +/** + * Thrown on problems in attempting to attest to a key using a remotely provisioned key. + * + * @hide + */ +public class GenerateRkpKeyException extends Exception { + + /** + * Constructs a new {@code GenerateRkpKeyException}. + */ + public GenerateRkpKeyException() { + } +} diff --git a/keystore/java/android/security/IGenerateRkpKeyService.aidl b/keystore/java/android/security/IGenerateRkpKeyService.aidl new file mode 100644 index 000000000000..5f1d6693c23a --- /dev/null +++ b/keystore/java/android/security/IGenerateRkpKeyService.aidl @@ -0,0 +1,36 @@ +/** + * 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; + +/** + * Interface to allow the framework to notify the RemoteProvisioner app when keys are empty. This + * will be used if Keystore replies with an error code NO_KEYS_AVAILABLE in response to an + * attestation request. The framework can then synchronously call generateKey() to get more + * attestation keys generated and signed. Upon return, the caller can be certain an attestation key + * is available. + * + * @hide + */ +interface IGenerateRkpKeyService { + /** + * Ping the provisioner service to let it know an app generated a key. This may or may not have + * consumed a remotely provisioned attestation key, so the RemoteProvisioner app should check. + */ + oneway void notifyKeyGenerated(in int securityLevel); + /** Ping the provisioner service to indicate there are no remaining attestation keys left. */ + void generateKey(in int securityLevel); +} diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java index a08f390c9fd3..b05149ef75bc 100644 --- a/keystore/java/android/security/KeyStore.java +++ b/keystore/java/android/security/KeyStore.java @@ -1204,6 +1204,7 @@ public class KeyStore { * Notify keystore that the device went off-body. */ public void onDeviceOffBody() { + AndroidKeyStoreMaintenance.onDeviceOffBody(); try { mBinder.onDeviceOffBody(); } catch (RemoteException e) { diff --git a/keystore/java/android/security/KeyStore2.java b/keystore/java/android/security/KeyStore2.java index d0cad233c93e..df579bba9dc2 100644 --- a/keystore/java/android/security/KeyStore2.java +++ b/keystore/java/android/security/KeyStore2.java @@ -18,8 +18,7 @@ package android.security; import android.annotation.NonNull; import android.compat.annotation.ChangeId; -import android.compat.annotation.EnabledAfter; -import android.os.Build; +import android.compat.annotation.Disabled; import android.os.RemoteException; import android.os.ServiceManager; import android.os.ServiceSpecificException; @@ -86,7 +85,7 @@ public class KeyStore2 { * successfully conclude an operation. */ @ChangeId - @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R) + @Disabled // See b/180133780 static final long KEYSTORE_OPERATION_CREATION_MAY_FAIL = 169897160L; // Never use mBinder directly, use KeyStore2.getService() instead or better yet diff --git a/keystore/java/android/security/keystore/AttestationUtils.java b/keystore/java/android/security/keystore/AttestationUtils.java index 11c36893d984..1eb854187b32 100644 --- a/keystore/java/android/security/keystore/AttestationUtils.java +++ b/keystore/java/android/security/keystore/AttestationUtils.java @@ -287,6 +287,8 @@ public abstract class AttestationUtils { keyStore.deleteEntry(keystoreAlias); return certificateChain; + } catch (SecurityException e) { + throw e; } catch (Exception e) { throw new DeviceIdAttestationException("Unable to perform attestation", e); } diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java index 5cb2c3b41517..9ca551b26aab 100644 --- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java +++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java @@ -288,7 +288,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu private static final Date DEFAULT_CERT_NOT_AFTER = new Date(2461449600000L); // Jan 1 2048 private final String mKeystoreAlias; - private final int mNamespace; + private final @KeyProperties.Namespace int mNamespace; private final int mKeySize; private final AlgorithmParameterSpec mSpec; private final X500Principal mCertificateSubject; @@ -331,7 +331,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu */ public KeyGenParameterSpec( String keyStoreAlias, - int namespace, + @KeyProperties.Namespace int namespace, int keySize, AlgorithmParameterSpec spec, X500Principal certificateSubject, @@ -472,7 +472,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu * @hide */ @SystemApi - public int getNamespace() { + public @KeyProperties.Namespace int getNamespace() { return mNamespace; } @@ -896,7 +896,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu private final String mKeystoreAlias; private @KeyProperties.PurposeEnum int mPurposes; - private int mNamespace = KeyProperties.NAMESPACE_APPLICATION; + private @KeyProperties.Namespace int mNamespace = KeyProperties.NAMESPACE_APPLICATION; private int mKeySize = -1; private AlgorithmParameterSpec mSpec; private X500Principal mCertificateSubject; @@ -1051,7 +1051,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu */ @SystemApi @NonNull - public Builder setNamespace(int namespace) { + public Builder setNamespace(@KeyProperties.Namespace int namespace) { mNamespace = namespace; return this; } diff --git a/keystore/java/android/security/keystore/KeyProperties.java b/keystore/java/android/security/keystore/KeyProperties.java index 7b0fa91380e1..682d12a65ea3 100644 --- a/keystore/java/android/security/keystore/KeyProperties.java +++ b/keystore/java/android/security/keystore/KeyProperties.java @@ -892,6 +892,22 @@ public abstract class KeyProperties { } /** + * Namespaces provide system developers and vendors with a way to use keystore without + * requiring an applications uid. Namespaces can be configured using SEPolicy. + * See <a href="https://source.android.com/security/keystore#access-control"> + * Keystore 2.0 access-control</a> + * {@See KeyGenParameterSpec.Builder#setNamespace} + * {@See android.security.keystore2.AndroidKeyStoreLoadStoreParameter} + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "NAMESPACE_" }, value = { + NAMESPACE_APPLICATION, + NAMESPACE_WIFI, + }) + public @interface Namespace {} + + /** * This value indicates the implicit keystore namespace of the calling application. * It is used by default. Only select system components can choose a different namespace * which it must be configured in SEPolicy. @@ -912,14 +928,12 @@ public abstract class KeyProperties { * For legacy support, translate namespaces into known UIDs. * @hide */ - public static int namespaceToLegacyUid(int namespace) { + public static int namespaceToLegacyUid(@Namespace int namespace) { switch (namespace) { case NAMESPACE_APPLICATION: return KeyStore.UID_SELF; case NAMESPACE_WIFI: return Process.WIFI_UID; - // TODO Translate WIFI and VPN UIDs once the namespaces are defined. - // b/171305388 and b/171305607 default: throw new IllegalArgumentException("No UID corresponding to namespace " + namespace); @@ -930,14 +944,12 @@ public abstract class KeyProperties { * For legacy support, translate namespaces into known UIDs. * @hide */ - public static int legacyUidToNamespace(int uid) { + public static @Namespace int legacyUidToNamespace(int uid) { switch (uid) { case KeyStore.UID_SELF: return NAMESPACE_APPLICATION; case Process.WIFI_UID: return NAMESPACE_WIFI; - // TODO Translate WIFI and VPN UIDs once the namespaces are defined. - // b/171305388 and b/171305607 default: throw new IllegalArgumentException("No namespace corresponding to uid " + uid); diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java index e401add9ece7..2d8901a37c05 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java @@ -24,6 +24,9 @@ import android.hardware.security.keymint.KeyPurpose; import android.hardware.security.keymint.SecurityLevel; import android.hardware.security.keymint.Tag; import android.os.Build; +import android.os.RemoteException; +import android.security.GenerateRkpKey; +import android.security.GenerateRkpKeyException; import android.security.KeyPairGeneratorSpec; import android.security.KeyStore; import android.security.KeyStore2; @@ -520,6 +523,18 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato @Override public KeyPair generateKeyPair() { + try { + return generateKeyPairHelper(); + } catch (GenerateRkpKeyException e) { + try { + return generateKeyPairHelper(); + } catch (GenerateRkpKeyException f) { + throw new ProviderException("Failed to provision new attestation keys."); + } + } + } + + private KeyPair generateKeyPairHelper() throws GenerateRkpKeyException { if (mKeyStore == null || mSpec == null) { throw new IllegalStateException("Not initialized"); } @@ -557,13 +572,30 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato AndroidKeyStorePublicKey publicKey = AndroidKeyStoreProvider.makeAndroidKeyStorePublicKeyFromKeyEntryResponse( descriptor, metadata, iSecurityLevel, mKeymasterAlgorithm); - + GenerateRkpKey keyGen = new GenerateRkpKey(KeyStore.getApplicationContext()); + try { + if (mSpec.getAttestationChallenge() != null) { + keyGen.notifyKeyGenerated(securityLevel); + } + } catch (RemoteException e) { + // This is not really an error state, and necessarily does not apply to non RKP + // systems or hybrid systems where RKP is not currently turned on. + Log.d(TAG, "Couldn't connect to the RemoteProvisioner backend."); + } success = true; return new KeyPair(publicKey, publicKey.getPrivateKey()); } catch (android.security.KeyStoreException e) { switch(e.getErrorCode()) { 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()); + try { + keyGen.notifyEmpty(securityLevel); + } catch (RemoteException f) { + throw new ProviderException("Failed to talk to RemoteProvisioner", f); + } + throw new GenerateRkpKeyException(); default: ProviderException p = new ProviderException("Failed to generate key pair.", e); if ((mSpec.getPurposes() & KeyProperties.PURPOSE_WRAP_KEY) != 0) { diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreLoadStoreParameter.java b/keystore/java/android/security/keystore2/AndroidKeyStoreLoadStoreParameter.java index 0c6744f9822c..25b1c864b5d1 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreLoadStoreParameter.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreLoadStoreParameter.java @@ -16,6 +16,8 @@ package android.security.keystore2; +import android.security.keystore.KeyProperties; + import java.security.KeyStore; import java.security.KeyStore.ProtectionParameter; @@ -24,9 +26,9 @@ import java.security.KeyStore.ProtectionParameter; */ public class AndroidKeyStoreLoadStoreParameter implements KeyStore.LoadStoreParameter { - private final int mNamespace; + private final @KeyProperties.Namespace int mNamespace; - public AndroidKeyStoreLoadStoreParameter(int namespace) { + public AndroidKeyStoreLoadStoreParameter(@KeyProperties.Namespace int namespace) { mNamespace = namespace; } @@ -35,7 +37,7 @@ public class AndroidKeyStoreLoadStoreParameter implements KeyStore.LoadStorePara return null; } - int getNamespace() { + @KeyProperties.Namespace int getNamespace() { return mNamespace; } } diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java index 39607aeb3852..32f98a2538f3 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java @@ -100,7 +100,7 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi { public static final String NAME = "AndroidKeyStore"; private KeyStore2 mKeyStore; - private int mNamespace = KeyProperties.NAMESPACE_APPLICATION; + private @KeyProperties.Namespace int mNamespace = KeyProperties.NAMESPACE_APPLICATION; @Override public Key engineGetKey(String alias, char[] password) throws NoSuchAlgorithmException, @@ -1125,7 +1125,7 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi { @Override public void engineLoad(LoadStoreParameter param) throws IOException, NoSuchAlgorithmException, CertificateException { - int namespace = KeyProperties.NAMESPACE_APPLICATION; + @KeyProperties.Namespace int namespace = KeyProperties.NAMESPACE_APPLICATION; if (param != null) { if (param instanceof AndroidKeyStoreLoadStoreParameter) { namespace = ((AndroidKeyStoreLoadStoreParameter) param).getNamespace(); |