Merge "Remove old key management"
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 25908ad..3a63a29 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -16,9 +16,6 @@
"name": "art_standalone_dexpreopt_tests"
},
{
- "name": "compsvc_device_tests"
- },
- {
"name": "compos_key_tests"
}
],
diff --git a/compos/Android.bp b/compos/Android.bp
index 0af0ec4..69b22d6 100644
--- a/compos/Android.bp
+++ b/compos/Android.bp
@@ -6,8 +6,6 @@
name: "compsvc_defaults",
srcs: ["src/compsvc_main.rs"],
rustlibs: [
- "android.hardware.security.dice-V1-rust",
- "android.security.dice-rust",
"android.system.virtualmachineservice-rust",
"authfs_aidl_interface-rust",
"compos_aidl_interface-rust",
@@ -18,7 +16,6 @@
"libbinder_rs",
"libclap",
"libcompos_common",
- "libcompos_native_rust",
"liblibc",
"liblog_rust",
"libminijail_rust",
@@ -26,7 +23,6 @@
"libodsign_proto_rust",
"libprotobuf",
"libregex",
- "libring",
"librustutils",
"libscopeguard",
],
@@ -44,9 +40,3 @@
"com.android.compos",
],
}
-
-rust_test {
- name: "compsvc_device_tests",
- defaults: ["compsvc_defaults"],
- test_suites: ["general-tests"],
-}
diff --git a/compos/aidl/com/android/compos/CompOsKeyData.aidl b/compos/aidl/com/android/compos/CompOsKeyData.aidl
deleted file mode 100644
index dafb009..0000000
--- a/compos/aidl/com/android/compos/CompOsKeyData.aidl
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.compos;
-
-/** {@hide} */
-parcelable CompOsKeyData {
- /**
- * The public key, as a DER-encoded RSAPublicKey
- * (https://datatracker.ietf.org/doc/html/rfc8017#appendix-A.1.1).
- */
- byte[] publicKey;
-
- /**
- * Opaque encrypted blob containing the private key and related metadata.
- */
- byte[] keyBlob;
-}
diff --git a/compos/aidl/com/android/compos/ICompOsService.aidl b/compos/aidl/com/android/compos/ICompOsService.aidl
index 5f1df6f..ef48ccf 100644
--- a/compos/aidl/com/android/compos/ICompOsService.aidl
+++ b/compos/aidl/com/android/compos/ICompOsService.aidl
@@ -16,8 +16,6 @@
package com.android.compos;
-import com.android.compos.CompOsKeyData;
-
/** {@hide} */
interface ICompOsService {
/**
@@ -32,16 +30,6 @@
}
/**
- * Initializes the service with the supplied encrypted private key blob. The key cannot be
- * changed once initialized, so once initiailzed, a repeated call will fail with
- * EX_ILLEGAL_STATE.
- *
- * @param keyBlob The encrypted blob containing the private key, as returned by
- * generateSigningKey().
- */
- void initializeSigningKey(in byte[] keyBlob);
-
- /**
* Run odrefresh in the VM context.
*
* The execution is based on the VM's APEX mounts, files on Android's /system (by accessing
@@ -63,24 +51,6 @@
String systemServerCompilerFilter);
/**
- * Generate a new public/private key pair suitable for signing CompOs output files.
- *
- * @return a certificate for the public key and the encrypted private key
- */
- CompOsKeyData generateSigningKey();
-
- /**
- * Check that the supplied encrypted private key is valid for signing CompOs output files, and
- * corresponds to the public key.
- *
- * @param keyBlob The encrypted blob containing the private key, as returned by
- * generateSigningKey().
- * @param publicKey The public key, as a DER encoded RSAPublicKey (RFC 3447 Appendix-A.1.1).
- * @return whether the inputs are valid and correspond to each other.
- */
- boolean verifySigningKey(in byte[] keyBlob, in byte[] publicKey);
-
- /**
* Returns the current VM's signing key, as an Ed25519 public key
* (https://datatracker.ietf.org/doc/html/rfc8032#section-5.1.5).
*/
diff --git a/compos/common/lib.rs b/compos/common/lib.rs
index 7b0eb7a..efbde06 100644
--- a/compos/common/lib.rs
+++ b/compos/common/lib.rs
@@ -34,24 +34,14 @@
/// The root of the data directory available for private use by the CompOS APEX.
pub const COMPOS_DATA_ROOT: &str = "/data/misc/apexdata/com.android.compos";
-/// The sub-directory where we store information relating to the pending instance
-/// of CompOS (based on staged APEXes).
-pub const PENDING_INSTANCE_DIR: &str = "pending";
-
-/// The sub-directory where we store information relating to the current instance
-/// of CompOS (based on active APEXes).
+/// The sub-directory where we store information relating to the instance of CompOS used for
+/// real compilation.
pub const CURRENT_INSTANCE_DIR: &str = "current";
/// The sub-directory where we store information relating to the instance of CompOS used for
/// tests.
pub const TEST_INSTANCE_DIR: &str = "test";
-/// The file that holds the encrypted private key for a CompOS instance.
-pub const PRIVATE_KEY_BLOB_FILE: &str = "key.blob";
-
-/// The file that holds the public key for a CompOS instance.
-pub const PUBLIC_KEY_FILE: &str = "key.pubkey";
-
/// The file that holds the instance image for a CompOS instance.
pub const INSTANCE_IMAGE_FILE: &str = "instance.img";
diff --git a/compos/composd/src/instance_manager.rs b/compos/composd/src/instance_manager.rs
index 9761a3e..848fc8c 100644
--- a/compos/composd/src/instance_manager.rs
+++ b/compos/composd/src/instance_manager.rs
@@ -23,7 +23,7 @@
use compos_aidl_interface::binder::Strong;
use compos_common::compos_client::VmParameters;
use compos_common::{
- DEX2OAT_CPU_SET_PROP_NAME, DEX2OAT_THREADS_PROP_NAME, PENDING_INSTANCE_DIR,
+ CURRENT_INSTANCE_DIR, DEX2OAT_CPU_SET_PROP_NAME, DEX2OAT_THREADS_PROP_NAME,
PREFER_STAGED_VM_CONFIG_PATH, TEST_INSTANCE_DIR,
};
use rustutils::system_properties;
@@ -42,10 +42,10 @@
Self { service, state: Default::default() }
}
- pub fn start_pending_instance(&self) -> Result<Arc<CompOsInstance>> {
+ pub fn start_current_instance(&self) -> Result<Arc<CompOsInstance>> {
let mut vm_parameters = new_vm_parameters()?;
vm_parameters.config_path = Some(PREFER_STAGED_VM_CONFIG_PATH.to_owned());
- self.start_instance(PENDING_INSTANCE_DIR, vm_parameters)
+ self.start_instance(CURRENT_INSTANCE_DIR, vm_parameters)
}
pub fn start_test_instance(&self) -> Result<Arc<CompOsInstance>> {
@@ -77,7 +77,7 @@
}
fn try_start_instance(&self, instance_starter: InstanceStarter) -> Result<Arc<CompOsInstance>> {
- let compos_instance = instance_starter.create_or_start_instance(&*self.service)?;
+ let compos_instance = instance_starter.start_new_instance(&*self.service)?;
Ok(Arc::new(compos_instance))
}
}
diff --git a/compos/composd/src/instance_starter.rs b/compos/composd/src/instance_starter.rs
index a886584..4873d7a 100644
--- a/compos/composd/src/instance_starter.rs
+++ b/compos/composd/src/instance_starter.rs
@@ -20,16 +20,13 @@
use android_system_virtualizationservice::aidl::android::system::virtualizationservice::{
IVirtualizationService::IVirtualizationService, PartitionType::PartitionType,
};
-use anyhow::{bail, Context, Result};
+use anyhow::{Context, Result};
use binder_common::lazy_service::LazyServiceGuard;
use compos_aidl_interface::aidl::com::android::compos::ICompOsService::ICompOsService;
use compos_aidl_interface::binder::{ParcelFileDescriptor, Strong};
use compos_common::compos_client::{VmInstance, VmParameters};
-use compos_common::{
- COMPOS_DATA_ROOT, IDSIG_FILE, IDSIG_MANIFEST_APK_FILE, INSTANCE_IMAGE_FILE,
- PRIVATE_KEY_BLOB_FILE, PUBLIC_KEY_FILE,
-};
-use log::{info, warn};
+use compos_common::{COMPOS_DATA_ROOT, IDSIG_FILE, IDSIG_MANIFEST_APK_FILE, INSTANCE_IMAGE_FILE};
+use log::info;
use std::fs;
use std::path::{Path, PathBuf};
@@ -53,8 +50,6 @@
instance_image: PathBuf,
idsig: PathBuf,
idsig_manifest_apk: PathBuf,
- key_blob: PathBuf,
- public_key: PathBuf,
vm_parameters: VmParameters,
}
@@ -65,61 +60,17 @@
let instance_image = instance_root_path.join(INSTANCE_IMAGE_FILE);
let idsig = instance_root_path.join(IDSIG_FILE);
let idsig_manifest_apk = instance_root_path.join(IDSIG_MANIFEST_APK_FILE);
- let key_blob = instance_root_path.join(PRIVATE_KEY_BLOB_FILE);
- let public_key = instance_root_path.join(PUBLIC_KEY_FILE);
Self {
instance_name: instance_name.to_owned(),
instance_root,
instance_image,
idsig,
idsig_manifest_apk,
- key_blob,
- public_key,
vm_parameters,
}
}
- pub fn create_or_start_instance(
- &self,
- virtualization_service: &dyn IVirtualizationService,
- ) -> Result<CompOsInstance> {
- let compos_instance = self.start_existing_instance(virtualization_service);
- match compos_instance {
- Ok(_) => return compos_instance,
- Err(e) => warn!("Failed to start: {}", e),
- }
-
- self.start_new_instance(virtualization_service)
- }
-
- fn start_existing_instance(
- &self,
- virtualization_service: &dyn IVirtualizationService,
- ) -> Result<CompOsInstance> {
- // 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")?;
-
- let compos_instance = self.start_vm(virtualization_service)?;
- let service = &compos_instance.service;
-
- if !service.verifySigningKey(&key_blob, &public_key).context("Verifying key pair")? {
- bail!("Key pair invalid");
- }
-
- // If we get this far then the instance image is valid in the current context (e.g. the
- // current set of APEXes) and the key blob can be successfully decrypted by the VM. So the
- // files have not been tampered with and we're good to go.
- service.initializeSigningKey(&key_blob).context("Loading signing key")?;
-
- Ok(compos_instance)
- }
-
- fn start_new_instance(
+ pub fn start_new_instance(
&self,
virtualization_service: &dyn IVirtualizationService,
) -> Result<CompOsInstance> {
@@ -128,23 +79,15 @@
// Ignore failure here - the directory may already exist.
let _ = fs::create_dir(&self.instance_root);
+ // Overwrite any existing instance - it's unlikely to be valid with the current set
+ // of APEXes, and finding out it isn't is much more expensive than creating a new one.
self.create_instance_image(virtualization_service)?;
+
// Delete existing idsig files. Ignore error in case idsig doesn't exist.
let _ = fs::remove_file(&self.idsig);
let _ = fs::remove_file(&self.idsig_manifest_apk);
- let compos_instance = self.start_vm(virtualization_service)?;
- let service = &compos_instance.service;
-
- let key_data = service.generateSigningKey().context("Generating signing key")?;
- fs::write(&self.key_blob, &key_data.keyBlob).context("Writing key blob")?;
- fs::write(&self.public_key, &key_data.publicKey).context("Writing public key")?;
-
- // Unlike when starting an existing instance, we don't need to verify the key, since we
- // just generated it and have it in memory.
- service.initializeSigningKey(&key_data.keyBlob).context("Loading signing key")?;
-
- Ok(compos_instance)
+ self.start_vm(virtualization_service)
}
fn start_vm(
@@ -187,21 +130,4 @@
.context("Writing instance image file")?;
Ok(())
}
-
- fn check_files_exist(&self) -> Result<()> {
- if !self.instance_root.is_dir() {
- bail!("Directory {:?} not found", self.instance_root)
- };
- Self::check_file_exists(&self.instance_image)?;
- Self::check_file_exists(&self.key_blob)?;
- Self::check_file_exists(&self.public_key)?;
- Ok(())
- }
-
- fn check_file_exists(file: &Path) -> Result<()> {
- if !file.is_file() {
- bail!("File {:?} not found", file)
- };
- Ok(())
- }
}
diff --git a/compos/composd/src/service.rs b/compos/composd/src/service.rs
index 8e5586e..f4798d7 100644
--- a/compos/composd/src/service.rs
+++ b/compos/composd/src/service.rs
@@ -70,8 +70,7 @@
&self,
callback: &Strong<dyn ICompilationTaskCallback>,
) -> Result<Strong<dyn ICompilationTask>> {
- // TODO: Try to start the current instance with staged APEXes to see if it works?
- let comp_os = self.instance_manager.start_pending_instance().context("Starting CompOS")?;
+ let comp_os = self.instance_manager.start_current_instance().context("Starting CompOS")?;
let target_dir_name = PENDING_ARTIFACTS_SUBDIR.to_owned();
let task = OdrefreshTask::start(
diff --git a/compos/native/Android.bp b/compos/native/Android.bp
deleted file mode 100644
index 84e312a..0000000
--- a/compos/native/Android.bp
+++ /dev/null
@@ -1,46 +0,0 @@
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-rust_library {
- name: "libcompos_native_rust",
- crate_name: "compos_native",
- srcs: ["lib.rs"],
- rustlibs: [
- "libanyhow",
- "libcxx",
- "liblibc",
- ],
- static_libs: [
- "libcompos_native_cpp",
- ],
- shared_libs: [
- "libcrypto",
- ],
- apex_available: ["com.android.compos"],
-}
-
-cc_library_static {
- name: "libcompos_native_cpp",
- srcs: ["compos_native.cpp"],
- shared_libs: ["libcrypto"],
- generated_headers: ["compos_native_header"],
- generated_sources: ["compos_native_code"],
- apex_available: ["com.android.compos"],
-}
-
-genrule {
- name: "compos_native_code",
- tools: ["cxxbridge"],
- cmd: "$(location cxxbridge) $(in) >> $(out)",
- srcs: ["lib.rs"],
- out: ["compos_native_cxx_generated.cc"],
-}
-
-genrule {
- name: "compos_native_header",
- tools: ["cxxbridge"],
- cmd: "$(location cxxbridge) $(in) --header >> $(out)",
- srcs: ["lib.rs"],
- out: ["lib.rs.h"],
-}
diff --git a/compos/native/compos_native.cpp b/compos/native/compos_native.cpp
deleted file mode 100644
index 81280fd..0000000
--- a/compos/native/compos_native.cpp
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * 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 "compos_native.h"
-
-#include <openssl/bn.h>
-#include <openssl/mem.h>
-#include <openssl/nid.h>
-#include <openssl/rsa.h>
-#include <openssl/sha.h>
-
-#include <algorithm>
-#include <iterator>
-#include <vector>
-
-namespace {
-KeyResult make_key_error(const char* message) {
- return KeyResult{{}, {}, message};
-}
-
-SignResult make_sign_error(const char* message) {
- return SignResult{{}, message};
-}
-} // namespace
-
-constexpr int KEY_BITS = 2048;
-
-KeyResult generate_key_pair() {
- bssl::UniquePtr<RSA> key_pair(RSA_new());
-
- // This function specifies that the public exponent is always 65537, which is good because
- // that's what odsign is expecting.
- if (!RSA_generate_key_fips(key_pair.get(), KEY_BITS, /*callback=*/nullptr)) {
- return make_key_error("Failed to generate key pair");
- }
-
- KeyResult result;
-
- uint8_t* out;
- int size;
- bssl::UniquePtr<uint8_t> out_owner;
-
- // Extract public key as DER.
- out = nullptr;
- size = i2d_RSAPublicKey(key_pair.get(), &out);
- if (size < 0 || !out) {
- return make_key_error("Failed to get RSAPublicKey");
- }
- out_owner.reset(out);
-
- result.public_key.reserve(size);
- std::copy(out, out + size, std::back_inserter(result.public_key));
- out_owner.reset();
-
- // And ditto for the private key (which actually includes the public bits).
- out = nullptr;
- size = i2d_RSAPrivateKey(key_pair.get(), &out);
- if (size < 0 || !out) {
- return make_key_error("Failed to get RSAPrivateKey");
- }
- out_owner.reset(out);
-
- result.private_key.reserve(size);
- std::copy(out, out + size, std::back_inserter(result.private_key));
- out_owner.reset();
-
- return result;
-}
-
-SignResult sign(rust::Slice<const uint8_t> private_key, rust::Slice<const uint8_t> data) {
- uint8_t digest[SHA256_DIGEST_LENGTH];
- SHA256(data.data(), data.size(), digest);
-
- const uint8_t* key_in = private_key.data();
- bssl::UniquePtr<RSA> key(d2i_RSAPrivateKey(nullptr, &key_in, private_key.size()));
- if (!key) {
- return make_sign_error("Failed to load RSAPrivateKey");
- }
-
- // rust::Vec doesn't support resize, so we need our own buffer.
- // The signature is always less than the modulus (public key), so
- // will fit in KEY_BITS.
-
- uint8_t signature[KEY_BITS / 8];
- if (sizeof(signature) < RSA_size(key.get())) {
- return make_sign_error("Signing key is too large");
- }
- unsigned signature_len = 0;
-
- if (!RSA_sign(NID_sha256, digest, sizeof(digest), signature, &signature_len, key.get())) {
- return make_sign_error("Failed to sign");
- }
-
- SignResult result;
- result.signature.reserve(signature_len);
- std::copy(signature, signature + signature_len, std::back_inserter(result.signature));
-
- return result;
-}
diff --git a/compos/native/compos_native.h b/compos/native/compos_native.h
deleted file mode 100644
index 6497c9b..0000000
--- a/compos/native/compos_native.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * 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 generate_key_pair();
-
-SignResult sign(rust::Slice<const uint8_t> private_key, rust::Slice<const uint8_t> data);
diff --git a/compos/native/lib.rs b/compos/native/lib.rs
deleted file mode 100644
index a5f0cc5..0000000
--- a/compos/native/lib.rs
+++ /dev/null
@@ -1,59 +0,0 @@
-// 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.
-
-//! Native helpers for CompOS.
-
-pub use crypto::*;
-
-#[cxx::bridge]
-mod crypto {
- /// Contains either a key pair or a reason why the key could not be extracted.
- struct KeyResult {
- /// The DER-encoded RSAPublicKey
- /// (https://datatracker.ietf.org/doc/html/rfc8017#appendix-A.1.1).
- public_key: Vec<u8>,
- /// The DER-encoded RSAPrivateKey
- /// (https://datatracker.ietf.org/doc/html/rfc8017#appendix-A.1.2).
- /// Note that this is unencrypted.
- private_key: Vec<u8>,
- /// A description of what went wrong if the attempt failed.
- error: String,
- }
-
- /// Contains either a signature or a reason why signing failed.
- struct SignResult {
- /// The RSAES-PKCS1-v1_5 signature
- /// (https://datatracker.ietf.org/doc/html/rfc3447#section-7.2).
- signature: Vec<u8>,
- /// A description of what went wrong if the attempt failed.
- error: String,
- }
-
- unsafe extern "C++" {
- include!("compos_native.h");
-
- // SAFETY: The C++ implementation manages its own memory. cxx handles the mapping of the
- // return value.
-
- /// Generate a public/private key pair.
- fn generate_key_pair() -> KeyResult;
-
- // SAFETY: The C++ implementation manages its own memory, and does not retain or abuse
- // the references passed to it. cxx handles the mapping of the return value.
-
- /// Sign data using a SHA256 digest and RSAES-PKCS1-v1_5 using the given
- /// DER-encoded RSAPrivateKey.
- fn sign(private_key: &[u8], data: &[u8]) -> SignResult;
- }
-}
diff --git a/compos/src/blob_encryption.rs b/compos/src/blob_encryption.rs
deleted file mode 100644
index 0db09ba..0000000
--- a/compos/src/blob_encryption.rs
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright 2022 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.
- */
-
-//! Allows for data to be encrypted and authenticated (AEAD) with a key derived from some secret.
-//! The encrypted blob can be passed to the untrusted host without revealing the encrypted data
-//! but with the key the data can be retrieved as long as the blob has not been tampered with.
-
-use anyhow::{bail, Context, Result};
-use ring::{
- aead::{Aad, LessSafeKey, Nonce, AES_256_GCM, NONCE_LEN},
- hkdf::{Salt, HKDF_SHA256},
- rand::{SecureRandom, SystemRandom},
-};
-
-// Non-secret input to the AEAD key derivation
-const KDF_INFO: &[u8] = b"CompOS blob sealing key";
-
-pub fn derive_aead_key(input_keying_material: &[u8]) -> Result<LessSafeKey> {
- // Derive key using HKDF - see https://datatracker.ietf.org/doc/html/rfc5869#section-2
- let salt = [];
- let prk = Salt::new(HKDF_SHA256, &salt).extract(input_keying_material);
- let okm = prk.expand(&[KDF_INFO], &AES_256_GCM).context("HKDF failed")?;
- // LessSafeKey is only less safe in that it has less inherent protection against nonce
- // reuse; we are safe because we use a new random nonce for each sealing operation.
- // (See https://github.com/briansmith/ring/issues/899.)
- Ok(LessSafeKey::new(okm.into()))
-}
-
-pub fn encrypt_bytes(key: LessSafeKey, bytes: &[u8]) -> Result<Vec<u8>> {
- let mut output = Vec::with_capacity(bytes.len() + NONCE_LEN + key.algorithm().tag_len());
-
- // Generate a unique nonce, since we may use the same key more than once, and put it at the
- // start of the output blob.
- let mut nonce_bytes = [0u8; NONCE_LEN];
- SystemRandom::new().fill(&mut nonce_bytes).context("Failed to generate random nonce")?;
- output.extend_from_slice(&nonce_bytes);
-
- // Copy input to output then encrypt & seal it in place.
- output.extend_from_slice(bytes);
- let nonce = Nonce::assume_unique_for_key(nonce_bytes);
- let tag = key
- .seal_in_place_separate_tag(nonce, Aad::empty(), &mut output[NONCE_LEN..])
- .context("Failed to seal blob")?;
- output.extend_from_slice(tag.as_ref());
-
- Ok(output)
-}
-
-pub fn decrypt_bytes(key: LessSafeKey, bytes: &[u8]) -> Result<Vec<u8>> {
- if bytes.len() < NONCE_LEN + key.algorithm().tag_len() {
- bail!("Encrypted blob is too small");
- }
-
- // We expect the nonce at the start followed by the sealed data (encrypted data +
- // authentication tag).
- let nonce = Nonce::try_assume_unique_for_key(&bytes[..NONCE_LEN]).unwrap();
- let mut output = bytes[NONCE_LEN..].to_vec();
-
- // Verify & decrypt the data in place
- let unsealed_size =
- key.open_in_place(nonce, Aad::empty(), &mut output).context("Failed to unseal blob")?.len();
-
- // Remove the tag after the plaintext
- output.truncate(unsealed_size);
-
- Ok(output)
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn test_round_trip_data() -> Result<()> {
- let input_keying_material = b"Key is derived from this";
- let original_bytes = b"This is the secret data";
-
- let key = derive_aead_key(input_keying_material)?;
- let blob = encrypt_bytes(key, original_bytes)?;
-
- let key = derive_aead_key(input_keying_material)?;
- let decoded_bytes = decrypt_bytes(key, &blob)?;
-
- assert_eq!(decoded_bytes, original_bytes);
- Ok(())
- }
-
- #[test]
- fn test_modified_data_detected() -> Result<()> {
- let input_keying_material = b"Key is derived from this";
- let original_bytes = b"This is the secret data";
-
- let key = derive_aead_key(input_keying_material)?;
- let mut blob = encrypt_bytes(key, original_bytes)?;
-
- // Flip a bit.
- blob[0] ^= 1;
-
- let key = derive_aead_key(input_keying_material)?;
- let decoded_bytes = decrypt_bytes(key, &blob);
-
- assert!(decoded_bytes.is_err());
- Ok(())
- }
-}
diff --git a/compos/src/compsvc.rs b/compos/src/compsvc.rs
index df36ed9..3a794ee 100644
--- a/compos/src/compsvc.rs
+++ b/compos/src/compsvc.rs
@@ -19,60 +19,35 @@
//! actual compiler.
use anyhow::{bail, Context, Result};
-use binder_common::new_binder_exception;
-use compos_common::binder::to_binder_result;
-use log::warn;
use std::default::Default;
use std::fs::read_dir;
use std::path::{Path, PathBuf};
-use std::sync::RwLock;
use crate::artifact_signer::ArtifactSigner;
use crate::compilation::{odrefresh, OdrefreshContext};
use crate::compos_key;
-use crate::dice::Dice;
-use crate::signing_key::DiceSigningKey;
-use authfs_aidl_interface::aidl::com::android::virt::fs::IAuthFsService::IAuthFsService;
-use compos_aidl_interface::aidl::com::android::compos::{
- CompOsKeyData::CompOsKeyData,
- ICompOsService::{BnCompOsService, CompilationMode::CompilationMode, ICompOsService},
+use compos_aidl_interface::aidl::com::android::compos::ICompOsService::{
+ BnCompOsService, CompilationMode::CompilationMode, ICompOsService,
};
-use compos_aidl_interface::binder::{
- BinderFeatures, ExceptionCode, Interface, Result as BinderResult, Strong,
-};
+use compos_aidl_interface::binder::{BinderFeatures, Interface, Result as BinderResult, Strong};
+use compos_common::binder::to_binder_result;
use compos_common::odrefresh::ODREFRESH_PATH;
const AUTHFS_SERVICE_NAME: &str = "authfs_service";
/// Constructs a binder object that implements ICompOsService.
pub fn new_binder() -> Result<Strong<dyn ICompOsService>> {
- let service = CompOsService {
- odrefresh_path: PathBuf::from(ODREFRESH_PATH),
- signing_key: DiceSigningKey::new(Dice::new()?),
- key_blob: RwLock::new(Vec::new()),
- };
+ let service = CompOsService { odrefresh_path: PathBuf::from(ODREFRESH_PATH) };
Ok(BnCompOsService::new_binder(service, BinderFeatures::default()))
}
struct CompOsService {
odrefresh_path: PathBuf,
- signing_key: DiceSigningKey,
- key_blob: RwLock<Vec<u8>>,
}
impl Interface for CompOsService {}
impl ICompOsService for CompOsService {
- fn initializeSigningKey(&self, key_blob: &[u8]) -> BinderResult<()> {
- let mut w = self.key_blob.write().unwrap();
- if w.is_empty() {
- *w = Vec::from(key_blob);
- Ok(())
- } else {
- Err(new_binder_exception(ExceptionCode::ILLEGAL_STATE, "Cannot re-initialize the key"))
- }
- }
-
fn odrefresh(
&self,
compilation_mode: CompilationMode,
@@ -83,14 +58,6 @@
zygote_arch: &str,
system_server_compiler_filter: &str,
) -> BinderResult<i8> {
- let key = &*self.key_blob.read().unwrap();
- if key.is_empty() {
- return Err(new_binder_exception(
- ExceptionCode::ILLEGAL_STATE,
- "Key is not initialized",
- ));
- }
-
let context = to_binder_result(OdrefreshContext::new(
compilation_mode,
system_dir_fd,
@@ -101,7 +68,7 @@
system_server_compiler_filter,
))?;
- let authfs_service = get_authfs_service()?;
+ let authfs_service = authfs_aidl_interface::binder::get_interface(AUTHFS_SERVICE_NAME)?;
let exit_code = to_binder_result(
odrefresh(&self.odrefresh_path, context, authfs_service, |output_dir| {
// authfs only shows us the files we created, so it's ok to just sign everything
@@ -116,28 +83,11 @@
Ok(exit_code as i8)
}
- fn generateSigningKey(&self) -> BinderResult<CompOsKeyData> {
- to_binder_result(self.signing_key.generate())
- }
-
- fn verifySigningKey(&self, key_blob: &[u8], public_key: &[u8]) -> BinderResult<bool> {
- Ok(if let Err(e) = self.signing_key.verify(key_blob, public_key) {
- warn!("Signing key verification failed: {:?}", e);
- false
- } else {
- true
- })
- }
-
fn getPublicKey(&self) -> BinderResult<Vec<u8>> {
to_binder_result(compos_key::get_public_key())
}
}
-fn get_authfs_service() -> BinderResult<Strong<dyn IAuthFsService>> {
- Ok(authfs_aidl_interface::binder::get_interface(AUTHFS_SERVICE_NAME)?)
-}
-
fn add_artifacts(target_dir: &Path, artifact_signer: &mut ArtifactSigner) -> Result<()> {
for entry in
read_dir(&target_dir).with_context(|| format!("Traversing {}", target_dir.display()))?
diff --git a/compos/src/compsvc_main.rs b/compos/src/compsvc_main.rs
index f0af752..4ecbfe9 100644
--- a/compos/src/compsvc_main.rs
+++ b/compos/src/compsvc_main.rs
@@ -17,13 +17,10 @@
//! A tool to start a standalone compsvc server that serves over RPC binder.
mod artifact_signer;
-mod blob_encryption;
mod compilation;
mod compos_key;
mod compsvc;
-mod dice;
mod fsverity;
-mod signing_key;
use android_system_virtualmachineservice::{
aidl::android::system::virtualmachineservice::IVirtualMachineService::{
diff --git a/compos/src/dice.rs b/compos/src/dice.rs
deleted file mode 100644
index 25148ab..0000000
--- a/compos/src/dice.rs
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2022 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.
- */
-
-//! Handles the use of DICE (via diced / IDiceNode) for accessing our VM's unique secret.
-
-use android_security_dice::aidl::android::security::dice::IDiceNode::IDiceNode;
-use android_security_dice::binder::{wait_for_interface, Strong};
-use anyhow::{Context, Result};
-
-#[derive(Clone)]
-pub struct Dice {
- node: Strong<dyn IDiceNode>,
-}
-
-impl Dice {
- pub fn new() -> Result<Self> {
- let dice_service = wait_for_interface::<dyn IDiceNode>("android.security.dice.IDiceNode")
- .context("No IDiceNode service")?;
- Ok(Self { node: dice_service })
- }
-
- pub fn get_sealing_cdi(&self) -> Result<Vec<u8>> {
- let input_values = [];
- let bcc_handover = self.node.derive(&input_values).context("Failed to retrieve CDI")?;
- Ok(bcc_handover.cdiSeal.to_vec())
- }
-}
diff --git a/compos/src/signing_key.rs b/compos/src/signing_key.rs
deleted file mode 100644
index b1a3238..0000000
--- a/compos/src/signing_key.rs
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * Copyright 2022 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.
- */
-
-//! RSA key pair generation, persistence (with the private key encrypted), verification and
-//! signing.
-
-use crate::blob_encryption;
-use crate::dice::Dice;
-use anyhow::{bail, Context, Result};
-use compos_aidl_interface::aidl::com::android::compos::CompOsKeyData::CompOsKeyData;
-use ring::{
- rand::{SecureRandom, SystemRandom},
- signature,
-};
-
-pub type DiceSigningKey = SigningKey<Dice>;
-
-pub struct SigningKey<T: SecretStore> {
- secret_store: T,
-}
-
-pub trait SecretStore: Clone {
- fn get_secret(&self) -> Result<Vec<u8>>;
-}
-
-impl SecretStore for Dice {
- fn get_secret(&self) -> Result<Vec<u8>> {
- self.get_sealing_cdi()
- }
-}
-
-impl<T: SecretStore> SigningKey<T> {
- pub fn new(secret_store: T) -> Self {
- Self { secret_store }
- }
-
- pub fn generate(&self) -> Result<CompOsKeyData> {
- let key_result = compos_native::generate_key_pair();
- if key_result.public_key.is_empty() || key_result.private_key.is_empty() {
- bail!("Failed to generate key pair: {}", key_result.error);
- }
-
- let encrypted =
- encrypt_private_key(&self.secret_store.get_secret()?, &key_result.private_key)?;
- Ok(CompOsKeyData { publicKey: key_result.public_key, keyBlob: encrypted })
- }
-
- pub fn verify(&self, key_blob: &[u8], public_key: &[u8]) -> Result<()> {
- // We verify the private key by verifying the AEAD authentication tag in the signer.
- // To verify the public key matches, we sign a block of random data with the private key,
- // and then check that the signature matches the purported key.
- let mut data = [0u8; 32]; // Size is fairly arbitrary.
- SystemRandom::new().fill(&mut data).context("No random data")?;
-
- let signature = self.new_signer(key_blob)?.sign(&data)?;
-
- let public_key =
- signature::UnparsedPublicKey::new(&signature::RSA_PKCS1_2048_8192_SHA256, public_key);
- public_key.verify(&data, &signature).context("Signature verification failed")?;
-
- Ok(())
- }
-
- pub fn new_signer(&self, key_blob: &[u8]) -> Result<Signer<T>> {
- Ok(Signer { key_blob: key_blob.to_owned(), secret_store: self.secret_store.clone() })
- }
-}
-
-pub struct Signer<T: SecretStore> {
- key_blob: Vec<u8>,
- secret_store: T,
-}
-
-impl<T: SecretStore> Signer<T> {
- pub fn sign(self, data: &[u8]) -> Result<Vec<u8>> {
- let private_key = decrypt_private_key(&self.secret_store.get_secret()?, &self.key_blob)?;
- let sign_result = compos_native::sign(&private_key, data);
- if sign_result.signature.is_empty() {
- bail!("Failed to sign: {}", sign_result.error);
- }
- Ok(sign_result.signature)
- }
-}
-
-fn encrypt_private_key(vm_secret: &[u8], private_key: &[u8]) -> Result<Vec<u8>> {
- let aead_key = blob_encryption::derive_aead_key(vm_secret)?;
- blob_encryption::encrypt_bytes(aead_key, private_key)
-}
-
-fn decrypt_private_key(vm_secret: &[u8], blob: &[u8]) -> Result<Vec<u8>> {
- let aead_key = blob_encryption::derive_aead_key(vm_secret)?;
- blob_encryption::decrypt_bytes(aead_key, blob)
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- const SECRET: &[u8] = b"This is not very secret";
-
- #[derive(Clone)]
- struct TestSecretStore;
-
- impl SecretStore for TestSecretStore {
- fn get_secret(&self) -> Result<Vec<u8>> {
- Ok(SECRET.to_owned())
- }
- }
-
- type TestSigningKey = SigningKey<TestSecretStore>;
-
- fn signing_key_for_test() -> TestSigningKey {
- TestSigningKey::new(TestSecretStore)
- }
-
- #[test]
- fn test_generated_key_verifies() -> Result<()> {
- let signing_key = signing_key_for_test();
- let key_pair = signing_key.generate()?;
-
- signing_key.verify(&key_pair.keyBlob, &key_pair.publicKey)
- }
-
- #[test]
- fn test_bogus_key_pair_rejected() -> Result<()> {
- let signing_key = signing_key_for_test();
- let key_pair = signing_key.generate()?;
-
- // Swap public key & key blob - clearly invalid
- assert!(signing_key.verify(&key_pair.publicKey, &key_pair.keyBlob).is_err());
-
- Ok(())
- }
-
- #[test]
- fn test_mismatched_key_rejected() -> Result<()> {
- let signing_key = signing_key_for_test();
- let key_pair1 = signing_key.generate()?;
- let key_pair2 = signing_key.generate()?;
-
- // Both pairs should be valid
- signing_key.verify(&key_pair1.keyBlob, &key_pair1.publicKey)?;
- signing_key.verify(&key_pair2.keyBlob, &key_pair2.publicKey)?;
-
- // But using the public key from one and the private key from the other should not,
- // even though both are well-formed
- assert!(signing_key.verify(&key_pair1.publicKey, &key_pair2.keyBlob).is_err());
- Ok(())
- }
-}
diff --git a/compos/verify/verify.rs b/compos/verify/verify.rs
index 228225d..55fe1bd 100644
--- a/compos/verify/verify.rs
+++ b/compos/verify/verify.rs
@@ -21,12 +21,11 @@
use compos_aidl_interface::binder::ProcessState;
use compos_common::compos_client::{VmInstance, VmParameters};
use compos_common::odrefresh::{
- CURRENT_ARTIFACTS_SUBDIR, ODREFRESH_OUTPUT_ROOT_DIR, PENDING_ARTIFACTS_SUBDIR,
- TEST_ARTIFACTS_SUBDIR,
+ CURRENT_ARTIFACTS_SUBDIR, ODREFRESH_OUTPUT_ROOT_DIR, TEST_ARTIFACTS_SUBDIR,
};
use compos_common::{
- COMPOS_DATA_ROOT, IDSIG_FILE, IDSIG_MANIFEST_APK_FILE, INSTANCE_IMAGE_FILE,
- PENDING_INSTANCE_DIR, TEST_INSTANCE_DIR,
+ COMPOS_DATA_ROOT, CURRENT_INSTANCE_DIR, IDSIG_FILE, IDSIG_MANIFEST_APK_FILE,
+ INSTANCE_IMAGE_FILE, TEST_INSTANCE_DIR,
};
use log::error;
use std::fs::File;
@@ -61,15 +60,14 @@
.long("instance")
.takes_value(true)
.required(true)
- .possible_values(&["pending", "current", "test"]),
+ .possible_values(&["current", "test"]),
)
.arg(clap::Arg::with_name("debug").long("debug"))
.get_matches();
let debug_mode = matches.is_present("debug");
let (instance_dir, artifacts_dir) = match matches.value_of("instance").unwrap() {
- "pending" => (PENDING_INSTANCE_DIR, PENDING_ARTIFACTS_SUBDIR),
- "current" => (PENDING_INSTANCE_DIR, CURRENT_ARTIFACTS_SUBDIR),
+ "current" => (CURRENT_INSTANCE_DIR, CURRENT_ARTIFACTS_SUBDIR),
"test" => (TEST_INSTANCE_DIR, TEST_ARTIFACTS_SUBDIR),
_ => unreachable!("Unexpected instance name"),
};