Merge "Updating RemoteProvisioner to new AIDL definition"
diff --git a/Android.bp b/Android.bp
index a465016..b3a51a1 100644
--- a/Android.bp
+++ b/Android.bp
@@ -18,11 +18,13 @@
     platform_apis: true,
     privileged: true,
     libs: [
-        "android.security.remoteprovisioning-java",
         "android.system.keystore2-java",
         "framework-annotations-lib",
         "cbor-java",
     ],
+    static_libs: [
+        "android.security.remoteprovisioning-java"
+    ],
     resource_dirs: ["res"],
     srcs: ["src/**/*.java",
            "src/**/I*.aidl",],
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 709e638..7295930 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -19,7 +19,8 @@
 
     <application
         android:label="@string/app_name">
-        <receiver android:name=".BootReceiver">
+        <receiver android:name=".BootReceiver"
+            android:exported="false">
             <intent-filter >
                 <action android:name="android.intent.action.BOOT_COMPLETED"/>
             </intent-filter>
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..e4b8ca5
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,7 @@
+# Primary Owner and Reviewer
+jbires@google.com
+
+# Reviewers
+mpgroover@google.com
+swillden@google.com
+
diff --git a/src/com/android/remoteprovisioner/CborUtils.java b/src/com/android/remoteprovisioner/CborUtils.java
index 356ddfa..a0ac340 100644
--- a/src/com/android/remoteprovisioner/CborUtils.java
+++ b/src/com/android/remoteprovisioner/CborUtils.java
@@ -51,7 +51,7 @@
      * @param serverResp The CBOR blob received from the server which contains all signed
      *                      certificate chains.
      *
-     * @return A List object where each byte[] entry is an entire PEM-encoded certificate chain.
+     * @return A List object where each byte[] entry is an entire DER-encoded certificate chain.
      */
     public static List<byte[]> parseSignedCertificates(byte[] serverResp) {
         try {
diff --git a/src/com/android/remoteprovisioner/PeriodicProvisioner.java b/src/com/android/remoteprovisioner/PeriodicProvisioner.java
index 8767f6c..e21224c 100644
--- a/src/com/android/remoteprovisioner/PeriodicProvisioner.java
+++ b/src/com/android/remoteprovisioner/PeriodicProvisioner.java
@@ -18,6 +18,7 @@
 
 import android.app.job.JobParameters;
 import android.app.job.JobService;
+import android.hardware.security.keymint.SecurityLevel;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.security.remoteprovisioning.AttestationPoolStatus;
@@ -63,26 +64,31 @@
                 IRemoteProvisioning binder =
                     IRemoteProvisioning.Stub.asInterface(ServiceManager.getService(SERVICE));
                 // TODO: Replace expiration date parameter with value fetched from server
-                AttestationPoolStatus pool =
-                    binder.getPoolStatus(1);
-                int generated = 0;
-                while (generated + pool.total - pool.expiring < TOTAL_SIGNED_KEYS) {
-                    generated++;
-                    binder.generateKeyPair(false /* isTestMode */);
-                    Thread.sleep(5000);
-                }
-                if (generated > 0) {
-                    Log.d(TAG, "Keys generated, moving to provisioning process.");
-                    Provisioner.provisionCerts(generated, binder);
-                }
+                checkAndProvision(binder, 1, SecurityLevel.TRUSTED_ENVIRONMENT);
                 jobFinished(mParams, false /* wantsReschedule */);
             } catch (RemoteException e) {
                 jobFinished(mParams, true /* wantsReschedule */);
-                Log.e(TAG, "Remote exception during provisioning.", e);
+                Log.e(TAG, "Error on the binder side during provisioning.", e);
             } catch (InterruptedException e) {
                 jobFinished(mParams, true /* wantsReschedule */);
                 Log.e(TAG, "Provisioner thread interrupted.", e);
             }
         }
+
+        private void checkAndProvision(IRemoteProvisioning binder, long expiringBy, int secLevel)
+                throws InterruptedException, RemoteException {
+            AttestationPoolStatus pool =
+                binder.getPoolStatus(expiringBy, secLevel);
+            int generated = 0;
+            while (generated + pool.total - pool.expiring < TOTAL_SIGNED_KEYS) {
+                generated++;
+                binder.generateKeyPair(false /* isTestMode */, secLevel);
+                Thread.sleep(5000);
+            }
+            if (generated > 0) {
+                Log.d(TAG, "Keys generated, moving to provisioning process.");
+                Provisioner.provisionCerts(generated, secLevel, binder);
+            }
+        }
     }
 }
diff --git a/src/com/android/remoteprovisioner/Provisioner.java b/src/com/android/remoteprovisioner/Provisioner.java
index 3aca315..f77acd3 100644
--- a/src/com/android/remoteprovisioner/Provisioner.java
+++ b/src/com/android/remoteprovisioner/Provisioner.java
@@ -20,6 +20,7 @@
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.hardware.security.keymint.SecurityLevel;
 import android.os.RemoteException;
 import android.security.remoteprovisioning.IRemoteProvisioning;
 import android.util.Log;
@@ -33,6 +34,7 @@
 import java.io.OutputStream;
 import java.net.HttpURLConnection;
 import java.net.URL;
+import java.security.interfaces.ECPublicKey;
 import java.security.KeyStore;
 import java.security.cert.Certificate;
 import java.security.cert.CertificateException;
@@ -40,6 +42,7 @@
 import java.security.cert.X509Certificate;
 import java.util.List;
 import java.util.ArrayList;
