Merge "Add command line interface for remote_provisioning" into udc-dev
This commit is contained in:
@@ -161,6 +161,7 @@ java_library_static {
|
||||
"android.hardware.health-V2-java", // AIDL
|
||||
"android.hardware.health-translate-java",
|
||||
"android.hardware.light-V1-java",
|
||||
"android.hardware.security.rkp-V3-java",
|
||||
"android.hardware.tv.cec-V1.1-java",
|
||||
"android.hardware.tv.hdmi.cec-V1-java",
|
||||
"android.hardware.tv.hdmi.connection-V1-java",
|
||||
@@ -177,6 +178,7 @@ java_library_static {
|
||||
"android.hardware.power.stats-V2-java",
|
||||
"android.hardware.power-V4-java",
|
||||
"android.hidl.manager-V1.2-java",
|
||||
"cbor-java",
|
||||
"icu4j_calendar_astronomer",
|
||||
"netd-client",
|
||||
"overlayable_policy_aidl-java",
|
||||
|
||||
@@ -19,14 +19,18 @@ package com.android.server.security.rkp;
|
||||
import android.content.Context;
|
||||
import android.os.Binder;
|
||||
import android.os.OutcomeReceiver;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.RemoteException;
|
||||
import android.security.rkp.IGetRegistrationCallback;
|
||||
import android.security.rkp.IRemoteProvisioning;
|
||||
import android.security.rkp.service.RegistrationProxy;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.util.DumpUtils;
|
||||
import com.android.server.SystemService;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
@@ -97,5 +101,18 @@ public class RemoteProvisioningService extends SystemService {
|
||||
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,250 @@
|
||||
/*
|
||||
* 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);
|
||||
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");
|
||||
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