Rewrite key management & signing
Extend compos_helper to support signing, use it from CompOS.
Expose the public key from the VM. Rename compos_verify_key to
compos_verify and get it to verify the signature against the current
instance's public key.
Also move DICE access to compos_key_main. There's no use having it in
the library - neither the tests nor compos_verify can use it - and it
complicates the build rules.
There's a lot more that can be deleted, but I'll do that in a
follow-up; this is big enough already.
Bug: 218494522
Test: atest CompOsSigningHostTest CompOsDenialHostTest
Change-Id: I2d71f68a595d5ddadb2e7b16937fa6855f5db0ab
diff --git a/compos/compos_key_helper/Android.bp b/compos/compos_key_helper/Android.bp
index c53d88d..a932b40 100644
--- a/compos/compos_key_helper/Android.bp
+++ b/compos/compos_key_helper/Android.bp
@@ -8,7 +8,6 @@
shared_libs: [
"libbase",
- "libbinder_ndk",
"libcrypto",
],
}
@@ -17,11 +16,7 @@
name: "libcompos_key",
defaults: ["compos_key_defaults"],
srcs: ["compos_key.cpp"],
-
- shared_libs: [
- "android.hardware.security.dice-V1-ndk",
- "android.security.dice-ndk",
- ],
+ export_include_dirs: ["."],
}
cc_binary {
@@ -31,7 +26,9 @@
static_libs: ["libcompos_key"],
shared_libs: [
+ "android.hardware.security.dice-V1-ndk",
"android.security.dice-ndk",
+ "libbinder_ndk",
],
}
diff --git a/compos/compos_key_helper/compos_key.cpp b/compos/compos_key_helper/compos_key.cpp
index 84a736d..2e3252c 100644
--- a/compos/compos_key_helper/compos_key.cpp
+++ b/compos/compos_key_helper/compos_key.cpp
@@ -16,24 +16,20 @@
#include "compos_key.h"
-#include <aidl/android/security/dice/IDiceNode.h>
-#include <android/binder_auto_utils.h>
-#include <android/binder_manager.h>
#include <openssl/digest.h>
#include <openssl/hkdf.h>
#include <openssl/mem.h>
-#include <unistd.h>
-using aidl::android::hardware::security::dice::BccHandover;
-using aidl::android::hardware::security::dice::InputValues;
-using aidl::android::security::dice::IDiceNode;
using android::base::ErrnoError;
using android::base::Error;
using android::base::Result;
+using compos_key::Ed25519KeyPair;
+using compos_key::Signature;
// Used to ensure the key we derive is distinct from any other.
constexpr const char* kSigningKeyInfo = "CompOS signing key";
+namespace compos_key {
Result<Ed25519KeyPair> deriveKeyFromSecret(const uint8_t* secret, size_t secret_size) {
// Ed25519 private keys are derived from a 32 byte seed:
// https://datatracker.ietf.org/doc/html/rfc8032#section-5.1.5
@@ -64,22 +60,4 @@
size_t data_size) {
return ED25519_verify(data, data_size, signature.data(), public_key.data()) == 1;
}
-
-Result<Ed25519KeyPair> deriveKeyFromDice() {
- ndk::SpAIBinder binder{AServiceManager_getService("android.security.dice.IDiceNode")};
- auto dice_node = IDiceNode::fromBinder(binder);
- if (!dice_node) {
- return Error() << "Unable to connect to IDiceNode";
- }
-
- const std::vector<InputValues> empty_input_values;
- BccHandover bcc;
- auto status = dice_node->derive(empty_input_values, &bcc);
- if (!status.isOk()) {
- return Error() << "Derive failed: " << status.getDescription();
- }
-
- // We use the sealing CDI because we want stability - the key needs to be the same
- // for any instance of the "same" VM.
- return deriveKeyFromSecret(bcc.cdiSeal.data(), bcc.cdiSeal.size());
-}
+} // namespace compos_key
diff --git a/compos/compos_key_helper/compos_key.h b/compos/compos_key_helper/compos_key.h
index 520f846..e9c6061 100644
--- a/compos/compos_key_helper/compos_key.h
+++ b/compos/compos_key_helper/compos_key.h
@@ -21,6 +21,7 @@
#include <array>
+namespace compos_key {
using PrivateKey = std::array<uint8_t, ED25519_PRIVATE_KEY_LEN>;
using PublicKey = std::array<uint8_t, ED25519_PUBLIC_KEY_LEN>;
using Signature = std::array<uint8_t, ED25519_SIGNATURE_LEN>;
@@ -33,10 +34,9 @@
android::base::Result<Ed25519KeyPair> deriveKeyFromSecret(const uint8_t* secret,
size_t secret_size);
-android::base::Result<Ed25519KeyPair> deriveKeyFromDice();
-
android::base::Result<Signature> sign(const PrivateKey& private_key, const uint8_t* data,
size_t data_size);
bool verify(const PublicKey& public_key, const Signature& signature, const uint8_t* data,
size_t data_size);
+} // namespace compos_key
diff --git a/compos/compos_key_helper/compos_key_main.cpp b/compos/compos_key_helper/compos_key_main.cpp
index 70f7539..a0d0b18 100644
--- a/compos/compos_key_helper/compos_key_main.cpp
+++ b/compos/compos_key_helper/compos_key_main.cpp
@@ -14,39 +14,102 @@
* limitations under the License.
*/
+#include <aidl/android/security/dice/IDiceNode.h>
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android/binder_auto_utils.h>
+#include <android/binder_manager.h>
#include <unistd.h>
-#include <iostream>
#include <string_view>
#include "compos_key.h"
-using android::base::ErrnoError;
+using aidl::android::hardware::security::dice::BccHandover;
+using aidl::android::hardware::security::dice::InputValues;
+using aidl::android::security::dice::IDiceNode;
+using android::base::Error;
+using android::base::ReadFdToString;
+using android::base::Result;
using android::base::WriteFully;
using namespace std::literals;
+using compos_key::Ed25519KeyPair;
+
+namespace {
+Result<Ed25519KeyPair> deriveKeyFromDice() {
+ ndk::SpAIBinder binder{AServiceManager_getService("android.security.dice.IDiceNode")};
+ auto dice_node = IDiceNode::fromBinder(binder);
+ if (!dice_node) {
+ return Error() << "Unable to connect to IDiceNode";
+ }
+
+ const std::vector<InputValues> empty_input_values;
+ BccHandover bcc;
+ auto status = dice_node->derive(empty_input_values, &bcc);
+ if (!status.isOk()) {
+ return Error() << "Derive failed: " << status.getDescription();
+ }
+
+ // We use the sealing CDI because we want stability - the key needs to be the same
+ // for any instance of the "same" VM.
+ return compos_key::deriveKeyFromSecret(bcc.cdiSeal.data(), bcc.cdiSeal.size());
+}
+
+int write_public_key() {
+ auto key_pair = deriveKeyFromDice();
+ if (!key_pair.ok()) {
+ LOG(ERROR) << key_pair.error();
+ return 1;
+ }
+ if (!WriteFully(STDOUT_FILENO, key_pair->public_key.data(), key_pair->public_key.size())) {
+ PLOG(ERROR) << "Write failed";
+ return 1;
+ }
+ return 0;
+}
+
+int sign_input() {
+ std::string to_sign;
+ if (!ReadFdToString(STDIN_FILENO, &to_sign)) {
+ PLOG(ERROR) << "Read failed";
+ return 1;
+ }
+
+ auto key_pair = deriveKeyFromDice();
+ if (!key_pair.ok()) {
+ LOG(ERROR) << key_pair.error();
+ return 1;
+ }
+
+ auto signature =
+ compos_key::sign(key_pair->private_key,
+ reinterpret_cast<const uint8_t*>(to_sign.data()), to_sign.size());
+ if (!signature.ok()) {
+ LOG(ERROR) << signature.error();
+ return 1;
+ }
+
+ if (!WriteFully(STDOUT_FILENO, signature->data(), signature->size())) {
+ PLOG(ERROR) << "Write failed";
+ return 1;
+ }
+ return 0;
+}
+} // namespace
int main(int argc, char** argv) {
android::base::InitLogging(argv, android::base::LogdLogger(android::base::SYSTEM));
if (argc == 2) {
if (argv[1] == "public_key"sv) {
- auto key_pair = deriveKeyFromDice();
- if (!key_pair.ok()) {
- LOG(ERROR) << key_pair.error();
- return 1;
- }
- if (!WriteFully(STDOUT_FILENO, key_pair->public_key.data(),
- key_pair->public_key.size())) {
- PLOG(ERROR) << "Write failed";
- return 1;
- }
- return 0;
+ return write_public_key();
+ } else if (argv[1] == "sign"sv) {
+ return sign_input();
}
}
- std::cerr << "Usage:\n"
- "compos_key_helper public_key Write current public key to stdout\n";
+ LOG(INFO) << "Usage: compos_key_helper <command>. Available commands are:\n"
+ "public_key Write current public key to stdout\n"
+ "sign Consume stdin, sign it and write signature to stdout\n";
return 1;
}
diff --git a/compos/compos_key_helper/compos_key_test.cpp b/compos/compos_key_helper/compos_key_test.cpp
index e5c4768..e4c3e8a 100644
--- a/compos/compos_key_helper/compos_key_test.cpp
+++ b/compos/compos_key_helper/compos_key_test.cpp
@@ -20,6 +20,8 @@
#include "gtest/gtest.h"
+using namespace compos_key;
+
const std::vector<uint8_t> secret = {1, 2, 3};
const std::vector<uint8_t> other_secret = {3, 2, 3};
const std::vector<uint8_t> data = {42, 180, 65, 0};