Implement Secretkeeper bootloader client library am: 97f9dc99fe

Original change: https://android-review.googlesource.com/c/trusty/external/trusty/+/2919725

Change-Id: Ied7b26a3fe6243ee0d7412e8982903ac155ca24f
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/interface/include/interface/secretkeeper/secretkeeper.h b/interface/include/interface/secretkeeper/secretkeeper.h
new file mode 100644
index 0000000..f8dd3d6
--- /dev/null
+++ b/interface/include/interface/secretkeeper/secretkeeper.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2024 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.
+ */
+
+#pragma once
+
+#include <lk/compiler.h>
+#include <stdint.h>
+
+/* Note: The definitive source for the message interface here is is in
+ * trusty/user/app/secretkeeper/lib.rs (TIPC port details) and
+ * system/secretkeeper/core/src/ta/bootloader.rs (message format).
+ * This is a manual translation into C.
+ */
+
+#define SECRETKEEPER_BL_PORT "com.android.trusty.secretkeeper.bootloader"
+
+/**
+ * enum secretkeeper_cmd - Secretkeeper commands.
+ * @SECRETKEEPER_RESPONSE_MARKER: Bit indicating that this is a response.
+ * @SECRETKEEPER_CMD_GET_IDENTITY: Get the per-boot identity (public key) of
+ *                                 Secretkeeper.
+ */
+enum secretkeeper_cmd {
+    SECRETKEEPER_RESPONSE_MARKER = 0x1 << 31,
+    SECRETKEEPER_CMD_GET_IDENTITY = 1,
+};
+
+/**
+ * struct secretkeeper_req_hdr - Generic header for all Secretkeeper requests.
+ * Note that all fields are stored in network byte order (big endian).
+ * @cmd:       The command to be run. Commands are described in
+ *             enum secretkeeper_cmd.
+ */
+struct secretkeeper_req_hdr {
+    uint32_t cmd;
+};
+STATIC_ASSERT(sizeof(struct secretkeeper_req_hdr) == 4);
+
+/**
+ * struct secretkeeper_resp_hdr - Generic header for all Secretkeeper responses.
+ * Note that all fields are stored in network byte order (big endian).
+ * Any response payload immediately follows this struct.
+ * @cmd:          Command identifier - %SECRETKEEPER_RESPONSE_MARKER or'ed with
+ *                the command identifier of the corresponding request.
+ * @error_code:   0 if the request succeeded, or an indication of how it failed.
+ */
+struct secretkeeper_resp_hdr {
+    uint32_t cmd;
+    uint32_t error_code;
+};
+STATIC_ASSERT(sizeof(struct secretkeeper_resp_hdr) == 8);
diff --git a/ql-tipc/include/trusty/secretkeeper.h b/ql-tipc/include/trusty/secretkeeper.h
new file mode 100644
index 0000000..47f0c0b
--- /dev/null
+++ b/ql-tipc/include/trusty/secretkeeper.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2024 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.
+ */
+
+#pragma once
+
+#include <interface/secretkeeper/secretkeeper.h>
+#include <trusty/trusty_ipc.h>
+
+/*
+ * Initialize SECRETKEEPER TIPC client. Returns one of trusty_err.
+ *
+ * @dev: trusty_ipc_dev
+ */
+int secretkeeper_tipc_init(struct trusty_ipc_dev* dev);
+
+/*
+ * Shutdown SECRETKEEPER TIPC client.
+ */
+void secretkeeper_tipc_shutdown(void);
+
+/**
+ * Retrieves the identity (public key) of the Secretkeeper implementation.
+ * The key is represented as a CBOR-encoded COSE_key, as one of as a
+ * PubKeyEd25519 / PubKeyECDSA256 / PubKeyECDSA384. See
+ * https://cs.android.com/android/platform/superproject/main/+/main:hardware/interfaces/security/rkp/aidl/android/hardware/security/keymint/generateCertificateRequestV2.cddl
+ * @identity_buf_size:          Size of the buffer pointed to by @identity_buf.
+ * @identity_buf:               Pointer to a buffer to store the CBOR-encoded
+ *                              public key.
+ * @identity_size:              On return the actual size of the public key.
+ */
+int secretkeeper_get_identity(size_t identity_buf_size,
+                              uint8_t identity_buf[],
+                              size_t* identity_size);
diff --git a/ql-tipc/secretkeeper.c b/ql-tipc/secretkeeper.c
new file mode 100644
index 0000000..a12d50f
--- /dev/null
+++ b/ql-tipc/secretkeeper.c
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2024 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.
+ */
+
+#include <endian.h>
+#include <trusty/secretkeeper.h>
+#include <trusty/trusty_ipc.h>
+#include <trusty/util.h>
+
+static struct trusty_ipc_chan secretkeeper_chan;
+static bool initialized;
+
+int secretkeeper_tipc_init(struct trusty_ipc_dev* dev) {
+    trusty_assert(dev);
+    trusty_assert(!initialized);
+    trusty_ipc_chan_init(&secretkeeper_chan, dev);
+
+    trusty_debug(
+            "In secretkeeper_tipc_init: connecting to secretkeeper bootloader service.\n");
+    int rc = trusty_ipc_connect(&secretkeeper_chan, SECRETKEEPER_BL_PORT,
+                                true /*wait*/);
+    if (rc < 0) {
+        trusty_error(
+                "In secretkeeper_tipc_init:: failed (%d) to connect to '%s'.\n",
+                rc, SECRETKEEPER_BL_PORT);
+        return rc;
+    }
+    initialized = true;
+    return TRUSTY_ERR_NONE;
+}
+
+void secretkeeper_tipc_shutdown(void) {
+    if (!initialized) {
+        return;
+    }
+    trusty_ipc_close(&secretkeeper_chan);
+    initialized = false;
+}
+
+static int send_header_only_request(struct secretkeeper_req_hdr* hdr,
+                                    size_t hdr_size) {
+    int num_iovec = 1;
+    struct trusty_ipc_iovec req_iov = {.base = hdr, .len = hdr_size};
+    return trusty_ipc_send(&secretkeeper_chan, &req_iov, num_iovec, true);
+}
+
+static int read_response_with_data(struct secretkeeper_req_hdr* hdr,
+                                   uint8_t* buf,
+                                   size_t buf_size,
+                                   size_t* out_size) {
+    struct secretkeeper_resp_hdr resp_hdr = {};
+
+    trusty_assert(buf);
+    trusty_assert(out_size);
+
+    int num_iovec = 2;
+    struct trusty_ipc_iovec resp_iovecs[2] = {
+            {.base = &resp_hdr, .len = sizeof(resp_hdr)},
+            {.base = buf, .len = buf_size},
+    };
+
+    int rc = trusty_ipc_recv(&secretkeeper_chan, resp_iovecs, num_iovec, true);
+    if (rc < 0) {
+        trusty_error("Secretkeeper: Failure on receiving response: %d\n", rc);
+        return rc;
+    }
+
+    size_t bytes = rc;
+    if (bytes < sizeof(resp_hdr)) {
+        trusty_error("Secretkeeper: Invalid response size (%d).\n", rc);
+        return TRUSTY_ERR_GENERIC;
+    }
+
+    if (resp_hdr.cmd != (hdr->cmd | htonl(SECRETKEEPER_RESPONSE_MARKER))) {
+        trusty_error("Secretkeeper: Unknown response cmd: %x\n",
+                     ntohl(resp_hdr.cmd));
+        return TRUSTY_ERR_GENERIC;
+    }
+
+    if (resp_hdr.error_code != 0) {
+        trusty_error("Secretkeeper: Error code (%d) is not zero.\n",
+                     ntohl(resp_hdr.error_code));
+        return TRUSTY_ERR_GENERIC;
+    }
+
+    *out_size = bytes - sizeof(resp_hdr);
+    return rc;
+}
+
+int secretkeeper_get_identity(size_t identity_buf_size,
+                              uint8_t identity_buf[],
+                              size_t* identity_size) {
+    trusty_assert(dice_artifacts);
+    trusty_assert(dice_artifacts_size);
+
+    struct secretkeeper_req_hdr hdr;
+    hdr.cmd = htonl(SECRETKEEPER_CMD_GET_IDENTITY);
+
+    int rc = send_header_only_request(&hdr, sizeof(hdr));
+
+    if (rc < 0) {
+        trusty_error(
+                "In secretkeeper_get_identity: failed (%d) to send request to Secretkeeper.",
+                rc);
+        return rc;
+    }
+
+    rc = read_response_with_data(&hdr, identity_buf, identity_buf_size,
+                                 identity_size);
+
+    if (rc < 0) {
+        trusty_error(
+                "In secretkeeper_get_identity: failed (%d) to read the response.",
+                rc);
+        return rc;
+    }
+
+    return TRUSTY_ERR_NONE;
+}
diff --git a/test-runner/rules.mk b/test-runner/rules.mk
index f81be68..82f82ce 100644
--- a/test-runner/rules.mk
+++ b/test-runner/rules.mk
@@ -64,6 +64,7 @@
 	$(QL_TIPC)/keymaster.c \
 	$(QL_TIPC)/keymaster_serializable.c \
 	$(QL_TIPC)/rpmb_proxy.c \
