blob: 79f4ceadf7edf882da5e6a5abf5905f54304fc5c [file] [log] [blame]
/**
* Copyright (C) 2021 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 com.android.remoteprovisioner;
import android.annotation.NonNull;
import android.hardware.security.keymint.DeviceInfo;
import android.hardware.security.keymint.ProtectedData;
import android.os.RemoteException;
import android.security.remoteprovisioning.IRemoteProvisioning;
import android.util.Log;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.List;
import co.nstant.in.cbor.CborBuilder;
import co.nstant.in.cbor.CborDecoder;
import co.nstant.in.cbor.CborEncoder;
import co.nstant.in.cbor.CborException;
import co.nstant.in.cbor.builder.ArrayBuilder;
import co.nstant.in.cbor.model.Array;
import co.nstant.in.cbor.model.DataItem;
/**
* Provides convenience methods for interfacing with the android.security.remoteprovisioning system
* service. Since the remoteprovisioning API is internal only and subject to change, it is handy
* to have an abstraction layer to reduce the impact of these changes on the app.
*/
public class SystemInterface {
private static final String TAG = "SystemInterface";
private static byte[] makeProtectedHeaders() throws CborException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
new CborEncoder(baos).encode(new CborBuilder()
.addMap()
.put(1, 5)
.end()
.build());
return baos.toByteArray();
}
private static byte[] encodePayload(List<DataItem> keys) throws CborException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ArrayBuilder<CborBuilder> builder = new CborBuilder().addArray();
for (int i = 1; i < keys.size(); i++) {
builder = builder.add(keys.get(i));
}
new CborEncoder(baos).encode(builder.end().build());
return baos.toByteArray();
}
/**
* Sends a generateCsr request over the binder interface. `dataBlob` is an out parameter that
* will be populated by the underlying binder service.
*/
public static byte[] generateCsr(boolean testMode, int numKeys, int secLevel,
byte[] geekChain, byte[] challenge, ProtectedData protectedData, DeviceInfo deviceInfo,
@NonNull IRemoteProvisioning binder) {
try {
ProtectedData dataBundle = new ProtectedData();
byte[] macedPublicKeys = binder.generateCsr(testMode,
numKeys,
geekChain,
challenge,
secLevel,
protectedData,
deviceInfo);
ByteArrayInputStream bais = new ByteArrayInputStream(macedPublicKeys);
List<DataItem> dataItems = new CborDecoder(bais).decode();
List<DataItem> macInfo = ((Array) dataItems.get(0)).getDataItems();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
new CborEncoder(baos).encode(new CborBuilder()
.addArray()
.add(makeProtectedHeaders())
.addMap() //unprotected headers
.end()
.add(encodePayload(macInfo))
.add(macInfo.get(0))
.end()
.build());
return baos.toByteArray();
} catch (RemoteException e) {
Log.e(TAG, "Failed to generate CSR blob", e);
return null;
} catch (CborException e) {
Log.e(TAG, "Failed to parse/build CBOR", e);
return null;
}
}
/**
* Sends a provisionCertChain request down to the underlying remote provisioning binder service.
*/
public static boolean provisionCertChain(byte[] rawPublicKey, byte[] encodedCert,
byte[] certChain,
long expirationDate, int secLevel,
IRemoteProvisioning binder) {
try {
binder.provisionCertChain(rawPublicKey, encodedCert, certChain,
expirationDate, secLevel);
return true;
} catch (RemoteException e) {
Log.e(TAG, "Error on the binder side when attempting to provision the signed chain",
e);
return false;
}
}
}