diff --git a/core/api/current.txt b/core/api/current.txt index ce7af52b1ecd9..376bab815b2d3 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -12429,6 +12429,7 @@ package android.content.pm { field public static final String FEATURE_INPUT_METHODS = "android.software.input_methods"; field public static final String FEATURE_IPSEC_TUNNELS = "android.software.ipsec_tunnels"; field public static final String FEATURE_IRIS = "android.hardware.biometrics.iris"; + field public static final String FEATURE_KEYSTORE_APP_ATTEST_KEY = "android.hardware.keystore.app_attest_key"; field public static final String FEATURE_KEYSTORE_LIMITED_USE_KEY = "android.hardware.keystore.limited_use_key"; field public static final String FEATURE_KEYSTORE_SINGLE_USE_KEY = "android.hardware.keystore.single_use_key"; field public static final String FEATURE_LEANBACK = "android.software.leanback"; @@ -37090,6 +37091,7 @@ package android.security.keystore { public final class KeyGenParameterSpec implements java.security.spec.AlgorithmParameterSpec { method @Nullable public java.security.spec.AlgorithmParameterSpec getAlgorithmParameterSpec(); + method @Nullable public String getAttestKeyAlias(); method public byte[] getAttestationChallenge(); method @NonNull public String[] getBlockModes(); method @NonNull public java.util.Date getCertificateNotAfter(); @@ -37124,6 +37126,7 @@ package android.security.keystore { ctor public KeyGenParameterSpec.Builder(@NonNull String, int); method @NonNull public android.security.keystore.KeyGenParameterSpec build(); method public android.security.keystore.KeyGenParameterSpec.Builder setAlgorithmParameterSpec(@NonNull java.security.spec.AlgorithmParameterSpec); + method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setAttestKeyAlias(@Nullable String); method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setAttestationChallenge(byte[]); method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setBlockModes(java.lang.String...); method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setCertificateNotAfter(@NonNull java.util.Date); @@ -37221,6 +37224,7 @@ package android.security.keystore { field public static final int ORIGIN_SECURELY_IMPORTED = 8; // 0x8 field public static final int ORIGIN_UNKNOWN = 4; // 0x4 field public static final int PURPOSE_AGREE_KEY = 64; // 0x40 + field public static final int PURPOSE_ATTEST_KEY = 128; // 0x80 field public static final int PURPOSE_DECRYPT = 2; // 0x2 field public static final int PURPOSE_ENCRYPT = 1; // 0x1 field public static final int PURPOSE_SIGN = 4; // 0x4 diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 13b6b1d1235a5..fdb00c690ffe9 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -3669,6 +3669,15 @@ public abstract class PackageManager { public static final String FEATURE_KEYSTORE_LIMITED_USE_KEY = "android.hardware.keystore.limited_use_key"; + /** + * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device has + * a Keystore implementation that can create application-specific attestation keys. + * See {@link android.security.keystore.KeyGenParameterSpec.Builder#setAttestKeyAlias}. + */ + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_KEYSTORE_APP_ATTEST_KEY = + "android.hardware.keystore.app_attest_key"; + /** @hide */ public static final boolean APP_ENUMERATION_ENABLED_BY_DEFAULT = true; diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java index a79b197d3faa4..5a89cdf1d3404 100644 --- a/core/java/android/security/keymaster/KeymasterDefs.java +++ b/core/java/android/security/keymaster/KeymasterDefs.java @@ -188,6 +188,7 @@ public final class KeymasterDefs { public static final int KM_PURPOSE_VERIFY = KeyPurpose.VERIFY; public static final int KM_PURPOSE_WRAP = KeyPurpose.WRAP_KEY; public static final int KM_PURPOSE_AGREE_KEY = KeyPurpose.AGREE_KEY; + public static final int KM_PURPOSE_ATTEST_KEY = KeyPurpose.ATTEST_KEY; // Key formats. public static final int KM_KEY_FORMAT_X509 = KeyFormat.X509; diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java index 2b0d7e53b749f..c79c12cd33434 100644 --- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java +++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java @@ -279,8 +279,8 @@ import javax.security.auth.x500.X500Principal; * } */ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAuthArgs { - - private static final X500Principal DEFAULT_CERT_SUBJECT = new X500Principal("CN=fake"); + private static final X500Principal DEFAULT_CERT_SUBJECT = + new X500Principal("CN=Android Keystore Key"); private static final BigInteger DEFAULT_CERT_SERIAL_NUMBER = new BigInteger("1"); private static final Date DEFAULT_CERT_NOT_BEFORE = new Date(0L); // Jan 1 1970 private static final Date DEFAULT_CERT_NOT_AFTER = new Date(2461449600000L); // Jan 1 2048 @@ -317,6 +317,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu private final boolean mUnlockedDeviceRequired; private final boolean mCriticalToDeviceEncryption; private final int mMaxUsageCount; + private final String mAttestKeyAlias; /* * ***NOTE***: All new fields MUST also be added to the following: * ParcelableKeyGenParameterSpec class. @@ -358,7 +359,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu boolean userConfirmationRequired, boolean unlockedDeviceRequired, boolean criticalToDeviceEncryption, - int maxUsageCount) { + int maxUsageCount, + String attestKeyAlias) { if (TextUtils.isEmpty(keyStoreAlias)) { throw new IllegalArgumentException("keyStoreAlias must not be empty"); } @@ -413,6 +415,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu mUnlockedDeviceRequired = unlockedDeviceRequired; mCriticalToDeviceEncryption = criticalToDeviceEncryption; mMaxUsageCount = maxUsageCount; + mAttestKeyAlias = attestKeyAlias; } /** @@ -868,6 +871,18 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu return mMaxUsageCount; } + /** + * Returns the alias of the attestation key that will be used to sign the attestation + * certificate of the generated key. Note that an attestation certificate will only be + * generated if an attestation challenge is set. + * + * @see Builder#setAttestKeyAlias(String) + */ + @Nullable + public String getAttestKeyAlias() { + return mAttestKeyAlias; + } + /** * Builder of {@link KeyGenParameterSpec} instances. */ @@ -906,6 +921,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu private boolean mUnlockedDeviceRequired = false; private boolean mCriticalToDeviceEncryption = false; private int mMaxUsageCount = KeyProperties.UNRESTRICTED_USAGE_COUNT; + private String mAttestKeyAlias = null; /** * Creates a new instance of the {@code Builder}. @@ -975,6 +991,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu mUnlockedDeviceRequired = sourceSpec.isUnlockedDeviceRequired(); mCriticalToDeviceEncryption = sourceSpec.isCriticalToDeviceEncryption(); mMaxUsageCount = sourceSpec.getMaxUsageCount(); + mAttestKeyAlias = sourceSpec.getAttestKeyAlias(); } /** @@ -1694,6 +1711,28 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu throw new IllegalArgumentException("maxUsageCount is not valid"); } + /** + * Sets the alias of the attestation key that will be used to sign the attestation + * certificate for the generated key pair, if an attestation challenge is set with {@link + * #setAttestationChallenge}. If an attestKeyAlias is set but no challenge, {@link + * java.security.KeyPairGenerator#initialize} will throw {@link + * java.security.InvalidAlgorithmParameterException}. + * + *
If the attestKeyAlias is set to null (the default), Android Keystore will select an
+ * appropriate system-provided attestation signing key. If not null, the alias must
+ * reference an Android Keystore Key that was created with {@link
+ * android.security.keystore.KeyProperties#PURPOSE_ATTEST_KEY}, or key generation will throw
+ * {@link java.security.InvalidAlgorithmParameterException}.
+ *
+ * @param attestKeyAlias the alias of the attestation key to be used to sign the
+ * attestation certificate.
+ */
+ @NonNull
+ public Builder setAttestKeyAlias(@Nullable String attestKeyAlias) {
+ mAttestKeyAlias = attestKeyAlias;
+ return this;
+ }
+
/**
* Builds an instance of {@code KeyGenParameterSpec}.
*/
@@ -1731,7 +1770,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
mUserConfirmationRequired,
mUnlockedDeviceRequired,
mCriticalToDeviceEncryption,
- mMaxUsageCount);
+ mMaxUsageCount,
+ mAttestKeyAlias);
}
}
}
diff --git a/keystore/java/android/security/keystore/KeyProperties.java b/keystore/java/android/security/keystore/KeyProperties.java
index 293ab05e0b10c..7b0fa91380e15 100644
--- a/keystore/java/android/security/keystore/KeyProperties.java
+++ b/keystore/java/android/security/keystore/KeyProperties.java
@@ -70,6 +70,7 @@ public abstract class KeyProperties {
PURPOSE_VERIFY,
PURPOSE_WRAP_KEY,
PURPOSE_AGREE_KEY,
+ PURPOSE_ATTEST_KEY,
})
public @interface PurposeEnum {}
@@ -112,6 +113,13 @@ public abstract class KeyProperties {
*/
public static final int PURPOSE_AGREE_KEY = 1 << 6;
+ /**
+ * Purpose of key: Signing attestaions. This purpose is incompatible with all others, meaning
+ * that when generating a key with PURPOSE_ATTEST_KEY, no other purposes may be specified. In
+ * addition, PURPOSE_ATTEST_KEY may not be specified for imported keys.
+ */
+ public static final int PURPOSE_ATTEST_KEY = 1 << 7;
+
/**
* @hide
*/
@@ -132,6 +140,8 @@ public abstract class KeyProperties {
return KeymasterDefs.KM_PURPOSE_WRAP;
case PURPOSE_AGREE_KEY:
return KeymasterDefs.KM_PURPOSE_AGREE_KEY;
+ case PURPOSE_ATTEST_KEY:
+ return KeymasterDefs.KM_PURPOSE_ATTEST_KEY;
default:
throw new IllegalArgumentException("Unknown purpose: " + purpose);
}
@@ -151,6 +161,8 @@ public abstract class KeyProperties {
return PURPOSE_WRAP_KEY;
case KeymasterDefs.KM_PURPOSE_AGREE_KEY:
return PURPOSE_AGREE_KEY;
+ case KeymasterDefs.KM_PURPOSE_ATTEST_KEY:
+ return PURPOSE_ATTEST_KEY;
default:
throw new IllegalArgumentException("Unknown purpose: " + purpose);
}
diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java
index aaa37150c0e9b..fe92270ca508d 100644
--- a/keystore/java/android/security/keystore/KeyProtection.java
+++ b/keystore/java/android/security/keystore/KeyProtection.java
@@ -588,7 +588,8 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs {
private long mBoundToSecureUserId = GateKeeper.INVALID_SECURE_USER_ID;
private boolean mCriticalToDeviceEncryption = false;
private boolean mIsStrongBoxBacked = false;
- private int mMaxUsageCount = KeyProperties.UNRESTRICTED_USAGE_COUNT;
+ private int mMaxUsageCount = KeyProperties.UNRESTRICTED_USAGE_COUNT;
+ private String mAttestKeyAlias = null;
/**
* Creates a new instance of the {@code Builder}.
diff --git a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
index 1f2f853b67a88..c20cf01a993ec 100644
--- a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
@@ -110,6 +110,7 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable {
out.writeBoolean(mSpec.isUnlockedDeviceRequired());
out.writeBoolean(mSpec.isCriticalToDeviceEncryption());
out.writeInt(mSpec.getMaxUsageCount());
+ out.writeString(mSpec.getAttestKeyAlias());
}
private static Date readDateOrNull(Parcel in) {
@@ -170,6 +171,7 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable {
final boolean unlockedDeviceRequired = in.readBoolean();
final boolean criticalToDeviceEncryption = in.readBoolean();
final int maxUsageCount = in.readInt();
+ final String attestKeyAlias = in.readString();
// The KeyGenParameterSpec is intentionally not constructed using a Builder here:
// The intention is for this class to break if new parameters are added to the
// KeyGenParameterSpec constructor (whereas using a builder would silently drop them).
@@ -205,7 +207,8 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable {
userConfirmationRequired,
unlockedDeviceRequired,
criticalToDeviceEncryption,
- maxUsageCount);
+ maxUsageCount,
+ attestKeyAlias);
}
public static final @android.annotation.NonNull Creator