From 249b56d80666af321c0f36e02e7582bebbe8655e Mon Sep 17 00:00:00 2001 From: Andrew Scull Date: Mon, 17 Apr 2023 13:19:45 +0000 Subject: [PATCH 1/2] Add command line interface for remote_provisioning Provide an ADB interface to interact with the remote_provisioning service for diagnostic purposes. The service reports details of the IRPC components in dumpsys and allows ADB to query the IRPC instances and request a CSR from each of them. Test: adb shell dumpsys remote_provisioning Test: adb shell cmd remote_provisioning Test: atest RemoteProvisioningShellCommandTest Bug: 265747549 Change-Id: I593a4b599f4fc8e27d7f79d1d5f3955eabc9641d Merged-In: I593a4b599f4fc8e27d7f79d1d5f3955eabc9641d --- services/core/Android.bp | 2 + .../rkp/RemoteProvisioningService.java | 17 ++ .../rkp/RemoteProvisioningShellCommand.java | 250 ++++++++++++++++++ .../RemoteProvisioningShellCommandTest.java | 244 +++++++++++++++++ 4 files changed, 513 insertions(+) create mode 100644 services/core/java/com/android/server/security/rkp/RemoteProvisioningShellCommand.java create mode 100644 services/tests/RemoteProvisioningServiceTests/src/com/android/server/security/rkp/RemoteProvisioningShellCommandTest.java diff --git a/services/core/Android.bp b/services/core/Android.bp index 6a14deabee4bb..d885cd197387a 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -155,6 +155,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", @@ -171,6 +172,7 @@ java_library_static { "android.hardware.power-V3-java", "android.hidl.manager-V1.2-java", "capture_state_listener-aidl-java", + "cbor-java", "icu4j_calendar_astronomer", "netd-client", "overlayable_policy_aidl-java", diff --git a/services/core/java/com/android/server/security/rkp/RemoteProvisioningService.java b/services/core/java/com/android/server/security/rkp/RemoteProvisioningService.java index cd1a968a8e890..97e463646fdc5 100644 --- a/services/core/java/com/android/server/security/rkp/RemoteProvisioningService.java +++ b/services/core/java/com/android/server/security/rkp/RemoteProvisioningService.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); + } } } diff --git a/services/core/java/com/android/server/security/rkp/RemoteProvisioningShellCommand.java b/services/core/java/com/android/server/security/rkp/RemoteProvisioningShellCommand.java new file mode 100644 index 0000000000000..71eca691dba78 --- /dev/null +++ b/services/core/java/com/android/server/security/rkp/RemoteProvisioningShellCommand.java @@ -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; + } + } +} diff --git a/services/tests/RemoteProvisioningServiceTests/src/com/android/server/security/rkp/RemoteProvisioningShellCommandTest.java b/services/tests/RemoteProvisioningServiceTests/src/com/android/server/security/rkp/RemoteProvisioningShellCommandTest.java new file mode 100644 index 0000000000000..77c3396464000 --- /dev/null +++ b/services/tests/RemoteProvisioningServiceTests/src/com/android/server/security/rkp/RemoteProvisioningShellCommandTest.java @@ -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 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"); + } +} From 6587776a3cd236da773f392344e108aaadee9b9e Mon Sep 17 00:00:00 2001 From: Andrew Scull Date: Thu, 20 Apr 2023 14:10:25 +0000 Subject: [PATCH 2/2] Tweak remote_provisioning command line interface Only print an IRPC's minimum number of keys on versions of the HAL where it will be meaningful and initialized. Also indicate which options could not be understood. Test: atest RemoteProvisioningShellCommandTest Bug: 265747549 Change-Id: I0dbcd6af5a5de20783b6d57874fb67801a39240d --- .../server/security/rkp/RemoteProvisioningShellCommand.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/services/core/java/com/android/server/security/rkp/RemoteProvisioningShellCommand.java b/services/core/java/com/android/server/security/rkp/RemoteProvisioningShellCommand.java index 71eca691dba78..bc3908452bc1f 100644 --- a/services/core/java/com/android/server/security/rkp/RemoteProvisioningShellCommand.java +++ b/services/core/java/com/android/server/security/rkp/RemoteProvisioningShellCommand.java @@ -134,7 +134,9 @@ class RemoteProvisioningShellCommand extends ShellCommand { pw.println("supportedEekCurve=" + info.supportedEekCurve); } pw.println("uniqueId=" + info.uniqueId); - pw.println("supportedNumKeysInCsr=" + info.supportedNumKeysInCsr); + if (info.versionNumber >= 3) { + pw.println("supportedNumKeysInCsr=" + info.supportedNumKeysInCsr); + } } private int list() throws RemoteException { @@ -153,7 +155,7 @@ class RemoteProvisioningShellCommand extends ShellCommand { challenge = Base64.getDecoder().decode(getNextArgRequired()); break; default: - getErrPrintWriter().println("error: unknown option"); + getErrPrintWriter().println("error: unknown option " + opt); return ERROR; } }