1 /** 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.remoteprovisioner; 18 19 import android.annotation.NonNull; 20 import android.content.Context; 21 import android.hardware.security.keymint.DeviceInfo; 22 import android.hardware.security.keymint.ProtectedData; 23 import android.security.remoteprovisioning.IRemoteProvisioning; 24 import android.util.Log; 25 26 import java.security.cert.CertificateEncodingException; 27 import java.security.cert.CertificateException; 28 import java.security.cert.X509Certificate; 29 import java.util.List; 30 31 /** 32 * Provides an easy package to run the provisioning process from start to finish, interfacing 33 * with the remote provisioning system service and the server backend in order to provision 34 * attestation certificates to the device. 35 */ 36 public class Provisioner { 37 private static final String TAG = "RemoteProvisioningService"; 38 39 /** 40 * Drives the process of provisioning certs. The method passes the data fetched from the 41 * provisioning server along with the requested number of keys to the remote provisioning 42 * system backend. The backend will talk to the underlying IRemotelyProvisionedComponent 43 * interface in order to get a CSR bundle generated, along with an encrypted package containing 44 * metadata that the server needs in order to make decisions about provisioning. 45 * 46 * This method then passes that bundle back out to the server backend, waits for the response, 47 * and, if successful, passes the certificate chains back to the remote provisioning service to 48 * be stored and later assigned to apps requesting a key attestation. 49 * 50 * @param numKeys The number of keys to be signed. The service will do a best-effort to 51 * provision the number requested, but if the number requested is larger 52 * than the number of unsigned attestation key pairs available, it will 53 * only sign the number that is available at time of calling. 54 * @param secLevel Which KM instance should be used to provision certs. 55 * @param geekChain The certificate chain that signs the endpoint encryption key. 56 * @param challenge A server provided challenge to ensure freshness of the response. 57 * @param binder The IRemoteProvisioning binder interface needed by the method to handle talking 58 * to the remote provisioning system component. 59 * @param context The application context object which enables this method to make use of 60 * SettingsManager. 61 * @return The number of certificates provisoned. Ideally, this should equal {@code numKeys}. 62 */ provisionCerts(int numKeys, int secLevel, byte[] geekChain, byte[] challenge, @NonNull IRemoteProvisioning binder, Context context)63 public static int provisionCerts(int numKeys, int secLevel, byte[] geekChain, byte[] challenge, 64 @NonNull IRemoteProvisioning binder, Context context) { 65 Log.i(TAG, "Request for " + numKeys + " keys to be provisioned."); 66 if (numKeys < 1) { 67 Log.e(TAG, "Request at least 1 key to be signed. Num requested: " + numKeys); 68 return 0; 69 } 70 DeviceInfo deviceInfo = new DeviceInfo(); 71 ProtectedData protectedData = new ProtectedData(); 72 byte[] macedKeysToSign = 73 SystemInterface.generateCsr(false /* testMode */, numKeys, secLevel, geekChain, 74 challenge, protectedData, deviceInfo, binder); 75 if (macedKeysToSign == null || protectedData.protectedData == null 76 || deviceInfo.deviceInfo == null) { 77 Log.e(TAG, "Keystore failed to generate a payload"); 78 return 0; 79 } 80 byte[] certificateRequest = 81 CborUtils.buildCertificateRequest(deviceInfo.deviceInfo, 82 challenge, 83 protectedData.protectedData, 84 macedKeysToSign); 85 if (certificateRequest == null) { 86 Log.e(TAG, "Failed to serialize the payload generated by keystore."); 87 return 0; 88 } 89 List<byte[]> certChains = ServerInterface.requestSignedCertificates(context, 90 certificateRequest, challenge); 91 if (certChains == null) { 92 Log.e(TAG, "Server response failed on provisioning attempt."); 93 return 0; 94 } 95 Log.i(TAG, "Received " + certChains.size() + " certificate chains from the server."); 96 int provisioned = 0; 97 for (byte[] certChain : certChains) { 98 // DER encoding specifies leaf to root ordering. Pull the public key and expiration 99 // date from the leaf. 100 X509Certificate cert; 101 try { 102 cert = X509Utils.formatX509Certs(certChain)[0]; 103 } catch (CertificateException e) { 104 Log.e(TAG, "Failed to interpret DER encoded certificate chain", e); 105 return 0; 106 } 107 // getTime returns the time in *milliseconds* since the epoch. 108 long expirationDate = cert.getNotAfter().getTime(); 109 byte[] rawPublicKey = X509Utils.getAndFormatRawPublicKey(cert); 110 if (rawPublicKey == null) { 111 Log.e(TAG, "Skipping malformed public key."); 112 continue; 113 } 114 try { 115 if (SystemInterface.provisionCertChain(rawPublicKey, cert.getEncoded(), certChain, 116 expirationDate, secLevel, binder)) { 117 provisioned++; 118 } 119 } catch (CertificateEncodingException e) { 120 Log.e(TAG, "Somehow can't re-encode the decoded batch cert...", e); 121 return provisioned; 122 } 123 } 124 Log.i(TAG, "In provisionCerts: Requested " + numKeys + " keys. " 125 + provisioned + " were provisioned."); 126 return provisioned; 127 } 128 } 129