Add provisioner CLI and its interface.
Change-Id: If7682c037232844568ba88a0ed38e26924e1c10e
diff --git a/provisioner/Android.bp b/provisioner/Android.bp
new file mode 100644
index 0000000..c1c8d15
--- /dev/null
+++ b/provisioner/Android.bp
@@ -0,0 +1,44 @@
+//
+// Copyright (C) 2020 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.
+//
+
+aidl_interface {
+ name: "android.security.provisioner",
+ unstable: true,
+ local_include_dir: "binder",
+ srcs: [
+ "binder/android/security/provisioner/*.aidl",
+ ],
+ backend: {
+ java: {
+ platform_apis: true,
+ },
+ cpp: {
+ enabled: false,
+ },
+ ndk: {
+ enabled: false,
+ },
+ },
+}
+
+java_binary {
+ name: "provisioner_cli",
+ wrapper: "provisioner_cli",
+ srcs: ["src/com/android/commands/provisioner/**/*.java"],
+ static_libs: [
+ "android.security.provisioner-java",
+ ],
+}
diff --git a/provisioner/binder/android/security/provisioner/IProvisionerService.aidl b/provisioner/binder/android/security/provisioner/IProvisionerService.aidl
new file mode 100644
index 0000000..f81e9ab
--- /dev/null
+++ b/provisioner/binder/android/security/provisioner/IProvisionerService.aidl
@@ -0,0 +1,27 @@
+/**
+ * Copyright (c) 2020, 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 android.security.provisioner;
+
+/**
+ * @hide
+ */
+interface IProvisionerService {
+ byte[] getCertificateRequest(in boolean testMode,
+ in int keyCount,
+ in byte[] endpointEncryptionKey,
+ in byte[] challenge) = 0;
+}
diff --git a/provisioner/provisioner_cli b/provisioner/provisioner_cli
new file mode 100755
index 0000000..7b53d6e
--- /dev/null
+++ b/provisioner/provisioner_cli
@@ -0,0 +1,21 @@
+#!/system/bin/sh
+#
+# Copyright (C) 2020 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.
+#
+# Script to start "provisioner_cli" on the device.
+#
+base=/system
+export CLASSPATH=$base/framework/provisioner_cli.jar
+exec app_process $base/bin com.android.commands.provisioner.Cli "$@"
diff --git a/provisioner/src/com/android/commands/provisioner/Cli.java b/provisioner/src/com/android/commands/provisioner/Cli.java
new file mode 100644
index 0000000..62afdac
--- /dev/null
+++ b/provisioner/src/com/android/commands/provisioner/Cli.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2020 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.commands.provisioner;
+
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.security.provisioner.IProvisionerService;
+
+import com.android.internal.os.BaseCommand;
+
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.lang.IllegalArgumentException;
+
+/**
+ * Contains the implementation of the remote provisioning command-line interface.
+ */
+public class Cli extends BaseCommand {
+ /**
+ * Creates an instance of the command-line interface and runs it. This is the entry point of
+ * the tool.
+ */
+ public static void main(String[] args) {
+ new Cli().run(args);
+ }
+
+ /**
+ * Runs the command requested by the invoker. It parses the very first required argument, which
+ * is the command, and calls the appropriate handler.
+ */
+ @Override
+ public void onRun() throws Exception {
+ String cmd = nextArgRequired();
+ switch (cmd) {
+ case "get-req":
+ getRequest();
+ break;
+
+ case "help":
+ onShowUsage(System.out);
+ break;
+
+ default:
+ throw new IllegalArgumentException("unknown command: " + cmd);
+ }
+ }
+
+ /**
+ * Retrieves a 'certificate request' from the provisioning service. The COSE-encoded
+ * 'certificate chain' describing the endpoint encryption key (EEK) to use for encryption is
+ * read from the standard input. The retrieved request is written to the standard output.
+ */
+ private void getRequest() throws Exception {
+ // Process options.
+ boolean test = false;
+ byte[] challenge = null;
+ int count = 0;
+ String arg;
+ while ((arg = nextArg()) != null) {
+ switch (arg) {
+ case "--test":
+ test = true;
+ break;
+
+ case "--challenge":
+ // TODO: We may need a different encoding of the challenge.
+ challenge = nextArgRequired().getBytes();
+ break;
+
+ case "--count":
+ count = Integer.parseInt(nextArgRequired());
+ if (count < 0) {
+ throw new IllegalArgumentException(
+ "--count must be followed by non-negative number");
+ }
+ break;
+
+ default:
+ throw new IllegalArgumentException("unknown argument: " + arg);
+ }
+ }
+
+ // Send the request over to the provisioning service and write the result to stdout.
+ byte[] res = getService().getCertificateRequest(test, count, readAll(System.in), challenge);
+ if (res != null) {
+ System.out.write(res);
+ }
+ }
+
+ /**
+ * Retrieves an implementation of the IProvisionerService interface. It allows the caller to
+ * call into the service via binder.
+ */
+ private static IProvisionerService getService() throws RemoteException {
+ IBinder binder = ServiceManager.getService("remote-provisioner");
+ if (binder == null) {
+ throw new RemoteException("Provisioning service is inaccessible");
+ }
+ return IProvisionerService.Stub.asInterface(binder);
+ }
+
+ /** Reads all data from the provided input stream and returns it as a byte array. */
+ private static byte[] readAll(InputStream in) throws IOException {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ byte[] buf = new byte[1024];
+ int read;
+ while ((read = in.read(buf)) != -1) {
+ out.write(buf, 0, read);
+ }
+ return out.toByteArray();
+ }
+
+ /**
+ * Writes the usage information to the given stream. This is displayed to users of the tool when
+ * they ask for help or when they pass incorrect arguments to the tool.
+ */
+ @Override
+ public void onShowUsage(PrintStream out) {
+ out.println(
+ "Usage: provisioner_cli <command> [options]\n" +
+ "Commands: help\n" +
+ " get-req [--count <n>] [--test] [--challenge <v>]");
+ }
+}