Add native code for cert munging.

Compsvc returns a self-signed cert (from Keystore), but we only want
the public key. Extracting this in Rust is non-trivial, so instead we
use existing support in BoringSSL in native code. (The details are
copied from compos_key_cmd.cpp, which in turn were copied from the
now-deleted FakeCompOS in odsign.)

We could alternatively do this in compsvc itself, but I was slightly
more reluctant to introduce native code there.

Bug: 186126194
Test: Run composd_cmd twice, check it accepts the key pair it generated.
Change-Id: I3faab9a7ada149d7f2776c2fb4d2656837c95e6f
diff --git a/compos/composd/Android.bp b/compos/composd/Android.bp
index 30d07ab..0a25f05 100644
--- a/compos/composd/Android.bp
+++ b/compos/composd/Android.bp
@@ -15,6 +15,7 @@
         "libanyhow",
         "libbinder_rs",
         "libcompos_common",
+        "libcomposd_native_rust",
         "libnum_traits",
         "liblog_rust",
     ],
diff --git a/compos/composd/native/Android.bp b/compos/composd/native/Android.bp
new file mode 100644
index 0000000..ad0afd9
--- /dev/null
+++ b/compos/composd/native/Android.bp
@@ -0,0 +1,42 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+rust_library {
+    name: "libcomposd_native_rust",
+    crate_name: "composd_native",
+    srcs: ["lib.rs"],
+    rustlibs: [
+        "libcxx",
+    ],
+    static_libs: [
+        "libcomposd_native_cpp",
+    ],
+    shared_libs: ["libcrypto"],
+    apex_available: ["com.android.compos"],
+}
+
+cc_library_static {
+    name: "libcomposd_native_cpp",
+    srcs: ["composd_native.cpp"],
+    shared_libs: ["libcrypto"],
+    generated_headers: ["composd_native_header"],
+    generated_sources: ["composd_native_code"],
+    apex_available: ["com.android.compos"],
+}
+
+genrule {
+    name: "composd_native_code",
+    tools: ["cxxbridge"],
+    cmd: "$(location cxxbridge) $(in) >> $(out)",
+    srcs: ["lib.rs"],
+    out: ["composd_native_cxx_generated.cc"],
+}
+
+genrule {
+    name: "composd_native_header",
+    tools: ["cxxbridge"],
+    cmd: "$(location cxxbridge) $(in) --header >> $(out)",
+    srcs: ["lib.rs"],
+    out: ["lib.rs.h"],
+}
diff --git a/compos/composd/native/composd_native.cpp b/compos/composd/native/composd_native.cpp
new file mode 100644
index 0000000..ebed816
--- /dev/null
+++ b/compos/composd/native/composd_native.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2021 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 "composd_native.h"
+
+#include <openssl/evp.h>
+#include <openssl/mem.h>
+#include <openssl/sha.h>
+#include <openssl/x509.h>
+
+#include <algorithm>
+#include <iterator>
+
+using rust::Slice;
+using rust::String;
+
+namespace {
+KeyResult make_error(const char* message) {
+    return KeyResult{{}, message};
+}
+} // namespace
+
+KeyResult extract_rsa_public_key(rust::Slice<const uint8_t> der_certificate) {
+    auto data = der_certificate.data();
+    bssl::UniquePtr<X509> x509(d2i_X509(nullptr, &data, der_certificate.size()));
+    if (!x509) {
+        return make_error("Failed to parse certificate");
+    }
+    if (data != der_certificate.data() + der_certificate.size()) {
+        return make_error("Certificate has unexpected trailing data");
+    }
+
+    bssl::UniquePtr<EVP_PKEY> pkey(X509_get_pubkey(x509.get()));
+    if (EVP_PKEY_base_id(pkey.get()) != EVP_PKEY_RSA) {
+        return make_error("Subject key is not RSA");
+    }
+    RSA* rsa = EVP_PKEY_get0_RSA(pkey.get());
+    if (!rsa) {
+        return make_error("Failed to extract RSA key");
+    }
+
+    uint8_t* out = nullptr;
+    int size = i2d_RSAPublicKey(rsa, &out);
+    if (size < 0 || !out) {
+        return make_error("Failed to convert to RSAPublicKey");
+    }
+    bssl::UniquePtr<uint8_t> buffer(out);
+
+    KeyResult result;
+    result.key.reserve(size);
+    std::copy(out, out + size, std::back_inserter(result.key));
+    return result;
+}
diff --git a/compos/composd/native/composd_native.h b/compos/composd/native/composd_native.h
new file mode 100644
index 0000000..112ef73
--- /dev/null
+++ b/compos/composd/native/composd_native.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2021 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 "lib.rs.h"
+
+KeyResult extract_rsa_public_key(rust::Slice<const uint8_t> der_certificate);
diff --git a/compos/composd/native/lib.rs b/compos/composd/native/lib.rs
new file mode 100644
index 0000000..ace9600
--- /dev/null
+++ b/compos/composd/native/lib.rs
@@ -0,0 +1,38 @@
+// Copyright 2021, 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.
+
+//! Bindings native helpers for composd.
+
+pub use ffi::*;
+
+#[cxx::bridge]
+mod ffi {
+    /// Contains either a key or a reason why the key could not be extracted.
+    struct KeyResult {
+        /// The extracted key. If empty, the attempt to extract the key failed.
+        key: Vec<u8>,
+        /// A description of what went wrong if the attempt failed.
+        error: String,
+    }
+
+    unsafe extern "C++" {
+        include!("composd_native.h");
+
+        // SAFETY: The C++ implementation manages its own memory, and does not retain or abuse
+        // the der_certificate reference. cxx handles the mapping of the return value.
+
+        /// Parse the supplied DER X.509 certificate and extract the subject's RsaPublicKey.
+        fn extract_rsa_public_key(der_certificate: &[u8]) -> KeyResult;
+    }
+}
diff --git a/compos/composd/src/instance_starter.rs b/compos/composd/src/instance_starter.rs
index 5352250..ec95ff8 100644
--- a/compos/composd/src/instance_starter.rs
+++ b/compos/composd/src/instance_starter.rs
@@ -74,7 +74,7 @@
         let compos_instance = self.start_existing_instance();
         match compos_instance {
             Ok(_) => return compos_instance,
-            Err(e) => warn!("Failed to start {}: {}", self.instance_name, e),
+            Err(e) => warn!("Failed to start: {}", e),
         }
 
         self.start_new_instance(service)
@@ -84,6 +84,8 @@
         // No point even trying if the files we need aren't there.
         self.check_files_exist()?;
 
+        info!("Starting {} CompOs instance", self.instance_name);
+
         let key_blob = fs::read(&self.key_blob).context("Reading private key blob")?;
         let public_key = fs::read(&self.public_key).context("Reading public key")?;
 
@@ -119,8 +121,13 @@
 
         let key_data = service.generateSigningKey().context("Generating signing key")?;
         fs::write(&self.key_blob, &key_data.keyBlob).context("Writing key blob")?;
-        // TODO: Extract public key from cert
-        fs::write(&self.public_key, &key_data.certificate).context("Writing public key")?;
+
+        let key_result = composd_native::extract_rsa_public_key(&key_data.certificate);
+        let rsa_public_key = key_result.key;
+        if rsa_public_key.is_empty() {
+            bail!("Failed to extract public key from certificate: {}", key_result.error);
+        }
+        fs::write(&self.public_key, &rsa_public_key).context("Writing public key")?;
 
         // We don't need to verify the key, since we just generated it and have it in memory.