Merge "Add attestation API to Android KeyStore." into nyc-dev
diff --git a/api/current.txt b/api/current.txt
index 40ead48..b9f932b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -34045,6 +34045,7 @@
 
   public final class KeyGenParameterSpec implements java.security.spec.AlgorithmParameterSpec {
     method public java.security.spec.AlgorithmParameterSpec getAlgorithmParameterSpec();
+    method public byte[] getAttestationChallenge();
     method public java.lang.String[] getBlockModes();
     method public java.util.Date getCertificateNotAfter();
     method public java.util.Date getCertificateNotBefore();
@@ -34069,6 +34070,7 @@
     ctor public KeyGenParameterSpec.Builder(java.lang.String, int);
     method public android.security.keystore.KeyGenParameterSpec build();
     method public android.security.keystore.KeyGenParameterSpec.Builder setAlgorithmParameterSpec(java.security.spec.AlgorithmParameterSpec);
+    method public android.security.keystore.KeyGenParameterSpec.Builder setAttestationChallenge(byte[]);
     method public android.security.keystore.KeyGenParameterSpec.Builder setBlockModes(java.lang.String...);
     method public android.security.keystore.KeyGenParameterSpec.Builder setCertificateNotAfter(java.util.Date);
     method public android.security.keystore.KeyGenParameterSpec.Builder setCertificateNotBefore(java.util.Date);
diff --git a/api/system-current.txt b/api/system-current.txt
index dfe1e08..b404bb0 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -36528,6 +36528,7 @@
 
   public final class KeyGenParameterSpec implements java.security.spec.AlgorithmParameterSpec {
     method public java.security.spec.AlgorithmParameterSpec getAlgorithmParameterSpec();
+    method public byte[] getAttestationChallenge();
     method public java.lang.String[] getBlockModes();
     method public java.util.Date getCertificateNotAfter();
     method public java.util.Date getCertificateNotBefore();
@@ -36552,6 +36553,7 @@
     ctor public KeyGenParameterSpec.Builder(java.lang.String, int);
     method public android.security.keystore.KeyGenParameterSpec build();
     method public android.security.keystore.KeyGenParameterSpec.Builder setAlgorithmParameterSpec(java.security.spec.AlgorithmParameterSpec);
+    method public android.security.keystore.KeyGenParameterSpec.Builder setAttestationChallenge(byte[]);
     method public android.security.keystore.KeyGenParameterSpec.Builder setBlockModes(java.lang.String...);
     method public android.security.keystore.KeyGenParameterSpec.Builder setCertificateNotAfter(java.util.Date);
     method public android.security.keystore.KeyGenParameterSpec.Builder setCertificateNotBefore(java.util.Date);
diff --git a/api/test-current.txt b/api/test-current.txt
index 10733f9..9f8b8a8 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -34060,6 +34060,7 @@
 
   public final class KeyGenParameterSpec implements java.security.spec.AlgorithmParameterSpec {
     method public java.security.spec.AlgorithmParameterSpec getAlgorithmParameterSpec();
+    method public byte[] getAttestationChallenge();
     method public java.lang.String[] getBlockModes();
     method public java.util.Date getCertificateNotAfter();
     method public java.util.Date getCertificateNotBefore();
@@ -34084,6 +34085,7 @@
     ctor public KeyGenParameterSpec.Builder(java.lang.String, int);
     method public android.security.keystore.KeyGenParameterSpec build();
     method public android.security.keystore.KeyGenParameterSpec.Builder setAlgorithmParameterSpec(java.security.spec.AlgorithmParameterSpec);
+    method public android.security.keystore.KeyGenParameterSpec.Builder setAttestationChallenge(byte[]);
     method public android.security.keystore.KeyGenParameterSpec.Builder setBlockModes(java.lang.String...);
     method public android.security.keystore.KeyGenParameterSpec.Builder setCertificateNotAfter(java.util.Date);
     method public android.security.keystore.KeyGenParameterSpec.Builder setCertificateNotBefore(java.util.Date);
diff --git a/core/java/android/security/IKeystoreService.aidl b/core/java/android/security/IKeystoreService.aidl
index 7cf1d71..8689dce 100644
--- a/core/java/android/security/IKeystoreService.aidl
+++ b/core/java/android/security/IKeystoreService.aidl
@@ -19,6 +19,7 @@
 import android.security.keymaster.ExportResult;
 import android.security.keymaster.KeyCharacteristics;
 import android.security.keymaster.KeymasterArguments;
+import android.security.keymaster.KeymasterCertificateChain;
 import android.security.keymaster.KeymasterBlob;
 import android.security.keymaster.OperationResult;
 import android.security.KeystoreArguments;
@@ -74,4 +75,5 @@
     int addAuthToken(in byte[] authToken);
     int onUserAdded(int userId, int parentId);
     int onUserRemoved(int userId);
+    int attestKey(String alias, in KeymasterArguments params, out KeymasterCertificateChain chain);
 }
diff --git a/core/java/android/security/keymaster/KeymasterCertificateChain.aidl b/core/java/android/security/keymaster/KeymasterCertificateChain.aidl
new file mode 100644
index 0000000..dc1876a
--- /dev/null
+++ b/core/java/android/security/keymaster/KeymasterCertificateChain.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2016 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.keymaster;
+
+/* @hide */
+parcelable KeymasterCertificateChain;
diff --git a/core/java/android/security/keymaster/KeymasterCertificateChain.java b/core/java/android/security/keymaster/KeymasterCertificateChain.java
new file mode 100644
index 0000000..243b9fe
--- /dev/null
+++ b/core/java/android/security/keymaster/KeymasterCertificateChain.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2016 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.keymaster;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Utility class for the Java side of keystore-generated certificate chains.
+ *
+ * Serialization code for this must be kept in sync with system/security/keystore
+ * @hide
+ */
+public class KeymasterCertificateChain implements Parcelable {
+
+    private List<byte[]> mCertificates;
+
+    public static final Parcelable.Creator<KeymasterCertificateChain> CREATOR = new
+            Parcelable.Creator<KeymasterCertificateChain>() {
+                public KeymasterCertificateChain createFromParcel(Parcel in) {
+                    return new KeymasterCertificateChain(in);
+                }
+                public KeymasterCertificateChain[] newArray(int size) {
+                    return new KeymasterCertificateChain[size];
+                }
+            };
+
+    public KeymasterCertificateChain() {
+        mCertificates = null;
+    }
+
+    public KeymasterCertificateChain(List<byte[]> mCertificates) {
+        this.mCertificates = mCertificates;
+    }
+
+    private KeymasterCertificateChain(Parcel in) {
+        readFromParcel(in);
+    }
+
+    public List<byte[]> getCertificates() {
+        return mCertificates;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        if (mCertificates == null) {
+            out.writeInt(0);
+        } else {
+            out.writeInt(mCertificates.size());
+            for (byte[] arg : mCertificates) {
+                out.writeByteArray(arg);
+            }
+        }
+    }
+
+    public void readFromParcel(Parcel in) {
+        int length = in.readInt();
+        mCertificates = new ArrayList<byte[]>(length);
+        for (int i = 0; i < length; i++) {
+            mCertificates.add(in.createByteArray());
+        }
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+}
diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java
index 04d5952..e01f2a0 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -58,6 +58,8 @@
     public static final int KM_TAG_BLOB_USAGE_REQUIREMENTS = KM_ENUM | 705;
 
     public static final int KM_TAG_RSA_PUBLIC_EXPONENT = KM_ULONG | 200;
+    public static final int KM_TAG_INCLUDE_UNIQUE_ID = KM_BOOL | 202;
+
     public static final int KM_TAG_ACTIVE_DATETIME = KM_DATE | 400;
     public static final int KM_TAG_ORIGINATION_EXPIRE_DATETIME = KM_DATE | 401;
     public static final int KM_TAG_USAGE_EXPIRE_DATETIME = KM_DATE | 402;
@@ -74,11 +76,12 @@
     public static final int KM_TAG_ALL_APPLICATIONS = KM_BOOL | 600;
     public static final int KM_TAG_APPLICATION_ID = KM_BYTES | 601;
 
-    public static final int KM_TAG_APPLICATION_DATA = KM_BYTES | 700;
     public static final int KM_TAG_CREATION_DATETIME = KM_DATE | 701;
     public static final int KM_TAG_ORIGIN = KM_ENUM | 702;
     public static final int KM_TAG_ROLLBACK_RESISTANT = KM_BOOL | 703;
     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_ASSOCIATED_DATA = KM_BYTES | 1000;
     public static final int KM_TAG_NONCE = KM_BYTES | 1001;
diff --git a/keystore/java/android/security/Credentials.java b/keystore/java/android/security/Credentials.java
index c8333c8..302b0bd 100644
--- a/keystore/java/android/security/Credentials.java
+++ b/keystore/java/android/security/Credentials.java
@@ -20,9 +20,11 @@
 import android.content.Context;
 import android.content.Intent;
 import android.util.Log;
+
 import com.android.org.bouncycastle.util.io.pem.PemObject;
 import com.android.org.bouncycastle.util.io.pem.PemReader;
 import com.android.org.bouncycastle.util.io.pem.PemWriter;
+
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
@@ -147,20 +149,23 @@
         Reader reader = new InputStreamReader(bai, StandardCharsets.US_ASCII);
         PemReader pr = new PemReader(reader);
 
-        CertificateFactory cf = CertificateFactory.getInstance("X509");
+        try {
+            CertificateFactory cf = CertificateFactory.getInstance("X509");
 
-        List<X509Certificate> result = new ArrayList<X509Certificate>();
-        PemObject o;
-        while ((o = pr.readPemObject()) != null) {
-            if (o.getType().equals("CERTIFICATE")) {
-                Certificate c = cf.generateCertificate(new ByteArrayInputStream(o.getContent()));
-                result.add((X509Certificate) c);
-            } else {
-                throw new IllegalArgumentException("Unknown type " + o.getType());
+            List<X509Certificate> result = new ArrayList<X509Certificate>();
+            PemObject o;
+            while ((o = pr.readPemObject()) != null) {
+                if (o.getType().equals("CERTIFICATE")) {
+                    Certificate c = cf.generateCertificate(new ByteArrayInputStream(o.getContent()));
+                    result.add((X509Certificate) c);
+                } else {
+                    throw new IllegalArgumentException("Unknown type " + o.getType());
+                }
             }
+            return result;
+        } finally {
+            pr.close();
         }
-        pr.close();
-        return result;
     }
 
     private static Credentials singleton;
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 1b87a41..3090ac1 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -19,7 +19,6 @@
 import android.app.ActivityThread;
 import android.app.Application;
 import android.app.KeyguardManager;
-
 import android.content.Context;
 import android.hardware.fingerprint.FingerprintManager;
 import android.os.Binder;
@@ -32,6 +31,7 @@
 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.KeyExpiredException;
@@ -615,6 +615,17 @@
         return onUserPasswordChanged(UserHandle.getUserId(Process.myUid()), newPassword);
     }
 
+    public int attestKey(
+            String alias, KeymasterArguments params, KeymasterCertificateChain outChain) {
+        try {
+            return mBinder.attestKey(alias, params, outChain);
+        } catch (RemoteException e) {
+            Log.w(TAG, "Cannot connect to keystore", e);
+            return SYSTEM_ERROR;
+        }
+    }
+
+
     /**
      * Returns a {@link KeyStoreException} corresponding to the provided keystore/keymaster error
      * code.
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
index 65460b5..3a0ff1c 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -22,6 +22,7 @@
 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 com.android.org.bouncycastle.asn1.ASN1EncodableVector;
@@ -46,6 +47,8 @@
 
 import libcore.util.EmptyArray;
 
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
 import java.math.BigInteger;
 import java.security.InvalidAlgorithmParameterException;
 import java.security.KeyPair;
@@ -57,14 +60,17 @@
 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;
@@ -166,6 +172,7 @@
         mOriginalKeymasterAlgorithm = keymasterAlgorithm;
     }
 
+    @SuppressWarnings("deprecation")
     @Override
     public void initialize(int keysize, SecureRandom random) {
         throw new IllegalArgumentException(
@@ -173,6 +180,7 @@
                 + " required to initialize this KeyPairGenerator");
     }
 
+    @SuppressWarnings("deprecation")
     @Override
     public void initialize(AlgorithmParameterSpec params, SecureRandom random)
             throws InvalidAlgorithmParameterException {
@@ -447,6 +455,69 @@
                     + ", but the user has not yet entered the credential");
         }
 
+        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;
+        } finally {
+            if (!success) {
+                Credentials.deleteAllTypesForAlias(mKeyStore, mEntryAlias, mEntryUid);
+            }
+        }
+    }
+
+    private Iterable<byte[]> createCertificateChain(final String privateKeyAlias, KeyPair keyPair)
+            throws ProviderException {
+        byte[] challenge = mSpec.getAttestationChallenge();
+        if (challenge != null) {
+            KeymasterArguments args = new KeymasterArguments();
+            args.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_CHALLENGE, challenge);
+            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) {
+            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 e) {
+            throw new ProviderException("Failed to load generated key pair from keystore", e);
+        }
+    }
+
+    private KeymasterArguments constructKeyGenerationArguments() {
         KeymasterArguments args = new KeymasterArguments();
         args.addUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, mKeySizeBits);
         args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, mKeymasterAlgorithm);
@@ -466,73 +537,72 @@
                 mSpec.getKeyValidityForConsumptionEnd());
         addAlgorithmSpecificParameters(args);
 
-        byte[] additionalEntropy =
-                KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng(
-                        mRng, (mKeySizeBits + 7) / 8);
+        if (mSpec.isUniqueIdIncluded())
+            args.addBoolean(KeymasterDefs.KM_TAG_INCLUDE_UNIQUE_ID);
 
-        final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + mEntryAlias;
-        boolean success = false;
-        try {
-            Credentials.deleteAllTypesForAlias(mKeyStore, mEntryAlias, mEntryUid);
-            KeyCharacteristics resultingKeyCharacteristics = new KeyCharacteristics();
-            int errorCode = mKeyStore.generateKey(
-                    privateKeyAlias,
-                    args,
-                    additionalEntropy,
-                    mEntryUid,
-                    flags,
-                    resultingKeyCharacteristics);
-            if (errorCode != KeyStore.NO_ERROR) {
-                throw new ProviderException(
-                        "Failed to generate key pair", KeyStore.getKeyStoreException(errorCode));
-            }
+        return args;
+    }
 
-            KeyPair result;
-            try {
-                result = AndroidKeyStoreProvider.loadAndroidKeyStoreKeyPairFromKeystore(
-                        mKeyStore, privateKeyAlias, mEntryUid);
-            } catch (UnrecoverableKeyException e) {
-                throw new ProviderException("Failed to load generated key pair from keystore", e);
-            }
+    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 (!mJcaKeyAlgorithm.equalsIgnoreCase(result.getPrivate().getAlgorithm())) {
-                throw new ProviderException(
-                        "Generated key pair algorithm does not match requested algorithm: "
-                        + result.getPrivate().getAlgorithm() + " vs " + mJcaKeyAlgorithm);
-            }
-
-            final X509Certificate cert;
-            try {
-                cert = generateSelfSignedCertificate(result.getPrivate(), result.getPublic());
-            } catch (Exception e) {
-                throw new ProviderException("Failed to generate self-signed certificate", e);
-            }
-
-            byte[] certBytes;
-            try {
-                certBytes = cert.getEncoded();
-            } catch (CertificateEncodingException e) {
-                throw new ProviderException(
-                        "Failed to obtain encoded form of self-signed certificate", e);
-            }
-
-            int insertErrorCode = mKeyStore.insert(
-                    Credentials.USER_CERTIFICATE + mEntryAlias,
-                    certBytes,
-                    mEntryUid,
-                    flags);
-            if (insertErrorCode != KeyStore.NO_ERROR) {
-                throw new ProviderException("Failed to store self-signed certificate",
-                        KeyStore.getKeyStoreException(insertErrorCode));
-            }
-
-            success = true;
-            return result;
-        } finally {
-            if (!success) {
-                Credentials.deleteAllTypesForAlias(mKeyStore, mEntryAlias, mEntryUid);
-            }
+        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 {
+        KeymasterCertificateChain outChain = new KeymasterCertificateChain();
+        int 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) {
@@ -548,8 +618,8 @@
         }
     }
 
-    private X509Certificate generateSelfSignedCertificate(
-            PrivateKey privateKey, PublicKey publicKey) throws Exception {
+    private X509Certificate generateSelfSignedCertificate(PrivateKey privateKey,
+            PublicKey publicKey) throws CertificateParsingException, IOException {
         String signatureAlgorithm =
                 getCertificateSignatureAlgorithm(mKeymasterAlgorithm, mKeySizeBits, mSpec);
         if (signatureAlgorithm == null) {
@@ -587,7 +657,7 @@
 
     @SuppressWarnings("deprecation")
     private X509Certificate generateSelfSignedCertificateWithFakeSignature(
-            PublicKey publicKey) throws Exception {
+            PublicKey publicKey) throws IOException, CertificateParsingException {
         V3TBSCertificateGenerator tbsGenerator = new V3TBSCertificateGenerator();
         ASN1ObjectIdentifier sigAlgOid;
         AlgorithmIdentifier sigAlgId;
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index add199f..f3fd129 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -250,6 +250,8 @@
     private final boolean mRandomizedEncryptionRequired;
     private final boolean mUserAuthenticationRequired;
     private final int mUserAuthenticationValidityDurationSeconds;
+    private final byte[] mAttestationChallenge;
+    private final boolean mUniqueIdIncluded;
 
     /**
      * @hide should be built with Builder
@@ -273,7 +275,9 @@
             @KeyProperties.BlockModeEnum String[] blockModes,
             boolean randomizedEncryptionRequired,
             boolean userAuthenticationRequired,
-            int userAuthenticationValidityDurationSeconds) {
+            int userAuthenticationValidityDurationSeconds,
+            byte[] attestationChallenge,
+            boolean uniqueIdIncluded) {
         if (TextUtils.isEmpty(keyStoreAlias)) {
             throw new IllegalArgumentException("keyStoreAlias must not be empty");
         }
@@ -315,6 +319,8 @@
         mRandomizedEncryptionRequired = randomizedEncryptionRequired;
         mUserAuthenticationRequired = userAuthenticationRequired;
         mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
+        mAttestationChallenge = Utils.cloneIfNotNull(attestationChallenge);
+        mUniqueIdIncluded = uniqueIdIncluded;
     }
 
     /**
@@ -539,6 +545,48 @@
     }
 
     /**
+     * Returns the attestation challenge value that will be placed in attestation certificate for
+     * this key pair.
+     *
+     * <p>If this method returns non-{@code null}, the public key certificate for this key pair will
+     * contain an extension that describes the details of the key's configuration and
+     * authorizations, including the content of the attestation challenge value. If the key is in
+     * secure hardware, and if the secure hardware supports attestation, the certificate will be
+     * signed by a chain of certificates rooted at a trustworthy CA key. Otherwise the chain will
+     * be rooted at an untrusted certificate.
+     *
+     * <p>If this method returns {@code null}, and the spec is used to generate an asymmetric (RSA
+     * or EC) key pair, the public key will have a self-signed certificate if it has purpose {@link
+     * KeyProperties#PURPOSE_SIGN} (see {@link #KeyGenParameterSpec(String, int)). If does not have
+     * purpose {@link KeyProperties#PURPOSE_SIGN}, it will have a fake certificate.
+     *
+     * <p>Symmetric keys, such as AES and HMAC keys, do not have public key certificates. If a
+     * {@link KeyGenParameterSpec} with {@link #hasAttestationCertificate()} returning
+     * non-{@code null} is used to generate a symmetric (AES or HMAC) key,
+     * {@link KeyGenerator#generateKey())} will throw
+     * {@link java.security.InvalidAlgorithmParameterException}.
+     *
+     * @see Builder#setAttestationChallenge(byte[])
+     */
+    /*
+     * TODO(swillden): Update this documentation to describe the hardware and software root keys,
+     * including information about CRL/OCSP services for discovering revocations, and to link to
+     * documentation of the extension format and content.
+     */
+    public byte[] getAttestationChallenge() {
+        return Utils.cloneIfNotNull(mAttestationChallenge);
+    }
+
+    /**
+     * @hide This is a system-only API
+     *
+     * Returns {@code true} if the attestation certificate will contain a unique ID field.
+     */
+    public boolean isUniqueIdIncluded() {
+        return mUniqueIdIncluded;
+    }
+
+    /**
      * Builder of {@link KeyGenParameterSpec} instances.
      */
     public final static class Builder {
@@ -562,6 +610,8 @@
         private boolean mRandomizedEncryptionRequired = true;
         private boolean mUserAuthenticationRequired;
         private int mUserAuthenticationValidityDurationSeconds = -1;
+        private byte[] mAttestationChallenge = null;
+        private boolean mUniqueIdIncluded = false;
 
         /**
          * Creates a new instance of the {@code Builder}.
@@ -957,6 +1007,59 @@
             return this;
         }
 
+        /*
+         * TODO(swillden): Update this documentation to describe the hardware and software root
+         * keys, including information about CRL/OCSP services for discovering revocations, and to
+         * link to documentation of the extension format and content.
+         */
+        /**
+         * Sets whether an attestation certificate will be generated for this key pair, and what
+         * challenge value will be placed in the certificate.  The attestation certificate chain
+         * can be retrieved with with {@link java.security.KeyStore#getCertificateChain(String)}.
+         *
+         * <p>If {@code attestationChallenge} is not {@code null}, the public key certificate for
+         * this key pair will contain an extension that describes the details of the key's
+         * configuration and authorizations, including the {@code attestationChallenge} value. If
+         * the key is in secure hardware, and if the secure hardware supports attestation, the
+         * certificate will be signed by a chain of certificates rooted at a trustworthy CA key.
+         * Otherwise the chain will be rooted at an untrusted certificate.
+         *
+         * <p>The purpose of the challenge value is to enable relying parties to verify that the key
+         * was created in response to a specific request. If attestation is desired but no
+         * challenged is needed, any non-{@code null} value may be used, including an empty byte
+         * array.
+         *
+         * <p>If {@code attestationChallenge} is {@code null}, and this spec is used to generate an
+         * asymmetric (RSA or EC) key pair, the public key certificate will be self-signed if the
+         * key has purpose {@link KeyProperties#PURPOSE_SIGN} (see
+         * {@link #KeyGenParameterSpec(String, int)). If the key does not have purpose
+         * {@link KeyProperties#PURPOSE_SIGN}, it is not possible to use the key to sign a
+         * certificate, so the public key certificate will contain a dummy signature.
+         *
+         * <p>Symmetric keys, such as AES and HMAC keys, do not have public key certificates. If a
+         * {@code getAttestationChallenge} returns non-{@code null} and the spec is used to
+         * generate a symmetric (AES or HMAC) key, {@link KeyGenerator#generateKey()} will throw
+         * {@link java.security.InvalidAlgorithmParameterException}.
+         *
+         * @see Builder#setAttestationChallenge(String attestationChallenge)
+         */
+        @NonNull
+        public Builder setAttestationChallenge(byte[] attestationChallenge) {
+            mAttestationChallenge = attestationChallenge;
+            return this;
+        }
+
+        /**
+         * @hide Only system apps can use this method.
+         *
+         * Sets whether to include a temporary unique ID field in the attestation certificate.
+         */
+        @NonNull
+        public Builder setUniqueIdIncluded(boolean uniqueIdIncluded) {
+            mUniqueIdIncluded = uniqueIdIncluded;
+            return this;
+        }
+
         /**
          * Builds an instance of {@code KeyGenParameterSpec}.
          */
@@ -981,7 +1084,9 @@
                     mBlockModes,
                     mRandomizedEncryptionRequired,
                     mUserAuthenticationRequired,
-                    mUserAuthenticationValidityDurationSeconds);
+                    mUserAuthenticationValidityDurationSeconds,
+                    mAttestationChallenge,
+                    mUniqueIdIncluded);
         }
     }
 }
diff --git a/keystore/java/android/security/keystore/Utils.java b/keystore/java/android/security/keystore/Utils.java
index 9bec682..5722c7b 100644
--- a/keystore/java/android/security/keystore/Utils.java
+++ b/keystore/java/android/security/keystore/Utils.java
@@ -29,4 +29,8 @@
     static Date cloneIfNotNull(Date value) {
         return (value != null) ? (Date) value.clone() : null;
     }
+
+    static byte[] cloneIfNotNull(byte[] value) {
+        return (value != null) ? value.clone() : null;
+    }
 }