Remove old key management
Delete a load of no longer needed code.
We no longer support persisting keys in the host. CompOS no longer
accesses DICE directly (compos_key_helper handles that).
We retain the instance image files, but rename pending to current
(it's created before reboot with the staged APEXes and used after
reboot with the current APEXes, but there's no point renaming it).
Remove the attempt to start an existing instance when running
compilation - it is slow, and vanishingly unlikely to work.
Sadly this also deletes all the CompOS unit tests. (But there are some
new ones in compos_key_tests.)
Bug: 218494522
Test: Manual; atest ComposTestCase; atest CompOsSigningHostTest
Change-Id: I0175270341d5dcad614106432b7d2650229cf8a6
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(())
- }
-}