+import java.util.Arrays;
 
 /**
  * Provides an easy package to run the provisioning process from start to finish, interfacing
@@ -53,8 +56,10 @@
             PROVISIONING_URL + "/v1:signCertificates?challenge=";
     private static final String TAG = "RemoteProvisioningService";
 
+    private static final byte ECDSA_UNCOMPRESSED_BYTE = 0x04;
+
     /**
-     * Takes a byte stream composed of PEM encoded certificates and returns the X.509 certificates
+     * Takes a byte array composed of DER encoded certificates and returns the X.509 certificates
      * contained within as an X509Certificate array.
      */
     private static X509Certificate[] formatX509Certs(byte[] certStream)
@@ -163,12 +168,14 @@
      *                     than the number of unsigned attestation key pairs available, it will
      *                     only sign the number that is available at time of calling.
      *
+     * @param secLevel Which KM instance should be used to provision certs.
      * @param binder The IRemoteProvisioning binder interface needed by the method to handle talking
      *                     to the remote provisioning system component.
      *
      * @return True if certificates were successfully provisioned for the signing keys.
      */
-    public static boolean provisionCerts(int numKeys, @NonNull IRemoteProvisioning binder) {
+    public static boolean provisionCerts(int numKeys, int secLevel,
+            @NonNull IRemoteProvisioning binder) {
         if (numKeys < 1) {
             Log.e(TAG, "Request at least 1 key to be signed. Num requested: " + numKeys);
             return false;
@@ -183,7 +190,8 @@
             payload = binder.generateCsr(false /* testMode */,
                     numKeys,
                     geek.geek,
-                    geek.challenge);
+                    geek.challenge,
+                    secLevel);
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to generate CSR blob", e);
             return false;
@@ -194,9 +202,45 @@
         }
         ArrayList<byte[]> certChains =
             new ArrayList<byte[]>(requestSignedCertificates(payload, geek.challenge));
-        // TODO: Fix AIDL here to add a matching scheme, since public key in KeyStore is a CBOR
-        // blob
-        //binder.provisionCertChain();
+        for (byte[] certChain : certChains) {
+            // DER encoding specifies leaf to root ordering. Pull the public key and expiration
+            // date from the leaf.
+            X509Certificate cert;
+            try {
+                cert = formatX509Certs(certChain)[0];
+            } catch (CertificateException e) {
+                Log.e(TAG, "Failed to interpret DER encoded certificate chain", e);
+                return false;
+            }
+            // getTime returns the time in *milliseconds* since the epoch.
+            long expirationDate = cert.getNotAfter().getTime();
+            ECPublicKey key = (ECPublicKey) cert.getPublicKey();
+
+            // Remote key provisioning internally supports the default, uncompressed public key
+            // format for ECDSA. This defines the format as (s | x | y), where s is the byte
+            // indicating if the key is compressed or not, and x and y make up the EC point.
+            // However, the key as stored in a COSE_Key object is just the two points. As such,
+            // the raw public key is stored in the database is (x | y), so the compression byte
+            // should be dropped here.
+            //
+            // s: 1 byte, x: 32 bytes, y: 32 bytes
+            byte[] keyEncoding = key.getEncoded();
+            if (keyEncoding.length != 65) {
+                Log.e(TAG, "Key is not encoded as expected, or corrupted. Length: "
+                        + keyEncoding.length);
+                return false;
+            } else if (keyEncoding[0] != ECDSA_UNCOMPRESSED_BYTE) {
+                Log.e(TAG, "Key is not uncompressed.");
+            }
+            byte[] rawPublicKey = Arrays.copyOfRange(keyEncoding, 1 /* from */, 65 /* to */);
+            try {
+                binder.provisionCertChain(rawPublicKey, certChain, expirationDate, secLevel);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error on the binder side when attempting to provision the signed chain",
+                        e);
+                return false;
+            }
+        }
         return true;
     }
 }
diff --git a/src/com/android/remoteprovisioner/service/GenerateKeyService.java b/src/com/android/remoteprovisioner/service/GenerateKeyService.java
index d32f18e..5124f45 100644
--- a/src/com/android/remoteprovisioner/service/GenerateKeyService.java
+++ b/src/com/android/remoteprovisioner/service/GenerateKeyService.java
@@ -20,6 +20,7 @@
 
 import android.app.Service;
 import android.content.Intent;
+import android.hardware.security.keymint.SecurityLevel;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -27,6 +28,8 @@
 import android.security.remoteprovisioning.IRemoteProvisioning;
 import android.util.Log;
 
+import java.lang.System;
+
 /**
  * Provides the implementation for IGenerateKeyService.aidl
  */
@@ -50,17 +53,23 @@
             try {
                 IRemoteProvisioning binder =
                         IRemoteProvisioning.Stub.asInterface(ServiceManager.getService(SERVICE));
-                AttestationPoolStatus pool =
-                        binder.getPoolStatus(1);
-                if (pool.unassigned == 0) {
-                    binder.generateKeyPair(false /* isTestMode */);
-                    Provisioner.provisionCerts(1 /* numCsr */, binder);
-                } else {
-                    Log.e(TAG, "generateKey() called, but signed certs are available.");
-                }
+                // Iterate through each security level backend
+                checkAndFillPool(binder, SecurityLevel.TRUSTED_ENVIRONMENT);
             } catch (RemoteException e) {
                 Log.e(TAG, "Remote Exception: ", e);
             }
         }
+
+        private void checkAndFillPool(IRemoteProvisioning binder, int secLevel)
+                throws RemoteException {
+            AttestationPoolStatus pool =
+                    binder.getPoolStatus(System.currentTimeMillis(), secLevel);
+            if (pool.unassigned == 0) {
+                binder.generateKeyPair(false /* isTestMode */, secLevel);
+                Provisioner.provisionCerts(1 /* numCsr */, secLevel, binder);
+            } else {
+                Log.e(TAG, "generateKey() called, but signed certs are available.");
+            }
+        }
     };
 }