Merge changes from topic "remote_provisioning_diagnostics"
* changes: Tweak remote_provisioning command line interface Add command line interface for remote_provisioning
This commit is contained in:
@@ -155,6 +155,7 @@ java_library_static {
|
|||||||
"android.hardware.health-V2-java", // AIDL
|
"android.hardware.health-V2-java", // AIDL
|
||||||
"android.hardware.health-translate-java",
|
"android.hardware.health-translate-java",
|
||||||
"android.hardware.light-V1-java",
|
"android.hardware.light-V1-java",
|
||||||
|
"android.hardware.security.rkp-V3-java",
|
||||||
"android.hardware.tv.cec-V1.1-java",
|
"android.hardware.tv.cec-V1.1-java",
|
||||||
"android.hardware.tv.hdmi.cec-V1-java",
|
"android.hardware.tv.hdmi.cec-V1-java",
|
||||||
"android.hardware.tv.hdmi.connection-V1-java",
|
"android.hardware.tv.hdmi.connection-V1-java",
|
||||||
@@ -171,6 +172,7 @@ java_library_static {
|
|||||||
"android.hardware.power-V3-java",
|
"android.hardware.power-V3-java",
|
||||||
"android.hidl.manager-V1.2-java",
|
"android.hidl.manager-V1.2-java",
|
||||||
"capture_state_listener-aidl-java",
|
"capture_state_listener-aidl-java",
|
||||||
|
"cbor-java",
|
||||||
"icu4j_calendar_astronomer",
|
"icu4j_calendar_astronomer",
|
||||||
"netd-client",
|
"netd-client",
|
||||||
"overlayable_policy_aidl-java",
|
"overlayable_policy_aidl-java",
|
||||||
|
|||||||
@@ -19,14 +19,18 @@ package com.android.server.security.rkp;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
import android.os.OutcomeReceiver;
|
import android.os.OutcomeReceiver;
|
||||||
|
import android.os.ParcelFileDescriptor;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.security.rkp.IGetRegistrationCallback;
|
import android.security.rkp.IGetRegistrationCallback;
|
||||||
import android.security.rkp.IRemoteProvisioning;
|
import android.security.rkp.IRemoteProvisioning;
|
||||||
import android.security.rkp.service.RegistrationProxy;
|
import android.security.rkp.service.RegistrationProxy;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.android.internal.util.DumpUtils;
|
||||||
import com.android.server.SystemService;
|
import com.android.server.SystemService;
|
||||||
|
|
||||||
|
import java.io.FileDescriptor;
|
||||||
|
import java.io.PrintWriter;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
@@ -97,5 +101,18 @@ public class RemoteProvisioningService extends SystemService {
|
|||||||
Binder.restoreCallingIdentity(callingIdentity);
|
Binder.restoreCallingIdentity(callingIdentity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
||||||
|
if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
|
||||||
|
new RemoteProvisioningShellCommand().dump(pw);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int handleShellCommand(ParcelFileDescriptor in, ParcelFileDescriptor out,
|
||||||
|
ParcelFileDescriptor err, String[] args) {
|
||||||
|
return new RemoteProvisioningShellCommand().exec(this, in.getFileDescriptor(),
|
||||||
|
out.getFileDescriptor(), err.getFileDescriptor(), args);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,252 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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.server.security.rkp;
|
||||||
|
|
||||||
|
import android.hardware.security.keymint.DeviceInfo;
|
||||||
|
import android.hardware.security.keymint.IRemotelyProvisionedComponent;
|
||||||
|
import android.hardware.security.keymint.MacedPublicKey;
|
||||||
|
import android.hardware.security.keymint.ProtectedData;
|
||||||
|
import android.hardware.security.keymint.RpcHardwareInfo;
|
||||||
|
import android.os.RemoteException;
|
||||||
|
import android.os.ServiceManager;
|
||||||
|
import android.os.ShellCommand;
|
||||||
|
import android.util.IndentingPrintWriter;
|
||||||
|
|
||||||
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
|
import co.nstant.in.cbor.CborDecoder;
|
||||||
|
import co.nstant.in.cbor.CborEncoder;
|
||||||
|
import co.nstant.in.cbor.CborException;
|
||||||
|
import co.nstant.in.cbor.model.Array;
|
||||||
|
import co.nstant.in.cbor.model.ByteString;
|
||||||
|
import co.nstant.in.cbor.model.DataItem;
|
||||||
|
import co.nstant.in.cbor.model.Map;
|
||||||
|
import co.nstant.in.cbor.model.SimpleValue;
|
||||||
|
import co.nstant.in.cbor.model.UnsignedInteger;
|
||||||
|
|
||||||
|
class RemoteProvisioningShellCommand extends ShellCommand {
|
||||||
|
private static final String USAGE = "usage: cmd remote_provisioning SUBCOMMAND [ARGS]\n"
|
||||||
|
+ "help\n"
|
||||||
|
+ " Show this message.\n"
|
||||||
|
+ "dump\n"
|
||||||
|
+ " Dump service diagnostics.\n"
|
||||||
|
+ "list [--min-version MIN_VERSION]\n"
|
||||||
|
+ " List the names of the IRemotelyProvisionedComponent instances.\n"
|
||||||
|
+ "csr [--challenge CHALLENGE] NAME\n"
|
||||||
|
+ " Generate and print a base64-encoded CSR from the named\n"
|
||||||
|
+ " IRemotelyProvisionedComponent. A base64-encoded challenge can be provided,\n"
|
||||||
|
+ " or else it defaults to an empty challenge.\n";
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
static final String EEK_ED25519_BASE64 = "goRDoQEnoFgqpAEBAycgBiFYIJm57t1e5FL2hcZMYtw+YatXSH11N"
|
||||||
|
+ "ymtdoAy0rPLY1jZWEAeIghLpLekyNdOAw7+uK8UTKc7b6XN3Np5xitk/pk5r3bngPpmAIUNB5gqrJFcpyUUS"
|
||||||
|
+ "QY0dcqKJ3rZ41pJ6wIDhEOhASegWE6lAQECWCDQrsEVyirPc65rzMvRlh1l6LHd10oaN7lDOpfVmd+YCAM4G"
|
||||||
|
+ "CAEIVggvoXnRsSjQlpA2TY6phXQLFh+PdwzAjLS/F4ehyVfcmBYQJvPkOIuS6vRGLEOjl0gJ0uEWP78MpB+c"
|
||||||
|
+ "gWDvNeCvvpkeC1UEEvAMb9r6B414vAtzmwvT/L1T6XUg62WovGHWAQ=";
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
static final String EEK_P256_BASE64 = "goRDoQEmoFhNpQECAyYgASFYIPcUituX9MxT79JkEcTjdR9mH6RxDGzP"
|
||||||
|
+ "+glGgHSHVPKtIlggXn9b9uzk9hnM/xM3/Q+hyJPbGAZ2xF3m12p3hsMtr49YQC+XjkL7vgctlUeFR5NAsB/U"
|
||||||
|
+ "m0ekxESp8qEHhxDHn8sR9L+f6Dvg5zRMFfx7w34zBfTRNDztAgRgehXgedOK/ySEQ6EBJqBYcaYBAgJYIDVz"
|
||||||
|
+ "tz+gioCJsSZn6ct8daGvAmH8bmUDkTvTS30UlD5GAzgYIAEhWCDgQc8vDzQPHDMsQbDP1wwwVTXSHmpHE0su"
|
||||||
|
+ "0UiWfiScaCJYIB/ORcX7YbqBIfnlBZubOQ52hoZHuB4vRfHOr9o/gGjbWECMs7p+ID4ysGjfYNEdffCsOI5R"
|
||||||
|
+ "vP9s4Wc7Snm8Vnizmdh8igfY2rW1f3H02GvfMyc0e2XRKuuGmZirOrSAqr1Q";
|
||||||
|
|
||||||
|
private static final int ERROR = -1;
|
||||||
|
private static final int SUCCESS = 0;
|
||||||
|
|
||||||
|
private final Injector mInjector;
|
||||||
|
|
||||||
|
RemoteProvisioningShellCommand() {
|
||||||
|
this(new Injector());
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
RemoteProvisioningShellCommand(Injector injector) {
|
||||||
|
mInjector = injector;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onHelp() {
|
||||||
|
getOutPrintWriter().print(USAGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("CatchAndPrintStackTrace")
|
||||||
|
public int onCommand(String cmd) {
|
||||||
|
if (cmd == null) {
|
||||||
|
return handleDefaultCommands(cmd);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
switch (cmd) {
|
||||||
|
case "list":
|
||||||
|
return list();
|
||||||
|
case "csr":
|
||||||
|
return csr();
|
||||||
|
default:
|
||||||
|
return handleDefaultCommands(cmd);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace(getErrPrintWriter());
|
||||||
|
return ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("CatchAndPrintStackTrace")
|
||||||
|
void dump(PrintWriter pw) {
|
||||||
|
try {
|
||||||
|
IndentingPrintWriter ipw = new IndentingPrintWriter(pw);
|
||||||
|
for (String name : mInjector.getIrpcNames()) {
|
||||||
|
ipw.println(name + ":");
|
||||||
|
ipw.increaseIndent();
|
||||||
|
dumpRpcInstance(ipw, name);
|
||||||
|
ipw.decreaseIndent();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace(pw);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void dumpRpcInstance(PrintWriter pw, String name) throws RemoteException {
|
||||||
|
RpcHardwareInfo info = mInjector.getIrpcBinder(name).getHardwareInfo();
|
||||||
|
pw.println("hwVersion=" + info.versionNumber);
|
||||||
|
pw.println("rpcAuthorName=" + info.rpcAuthorName);
|
||||||
|
if (info.versionNumber < 3) {
|
||||||
|
pw.println("supportedEekCurve=" + info.supportedEekCurve);
|
||||||
|
}
|
||||||
|
pw.println("uniqueId=" + info.uniqueId);
|
||||||
|
if (info.versionNumber >= 3) {
|
||||||
|
pw.println("supportedNumKeysInCsr=" + info.supportedNumKeysInCsr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int list() throws RemoteException {
|
||||||
|
for (String name : mInjector.getIrpcNames()) {
|
||||||
|
getOutPrintWriter().println(name);
|
||||||
|
}
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int csr() throws RemoteException, CborException {
|
||||||
|
byte[] challenge = {};
|
||||||
|
String opt;
|
||||||
|
while ((opt = getNextOption()) != null) {
|
||||||
|
switch (opt) {
|
||||||
|
case "--challenge":
|
||||||
|
challenge = Base64.getDecoder().decode(getNextArgRequired());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
getErrPrintWriter().println("error: unknown option " + opt);
|
||||||
|
return ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String name = getNextArgRequired();
|
||||||
|
|
||||||
|
IRemotelyProvisionedComponent binder = mInjector.getIrpcBinder(name);
|
||||||
|
RpcHardwareInfo info = binder.getHardwareInfo();
|
||||||
|
MacedPublicKey[] emptyKeys = new MacedPublicKey[] {};
|
||||||
|
byte[] csrBytes;
|
||||||
|
switch (info.versionNumber) {
|
||||||
|
case 1:
|
||||||
|
case 2:
|
||||||
|
DeviceInfo deviceInfo = new DeviceInfo();
|
||||||
|
ProtectedData protectedData = new ProtectedData();
|
||||||
|
byte[] eek = getEekChain(info.supportedEekCurve);
|
||||||
|
byte[] keysToSignMac = binder.generateCertificateRequest(
|
||||||
|
/*testMode=*/false, emptyKeys, eek, challenge, deviceInfo, protectedData);
|
||||||
|
csrBytes = composeCertificateRequestV1(
|
||||||
|
deviceInfo, challenge, protectedData, keysToSignMac);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
csrBytes = binder.generateCertificateRequestV2(emptyKeys, challenge);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
getErrPrintWriter().println("error: unsupported hwVersion: " + info.versionNumber);
|
||||||
|
return ERROR;
|
||||||
|
}
|
||||||
|
getOutPrintWriter().println(Base64.getEncoder().encodeToString(csrBytes));
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] getEekChain(int supportedEekCurve) {
|
||||||
|
switch (supportedEekCurve) {
|
||||||
|
case RpcHardwareInfo.CURVE_25519:
|
||||||
|
return Base64.getDecoder().decode(EEK_ED25519_BASE64);
|
||||||
|
case RpcHardwareInfo.CURVE_P256:
|
||||||
|
return Base64.getDecoder().decode(EEK_P256_BASE64);
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("unsupported EEK curve: " + supportedEekCurve);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] composeCertificateRequestV1(DeviceInfo deviceInfo, byte[] challenge,
|
||||||
|
ProtectedData protectedData, byte[] keysToSignMac) throws CborException {
|
||||||
|
Array info = new Array()
|
||||||
|
.add(decode(deviceInfo.deviceInfo))
|
||||||
|
.add(new Map());
|
||||||
|
|
||||||
|
// COSE_Signature with the hmac-sha256 algorithm and without a payload.
|
||||||
|
Array mac = new Array()
|
||||||
|
.add(new ByteString(encode(
|
||||||
|
new Map().put(new UnsignedInteger(1), new UnsignedInteger(5)))))
|
||||||
|
.add(new Map())
|
||||||
|
.add(SimpleValue.NULL)
|
||||||
|
.add(new ByteString(keysToSignMac));
|
||||||
|
|
||||||
|
Array csr = new Array()
|
||||||
|
.add(info)
|
||||||
|
.add(new ByteString(challenge))
|
||||||
|
.add(decode(protectedData.protectedData))
|
||||||
|
.add(mac);
|
||||||
|
|
||||||
|
return encode(csr);
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] encode(DataItem item) throws CborException {
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
new CborEncoder(baos).encode(item);
|
||||||
|
return baos.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private DataItem decode(byte[] data) throws CborException {
|
||||||
|
ByteArrayInputStream bais = new ByteArrayInputStream(data);
|
||||||
|
return new CborDecoder(bais).decodeNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
static class Injector {
|
||||||
|
String[] getIrpcNames() {
|
||||||
|
return ServiceManager.getDeclaredInstances(IRemotelyProvisionedComponent.DESCRIPTOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
IRemotelyProvisionedComponent getIrpcBinder(String name) {
|
||||||
|
String irpc = IRemotelyProvisionedComponent.DESCRIPTOR + "/" + name;
|
||||||
|
IRemotelyProvisionedComponent binder =
|
||||||
|
IRemotelyProvisionedComponent.Stub.asInterface(
|
||||||
|
ServiceManager.waitForDeclaredService(irpc));
|
||||||
|
if (binder == null) {
|
||||||
|
throw new IllegalArgumentException("failed to find " + irpc);
|
||||||
|
}
|
||||||
|
return binder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,244 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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.server.security.rkp;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.doAnswer;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import android.hardware.security.keymint.DeviceInfo;
|
||||||
|
import android.hardware.security.keymint.IRemotelyProvisionedComponent;
|
||||||
|
import android.hardware.security.keymint.MacedPublicKey;
|
||||||
|
import android.hardware.security.keymint.ProtectedData;
|
||||||
|
import android.hardware.security.keymint.RpcHardwareInfo;
|
||||||
|
import android.os.Binder;
|
||||||
|
import android.os.FileUtils;
|
||||||
|
|
||||||
|
import androidx.test.runner.AndroidJUnit4;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Base64;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
public class RemoteProvisioningShellCommandTest {
|
||||||
|
|
||||||
|
private static class Injector extends RemoteProvisioningShellCommand.Injector {
|
||||||
|
|
||||||
|
private final Map<String, IRemotelyProvisionedComponent> mIrpcs;
|
||||||
|
|
||||||
|
Injector(Map irpcs) {
|
||||||
|
mIrpcs = irpcs;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
String[] getIrpcNames() {
|
||||||
|
return mIrpcs.keySet().toArray(new String[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
IRemotelyProvisionedComponent getIrpcBinder(String name) {
|
||||||
|
IRemotelyProvisionedComponent irpc = mIrpcs.get(name);
|
||||||
|
if (irpc == null) {
|
||||||
|
throw new IllegalArgumentException("failed to find " + irpc);
|
||||||
|
}
|
||||||
|
return irpc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class CommandResult {
|
||||||
|
private int mCode;
|
||||||
|
private String mOut;
|
||||||
|
private String mErr;
|
||||||
|
|
||||||
|
CommandResult(int code, String out, String err) {
|
||||||
|
mCode = code;
|
||||||
|
mOut = out;
|
||||||
|
mErr = err;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getCode() {
|
||||||
|
return mCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getOut() {
|
||||||
|
return mOut;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getErr() {
|
||||||
|
return mErr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static CommandResult exec(
|
||||||
|
RemoteProvisioningShellCommand cmd, String[] args) throws Exception {
|
||||||
|
File in = File.createTempFile("rpsct_in_", null);
|
||||||
|
File out = File.createTempFile("rpsct_out_", null);
|
||||||
|
File err = File.createTempFile("rpsct_err_", null);
|
||||||
|
int code = cmd.exec(
|
||||||
|
new Binder(),
|
||||||
|
new FileInputStream(in).getFD(),
|
||||||
|
new FileOutputStream(out).getFD(),
|
||||||
|
new FileOutputStream(err).getFD(),
|
||||||
|
args);
|
||||||
|
return new CommandResult(
|
||||||
|
code, FileUtils.readTextFile(out, 0, null), FileUtils.readTextFile(err, 0, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void list_zeroInstances() throws Exception {
|
||||||
|
RemoteProvisioningShellCommand cmd = new RemoteProvisioningShellCommand(
|
||||||
|
new Injector(Map.of()));
|
||||||
|
CommandResult res = exec(cmd, new String[] {"list"});
|
||||||
|
assertThat(res.getErr()).isEmpty();
|
||||||
|
assertThat(res.getCode()).isEqualTo(0);
|
||||||
|
assertThat(res.getOut()).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void list_oneInstances() throws Exception {
|
||||||
|
RemoteProvisioningShellCommand cmd = new RemoteProvisioningShellCommand(
|
||||||
|
new Injector(Map.of("default", mock(IRemotelyProvisionedComponent.class))));
|
||||||
|
CommandResult res = exec(cmd, new String[] {"list"});
|
||||||
|
assertThat(res.getErr()).isEmpty();
|
||||||
|
assertThat(res.getCode()).isEqualTo(0);
|
||||||
|
assertThat(Arrays.asList(res.getOut().split("\n"))).containsExactly("default");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void list_twoInstances() throws Exception {
|
||||||
|
RemoteProvisioningShellCommand cmd = new RemoteProvisioningShellCommand(
|
||||||
|
new Injector(Map.of(
|
||||||
|
"default", mock(IRemotelyProvisionedComponent.class),
|
||||||
|
"strongbox", mock(IRemotelyProvisionedComponent.class))));
|
||||||
|
CommandResult res = exec(cmd, new String[] {"list"});
|
||||||
|
assertThat(res.getErr()).isEmpty();
|
||||||
|
assertThat(res.getCode()).isEqualTo(0);
|
||||||
|
assertThat(Arrays.asList(res.getOut().split("\n"))).containsExactly("default", "strongbox");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void csr_hwVersion1_withChallenge() throws Exception {
|
||||||
|
IRemotelyProvisionedComponent defaultMock = mock(IRemotelyProvisionedComponent.class);
|
||||||
|
RpcHardwareInfo defaultInfo = new RpcHardwareInfo();
|
||||||
|
defaultInfo.versionNumber = 1;
|
||||||
|
defaultInfo.supportedEekCurve = RpcHardwareInfo.CURVE_25519;
|
||||||
|
when(defaultMock.getHardwareInfo()).thenReturn(defaultInfo);
|
||||||
|
doAnswer(invocation -> {
|
||||||
|
((DeviceInfo) invocation.getArgument(4)).deviceInfo = new byte[] {0x00};
|
||||||
|
((ProtectedData) invocation.getArgument(5)).protectedData = new byte[] {0x00};
|
||||||
|
return new byte[] {0x77, 0x77, 0x77, 0x77};
|
||||||
|
}).when(defaultMock).generateCertificateRequest(
|
||||||
|
anyBoolean(), any(), any(), any(), any(), any());
|
||||||
|
|
||||||
|
RemoteProvisioningShellCommand cmd = new RemoteProvisioningShellCommand(
|
||||||
|
new Injector(Map.of("default", defaultMock)));
|
||||||
|
CommandResult res = exec(cmd, new String[] {
|
||||||
|
"csr", "--challenge", "dGVzdHRlc3R0ZXN0dGVzdA==", "default"});
|
||||||
|
verify(defaultMock).generateCertificateRequest(
|
||||||
|
/*test_mode=*/eq(false),
|
||||||
|
eq(new MacedPublicKey[0]),
|
||||||
|
eq(Base64.getDecoder().decode(RemoteProvisioningShellCommand.EEK_ED25519_BASE64)),
|
||||||
|
eq(new byte[] {
|
||||||
|
0x74, 0x65, 0x73, 0x74, 0x74, 0x65, 0x73, 0x74,
|
||||||
|
0x74, 0x65, 0x73, 0x74, 0x74, 0x65, 0x73, 0x74}),
|
||||||
|
any(DeviceInfo.class),
|
||||||
|
any(ProtectedData.class));
|
||||||
|
assertThat(res.getErr()).isEmpty();
|
||||||
|
assertThat(res.getCode()).isEqualTo(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void csr_hwVersion2_withChallenge() throws Exception {
|
||||||
|
IRemotelyProvisionedComponent defaultMock = mock(IRemotelyProvisionedComponent.class);
|
||||||
|
RpcHardwareInfo defaultInfo = new RpcHardwareInfo();
|
||||||
|
defaultInfo.versionNumber = 2;
|
||||||
|
defaultInfo.supportedEekCurve = RpcHardwareInfo.CURVE_P256;
|
||||||
|
when(defaultMock.getHardwareInfo()).thenReturn(defaultInfo);
|
||||||
|
doAnswer(invocation -> {
|
||||||
|
((DeviceInfo) invocation.getArgument(4)).deviceInfo = new byte[] {0x00};
|
||||||
|
((ProtectedData) invocation.getArgument(5)).protectedData = new byte[] {0x00};
|
||||||
|
return new byte[] {0x77, 0x77, 0x77, 0x77};
|
||||||
|
}).when(defaultMock).generateCertificateRequest(
|
||||||
|
anyBoolean(), any(), any(), any(), any(), any());
|
||||||
|
|
||||||
|
RemoteProvisioningShellCommand cmd = new RemoteProvisioningShellCommand(
|
||||||
|
new Injector(Map.of("default", defaultMock)));
|
||||||
|
CommandResult res = exec(cmd, new String[] {
|
||||||
|
"csr", "--challenge", "dGVzdHRlc3R0ZXN0dGVzdA==", "default"});
|
||||||
|
verify(defaultMock).generateCertificateRequest(
|
||||||
|
/*test_mode=*/eq(false),
|
||||||
|
eq(new MacedPublicKey[0]),
|
||||||
|
eq(Base64.getDecoder().decode(RemoteProvisioningShellCommand.EEK_P256_BASE64)),
|
||||||
|
eq(new byte[] {
|
||||||
|
0x74, 0x65, 0x73, 0x74, 0x74, 0x65, 0x73, 0x74,
|
||||||
|
0x74, 0x65, 0x73, 0x74, 0x74, 0x65, 0x73, 0x74}),
|
||||||
|
any(DeviceInfo.class),
|
||||||
|
any(ProtectedData.class));
|
||||||
|
assertThat(res.getErr()).isEmpty();
|
||||||
|
assertThat(res.getCode()).isEqualTo(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void csr_hwVersion3_withoutChallenge() throws Exception {
|
||||||
|
IRemotelyProvisionedComponent defaultMock = mock(IRemotelyProvisionedComponent.class);
|
||||||
|
RpcHardwareInfo defaultInfo = new RpcHardwareInfo();
|
||||||
|
defaultInfo.versionNumber = 3;
|
||||||
|
when(defaultMock.getHardwareInfo()).thenReturn(defaultInfo);
|
||||||
|
when(defaultMock.generateCertificateRequestV2(any(), any()))
|
||||||
|
.thenReturn(new byte[] {0x68, 0x65, 0x6c, 0x6c, 0x6f});
|
||||||
|
|
||||||
|
RemoteProvisioningShellCommand cmd = new RemoteProvisioningShellCommand(
|
||||||
|
new Injector(Map.of("default", defaultMock)));
|
||||||
|
CommandResult res = exec(cmd, new String[] {"csr", "default"});
|
||||||
|
verify(defaultMock).generateCertificateRequestV2(new MacedPublicKey[0], new byte[0]);
|
||||||
|
assertThat(res.getErr()).isEmpty();
|
||||||
|
assertThat(res.getCode()).isEqualTo(0);
|
||||||
|
assertThat(res.getOut()).isEqualTo("aGVsbG8=\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void csr_hwVersion3_withChallenge() throws Exception {
|
||||||
|
IRemotelyProvisionedComponent defaultMock = mock(IRemotelyProvisionedComponent.class);
|
||||||
|
RpcHardwareInfo defaultInfo = new RpcHardwareInfo();
|
||||||
|
defaultInfo.versionNumber = 3;
|
||||||
|
when(defaultMock.getHardwareInfo()).thenReturn(defaultInfo);
|
||||||
|
when(defaultMock.generateCertificateRequestV2(any(), any()))
|
||||||
|
.thenReturn(new byte[] {0x68, 0x69});
|
||||||
|
|
||||||
|
RemoteProvisioningShellCommand cmd = new RemoteProvisioningShellCommand(
|
||||||
|
new Injector(Map.of("default", defaultMock)));
|
||||||
|
CommandResult res = exec(cmd, new String[] {"csr", "--challenge", "dHJpYWw=", "default"});
|
||||||
|
verify(defaultMock).generateCertificateRequestV2(
|
||||||
|
new MacedPublicKey[0], new byte[] {0x74, 0x72, 0x69, 0x61, 0x6c});
|
||||||
|
assertThat(res.getErr()).isEmpty();
|
||||||
|
assertThat(res.getCode()).isEqualTo(0);
|
||||||
|
assertThat(res.getOut()).isEqualTo("aGk=\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user