1 /** 2 * Copyright (C) 2021 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.hardware.security.keymint.DeviceInfo; 21 import android.hardware.security.keymint.ProtectedData; 22 import android.os.RemoteException; 23 import android.os.ServiceSpecificException; 24 import android.security.remoteprovisioning.AttestationPoolStatus; 25 import android.security.remoteprovisioning.IRemoteProvisioning; 26 import android.util.Log; 27 28 import java.io.ByteArrayInputStream; 29 import java.io.ByteArrayOutputStream; 30 import java.util.List; 31 32 import co.nstant.in.cbor.CborBuilder; 33 import co.nstant.in.cbor.CborDecoder; 34 import co.nstant.in.cbor.CborEncoder; 35 import co.nstant.in.cbor.CborException; 36 import co.nstant.in.cbor.builder.ArrayBuilder; 37 import co.nstant.in.cbor.model.Array; 38 import co.nstant.in.cbor.model.DataItem; 39 40 /** 41 * Provides convenience methods for interfacing with the android.security.remoteprovisioning system 42 * service. Since the remoteprovisioning API is internal only and subject to change, it is handy 43 * to have an abstraction layer to reduce the impact of these changes on the app. 44 */ 45 public class SystemInterface { 46 47 private static final String TAG = "RemoteProvisioner"; 48 makeProtectedHeaders()49 private static byte[] makeProtectedHeaders() throws CborException { 50 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 51 new CborEncoder(baos).encode(new CborBuilder() 52 .addMap() 53 .put(1, 5) 54 .end() 55 .build()); 56 return baos.toByteArray(); 57 } 58 encodePayload(List<DataItem> keys)59 private static byte[] encodePayload(List<DataItem> keys) throws CborException { 60 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 61 ArrayBuilder<CborBuilder> builder = new CborBuilder().addArray(); 62 for (int i = 1; i < keys.size(); i++) { 63 builder = builder.add(keys.get(i)); 64 } 65 new CborEncoder(baos).encode(builder.end().build()); 66 return baos.toByteArray(); 67 } 68 69 /** 70 * Sends a generateCsr request over the binder interface. `dataBlob` is an out parameter that 71 * will be populated by the underlying binder service. 72 */ generateCsr(boolean testMode, int numKeys, int secLevel, byte[] geekChain, byte[] challenge, ProtectedData protectedData, DeviceInfo deviceInfo, @NonNull IRemoteProvisioning binder)73 public static byte[] generateCsr(boolean testMode, int numKeys, int secLevel, 74 byte[] geekChain, byte[] challenge, ProtectedData protectedData, DeviceInfo deviceInfo, 75 @NonNull IRemoteProvisioning binder) { 76 try { 77 Log.i(TAG, "Packaging " + numKeys + " keys into a CSR. Test: " + testMode); 78 ProtectedData dataBundle = new ProtectedData(); 79 byte[] macedPublicKeys = binder.generateCsr(testMode, 80 numKeys, 81 geekChain, 82 challenge, 83 secLevel, 84 protectedData, 85 deviceInfo); 86 if (macedPublicKeys == null) { 87 Log.e(TAG, "Keystore didn't generate a CSR successfully."); 88 return null; 89 } 90 ByteArrayInputStream bais = new ByteArrayInputStream(macedPublicKeys); 91 List<DataItem> dataItems = new CborDecoder(bais).decode(); 92 List<DataItem> macInfo = ((Array) dataItems.get(0)).getDataItems(); 93 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 94 new CborEncoder(baos).encode(new CborBuilder() 95 .addArray() 96 .add(makeProtectedHeaders()) 97 .addMap() //unprotected headers 98 .end() 99 .add(encodePayload(macInfo)) 100 .add(macInfo.get(0)) 101 .end() 102 .build()); 103 return baos.toByteArray(); 104 } catch (RemoteException e) { 105 Log.e(TAG, "Failed to generate CSR blob", e); 106 return null; 107 } catch (ServiceSpecificException e) { 108 Log.e(TAG, "Failure in Keystore or Keymint to facilitate blob generation.", e); 109 return null; 110 } catch (CborException e) { 111 Log.e(TAG, "Failed to parse/build CBOR", e); 112 return null; 113 } 114 } 115 116 /** 117 * Sends a provisionCertChain request down to the underlying remote provisioning binder service. 118 */ provisionCertChain(byte[] rawPublicKey, byte[] encodedCert, byte[] certChain, long expirationDate, int secLevel, IRemoteProvisioning binder)119 public static boolean provisionCertChain(byte[] rawPublicKey, byte[] encodedCert, 120 byte[] certChain, 121 long expirationDate, int secLevel, 122 IRemoteProvisioning binder) { 123 try { 124 binder.provisionCertChain(rawPublicKey, encodedCert, certChain, 125 expirationDate, secLevel); 126 return true; 127 } catch (RemoteException e) { 128 Log.e(TAG, "Error on the binder side when attempting to provision the signed chain", 129 e); 130 return false; 131 } catch (ServiceSpecificException e) { 132 Log.e(TAG, "Error on the Keystore side", e); 133 return false; 134 } 135 } 136 137 /** 138 * Returns the pool status for a given {@code secLevel}, with the expiration date configured to 139 * the value passed in as {@code expiringBy}. 140 */ getPoolStatus(long expiringBy, int secLevel, IRemoteProvisioning binder)141 public static AttestationPoolStatus getPoolStatus(long expiringBy, 142 int secLevel, 143 IRemoteProvisioning binder) 144 throws RemoteException { 145 try { 146 return binder.getPoolStatus(expiringBy, secLevel); 147 } catch (ServiceSpecificException e) { 148 Log.e(TAG, "Failure in Keystore", e); 149 throw new RemoteException(e); 150 } 151 } 152 153 /** 154 * Generates an attestation key pair. 155 * 156 * @param isTestMode Whether or not to generate a test key, which would accept any EEK later. 157 * @param secLevel Which security level to generate the key for. 158 */ generateKeyPair(boolean isTestMode, int secLevel, IRemoteProvisioning binder)159 public static void generateKeyPair(boolean isTestMode, int secLevel, IRemoteProvisioning binder) 160 throws RemoteException { 161 try { 162 binder.generateKeyPair(isTestMode, secLevel); 163 } catch (ServiceSpecificException e) { 164 Log.e(TAG, "Failure in Keystore or KeyMint.", e); 165 throw new RemoteException(e); 166 } 167 } 168 } 169