diff --git a/api/system-current.txt b/api/system-current.txt index fe8af623e23b5..8e108a71a8a8e 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -38405,6 +38405,18 @@ package android.security { package android.security.keystore { + public abstract class AttestationUtils { + method public static java.security.cert.X509Certificate[] attestDeviceIds(android.content.Context, int[], byte[]) throws android.security.keystore.DeviceIdAttestationException; + field public static final int ID_TYPE_IMEI = 2; // 0x2 + field public static final int ID_TYPE_MEID = 3; // 0x3 + field public static final int ID_TYPE_SERIAL = 1; // 0x1 + } + + public class DeviceIdAttestationException extends java.lang.Exception { + ctor public DeviceIdAttestationException(java.lang.String); + ctor public DeviceIdAttestationException(java.lang.String, java.lang.Throwable); + } + public class KeyExpiredException extends java.security.InvalidKeyException { ctor public KeyExpiredException(); ctor public KeyExpiredException(java.lang.String); diff --git a/api/test-current.txt b/api/test-current.txt index 028c67e38d6c6..bafbf30e0e22b 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -35522,6 +35522,18 @@ package android.security { package android.security.keystore { + public abstract class AttestationUtils { + method public static java.security.cert.X509Certificate[] attestDeviceIds(android.content.Context, int[], byte[]) throws android.security.keystore.DeviceIdAttestationException; + field public static final int ID_TYPE_IMEI = 2; // 0x2 + field public static final int ID_TYPE_MEID = 3; // 0x3 + field public static final int ID_TYPE_SERIAL = 1; // 0x1 + } + + public class DeviceIdAttestationException extends java.lang.Exception { + ctor public DeviceIdAttestationException(java.lang.String); + ctor public DeviceIdAttestationException(java.lang.String, java.lang.Throwable); + } + public class KeyExpiredException extends java.security.InvalidKeyException { ctor public KeyExpiredException(); ctor public KeyExpiredException(java.lang.String); diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java index 22f1bedc6072a..5461e6b6d699b 100644 --- a/core/java/android/security/keymaster/KeymasterDefs.java +++ b/core/java/android/security/keymaster/KeymasterDefs.java @@ -83,6 +83,12 @@ public final class KeymasterDefs { public static final int KM_TAG_ROOT_OF_TRUST = KM_BYTES | 704; public static final int KM_TAG_UNIQUE_ID = KM_BYTES | 707; public static final int KM_TAG_ATTESTATION_CHALLENGE = KM_BYTES | 708; + public static final int KM_TAG_ATTESTATION_ID_BRAND = KM_BYTES | 710; + public static final int KM_TAG_ATTESTATION_ID_DEVICE = KM_BYTES | 711; + public static final int KM_TAG_ATTESTATION_ID_PRODUCT = KM_BYTES | 712; + public static final int KM_TAG_ATTESTATION_ID_SERIAL = KM_BYTES | 713; + public static final int KM_TAG_ATTESTATION_ID_IMEI = KM_BYTES | 714; + public static final int KM_TAG_ATTESTATION_ID_MEID = KM_BYTES | 715; public static final int KM_TAG_ASSOCIATED_DATA = KM_BYTES | 1000; public static final int KM_TAG_NONCE = KM_BYTES | 1001; @@ -204,6 +210,7 @@ public final class KeymasterDefs { public static final int KM_ERROR_INVALID_MAC_LENGTH = -57; public static final int KM_ERROR_MISSING_MIN_MAC_LENGTH = -58; public static final int KM_ERROR_UNSUPPORTED_MIN_MAC_LENGTH = -59; + public static final int KM_ERROR_CANNOT_ATTEST_IDS = -66; public static final int KM_ERROR_UNIMPLEMENTED = -100; public static final int KM_ERROR_VERSION_MISMATCH = -101; public static final int KM_ERROR_UNKNOWN_ERROR = -1000; @@ -249,6 +256,7 @@ public final class KeymasterDefs { "Caller-provided IV not permitted"); sErrorCodeToString.put(KM_ERROR_INVALID_MAC_LENGTH, "Invalid MAC or authentication tag length"); + sErrorCodeToString.put(KM_ERROR_CANNOT_ATTEST_IDS, "Unable to attest device ids"); sErrorCodeToString.put(KM_ERROR_UNIMPLEMENTED, "Not implemented"); sErrorCodeToString.put(KM_ERROR_UNKNOWN_ERROR, "Unknown error"); } diff --git a/keystore/java/android/security/keystore/AttestationUtils.java b/keystore/java/android/security/keystore/AttestationUtils.java new file mode 100644 index 0000000000000..e36632a591eb4 --- /dev/null +++ b/keystore/java/android/security/keystore/AttestationUtils.java @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2017 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.Manifest; +import android.annotation.NonNull; +import android.annotation.RequiresPermission; +import android.annotation.SystemApi; +import android.annotation.TestApi; +import android.content.Context; +import android.os.Build; +import android.os.Process; +import android.security.KeyStore; +import android.security.KeyStoreException; +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 java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.nio.charset.StandardCharsets; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.security.spec.RSAKeyGenParameterSpec; +import java.util.Collection; +import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Utilities for attesting the device's hardware identifiers. + * + * @hide + */ +@SystemApi +@TestApi +public abstract class AttestationUtils { + private static AtomicInteger sSequenceNumber = new AtomicInteger(0); + + private AttestationUtils() { + } + + /** + * Specifies that the device should attest its serial number. For use with + * {@link #attestDeviceIds}. + * + * @see #attestDeviceIds + */ + public static final int ID_TYPE_SERIAL = 1; + + /** + * Specifies that the device should attest its IMEIs. For use with {@link #attestDeviceIds}. + * + * @see #attestDeviceIds + */ + public static final int ID_TYPE_IMEI = 2; + + /** + * Specifies that the device should attest its MEIDs. For use with {@link #attestDeviceIds}. + * + * @see #attestDeviceIds + */ + public static final int ID_TYPE_MEID = 3; + + /** + * Performs attestation of the device's identifiers. This method returns a certificate chain + * whose first element contains the requested device identifiers in an extension. The device's + * brand, device and product are always also included in the attestation. If the device supports + * attestation in secure hardware, the chain will be rooted at a trustworthy CA key. Otherwise, + * the chain will be rooted at an untrusted certificate. See + * + * Key Attestation for the format of the certificate extension. + *
+ * Attestation will only be successful when all of the following are true: + * 1) The device has been set up to support device identifier attestation at the factory. + * 2) The user has not permanently disabled device identifier attestation. + * 3) You have permission to access the device identifiers you are requesting attestation for. + *
+ * For privacy reasons, you cannot distinguish between (1) and (2). If attestation is + * unsuccessful, the device may not support it in general or the user may have permanently + * disabled it. + *
+ * The caller must hold {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE}
+ * permission.
+ *
+ * @param context the context to use for retrieving device identifiers.
+ * @param idTypes the types of device identifiers to attest.
+ * @param attestationChallenge a blob to include in the certificate alongside the device
+ * identifiers.
+ *
+ * @return a certificate chain containing the requested device identifiers in the first element
+ *
+ * @exception SecurityException if you are not permitted to obtain an attestation of the
+ * device's identifiers.
+ * @exception DeviceIdAttestationException if the attestation operation fails.
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @NonNull public static X509Certificate[] attestDeviceIds(Context context,
+ @NonNull int[] idTypes, @NonNull byte[] attestationChallenge) throws
+ DeviceIdAttestationException {
+ // Check method arguments, retrieve requested device IDs and prepare attestation arguments.
+ if (idTypes == null) {
+ throw new NullPointerException("Missing id types");
+ }
+ if (attestationChallenge == null) {
+ throw new NullPointerException("Missing attestation challenge");
+ }
+ final KeymasterArguments attestArgs = new KeymasterArguments();
+ attestArgs.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_CHALLENGE, attestationChallenge);
+ final Set