+	$(QL_TIPC)/secretkeeper.c \
 	$(QL_TIPC)/trusty_dev_common.c \
 	$(QL_TIPC)/util.c \
 
diff --git a/test-runner/test-runner-sysdeps.c b/test-runner/test-runner-sysdeps.c
index d99c5da..93466aa 100644
--- a/test-runner/test-runner-sysdeps.c
+++ b/test-runner/test-runner-sysdeps.c
@@ -56,6 +56,18 @@
     return dest;
 }
 
+int memcmp(const void* lhs, const void* rhs, size_t count) {
+    const uint8_t* l = lhs;
+    const uint8_t* r = rhs;
+    while (count--) {
+        if (*l != *r) {
+            return *l > *r ? +1 : -1;
+        }
+        ++l, ++r;
+    }
+    return 0;
+}
+
 char* strcpy(char* dest, char const* src) {
     char* ret = dest;
     while ((*dest++ = *src++) != '\0') {
diff --git a/test-runner/test-runner.c b/test-runner/test-runner.c
index 25e6268..9ef66b8 100644
--- a/test-runner/test-runner.c
+++ b/test-runner/test-runner.c
@@ -31,6 +31,7 @@
 #include <trusty/hwbcc.h>
 #include <trusty/keymaster.h>
 #include <trusty/rpmb.h>
+#include <trusty/secretkeeper.h>
 #include <trusty/trusty_dev.h>
 #include <trusty/trusty_ipc.h>
 #include <utils.h>
@@ -59,6 +60,7 @@
  * Any return from this function indicates an internal error. The caller is
  * responsible for reporting the error. It currently returns to the host with
  * 2 as the exit code.
+ * No attempt is made to clean up before returning.
  */
 void boot(int cpu) {
     int ret;
@@ -222,6 +224,56 @@
 
     hwbcc_tipc_shutdown();
 
+    /**
+     * Check that Secretkeeper can be connected to.
+     */
+    ret = secretkeeper_tipc_init(ipc_dev);
+    if (ret != 0) {
+        log_msg("secretkeeper_tipc_init failed.\n");
+        return;
+    }
+
+    /**
+     * Check that we can retrieve the public key and that it has the expected
+     * form.
+     */
+    uint8_t sk_identity[100];
+    size_t sk_identity_size = 0;
+    ret = secretkeeper_get_identity(sizeof(sk_identity), sk_identity,
+                                    &sk_identity_size);
+    if (ret != 0) {
+        log_msg("secretkeeper_get_identity failed.\n");
+        return;
+    }
+
+    const size_t ed25519_key_size = 32;
+    const uint8_t expected_cbor_prefix[] = {
+            // Map with 4 entries (see PubKeyEd25519)
+            0xa4,
+            // Key type: 1: 1
+            0x01, 0x01,
+            // Algorithm: 3: -8
+            0x03, 0x27,
+            // Curve: -1: 6
+            0x20, 0x06,
+            // Public key: -2, 32 byte bstr (which follows)
+            0x21, 0x58, 0x20};
+    const size_t expected_identity_size =
+            sizeof(expected_cbor_prefix) + ed25519_key_size;
+
+    if (sk_identity_size != expected_identity_size) {
+        log_msg("Unexpected sk_identity_size\n");
+        return;
+    }
+
+    if (memcmp(sk_identity, expected_cbor_prefix,
+               sizeof(expected_cbor_prefix))) {
+        log_msg("sk_identity does not start with expected prefix\n");
+        return;
+    }
+
+    secretkeeper_tipc_shutdown();
+
     ret = arch_start_secondary_cpus();
     if (ret) {
         log_msg("Failed to start secondary CPUs\n");