[test] Add e2e instrumentation test for VM attestation
This cl adds an e2e test to check the VM attestation with the
following steps:
- Provisioning a pair of mock keys in virtualizationservice.
- Requesting VM attestation from a running VM and tracing this
step.
- Validating the attestation result.
To facilitate the testing process, two new APIs have been added.
The first API allows for the provisioning of mock keys, while the
second API facilitates the request for attestation in testing mode.
The new test has been added to busytown config at cl/605577401.
Bug: 318333789
Test: atest VmAttestationTestApp
Change-Id: Icf9d39c704af316f7625cf9d057d255243eca445
diff --git a/virtualizationservice/src/aidl.rs b/virtualizationservice/src/aidl.rs
index a1a1fb9..d0c5d4a 100644
--- a/virtualizationservice/src/aidl.rs
+++ b/virtualizationservice/src/aidl.rs
@@ -16,7 +16,8 @@
use crate::{get_calling_pid, get_calling_uid, REMOTELY_PROVISIONED_COMPONENT_SERVICE_NAME};
use crate::atom::{forward_vm_booted_atom, forward_vm_creation_atom, forward_vm_exited_atom};
-use crate::rkpvm::request_attestation;
+use crate::rkpvm::{request_attestation, generate_ecdsa_p256_key_pair};
+use crate::remote_provisioning;
use android_os_permissions_aidl::aidl::android::os::IPermissionController;
use android_system_virtualizationcommon::aidl::android::system::virtualizationcommon::Certificate::Certificate;
use android_system_virtualizationservice::{
@@ -38,6 +39,7 @@
use anyhow::{anyhow, ensure, Context, Result};
use avflog::LogResult;
use binder::{self, wait_for_interface, BinderFeatures, ExceptionCode, Interface, LazyServiceGuard, Status, Strong, IntoBinderResult};
+use service_vm_comm::Response;
use lazy_static::lazy_static;
use libc::VMADDR_CID_HOST;
use log::{error, info, warn};
@@ -73,7 +75,73 @@
const CHUNK_RECV_MAX_LEN: usize = 1024;
+/// The fake certificate is used for testing only when a client VM requests attestation in test
+/// mode, it is a single certificate extracted on an unregistered device for testing.
+/// Here is the snapshot of the certificate:
+///
+/// ```
+/// Certificate:
+/// Data:
+/// Version: 3 (0x2)
+/// Serial Number:
+/// 59:ae:50:98:95:e1:34:25:f1:21:93:c0:4c:e5:24:66
+/// Signature Algorithm: ecdsa-with-SHA256
+/// Issuer: CN = Droid Unregistered Device CA, O = Google Test LLC
+/// Validity
+/// Not Before: Feb 5 14:39:39 2024 GMT
+/// Not After : Feb 14 14:39:39 2024 GMT
+/// Subject: CN = 59ae509895e13425f12193c04ce52466, O = TEE
+/// Subject Public Key Info:
+/// Public Key Algorithm: id-ecPublicKey
+/// Public-Key: (256 bit)
+/// pub:
+/// 04:30:32:cd:95:12:b0:71:8b:b7:14:44:26:58:d5:
+/// 82:8c:25:55:2c:6d:ef:98:e3:4f:88:d0:74:82:09:
+/// 3e:8d:6c:f0:f2:18:d5:83:0e:0d:f2:ce:c5:15:38:
+/// e5:6a:e6:4d:4d:95:15:b7:24:e7:cb:4b:63:42:21:
+/// bc:36:c6:0a:d8
+/// ASN1 OID: prime256v1
+/// NIST CURVE: P-256
+/// X509v3 extensions:
+/// ...
+/// ```
+const FAKE_CERTIFICATE_FOR_TESTING: &[u8] = &[
+ 0x30, 0x82, 0x01, 0xee, 0x30, 0x82, 0x01, 0x94, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x10, 0x59,
+ 0xae, 0x50, 0x98, 0x95, 0xe1, 0x34, 0x25, 0xf1, 0x21, 0x93, 0xc0, 0x4c, 0xe5, 0x24, 0x66, 0x30,
+ 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x41, 0x31, 0x25, 0x30,
+ 0x23, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1c, 0x44, 0x72, 0x6f, 0x69, 0x64, 0x20, 0x55, 0x6e,
+ 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x20, 0x44, 0x65, 0x76, 0x69, 0x63,
+ 0x65, 0x20, 0x43, 0x41, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0f, 0x47,
+ 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x4c, 0x4c, 0x43, 0x30, 0x1e,
+ 0x17, 0x0d, 0x32, 0x34, 0x30, 0x32, 0x30, 0x35, 0x31, 0x34, 0x33, 0x39, 0x33, 0x39, 0x5a, 0x17,
+ 0x0d, 0x32, 0x34, 0x30, 0x32, 0x31, 0x34, 0x31, 0x34, 0x33, 0x39, 0x33, 0x39, 0x5a, 0x30, 0x39,
+ 0x31, 0x29, 0x30, 0x27, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x20, 0x35, 0x39, 0x61, 0x65, 0x35,
+ 0x30, 0x39, 0x38, 0x39, 0x35, 0x65, 0x31, 0x33, 0x34, 0x32, 0x35, 0x66, 0x31, 0x32, 0x31, 0x39,
+ 0x33, 0x63, 0x30, 0x34, 0x63, 0x65, 0x35, 0x32, 0x34, 0x36, 0x36, 0x31, 0x0c, 0x30, 0x0a, 0x06,
+ 0x03, 0x55, 0x04, 0x0a, 0x13, 0x03, 0x54, 0x45, 0x45, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a,
+ 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07,
+ 0x03, 0x42, 0x00, 0x04, 0x30, 0x32, 0xcd, 0x95, 0x12, 0xb0, 0x71, 0x8b, 0xb7, 0x14, 0x44, 0x26,
+ 0x58, 0xd5, 0x82, 0x8c, 0x25, 0x55, 0x2c, 0x6d, 0xef, 0x98, 0xe3, 0x4f, 0x88, 0xd0, 0x74, 0x82,
+ 0x09, 0x3e, 0x8d, 0x6c, 0xf0, 0xf2, 0x18, 0xd5, 0x83, 0x0e, 0x0d, 0xf2, 0xce, 0xc5, 0x15, 0x38,
+ 0xe5, 0x6a, 0xe6, 0x4d, 0x4d, 0x95, 0x15, 0xb7, 0x24, 0xe7, 0xcb, 0x4b, 0x63, 0x42, 0x21, 0xbc,
+ 0x36, 0xc6, 0x0a, 0xd8, 0xa3, 0x76, 0x30, 0x74, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04,
+ 0x16, 0x04, 0x14, 0x39, 0x81, 0x41, 0x0a, 0xb9, 0xf3, 0xf4, 0x5b, 0x75, 0x97, 0x4a, 0x46, 0xd6,
+ 0x30, 0x9e, 0x1d, 0x7a, 0x3b, 0xec, 0xa8, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18,
+ 0x30, 0x16, 0x80, 0x14, 0x82, 0xbd, 0x00, 0xde, 0xcb, 0xc5, 0xe7, 0x72, 0x87, 0x3d, 0x1c, 0x0a,
+ 0x1e, 0x78, 0x4f, 0xf5, 0xd3, 0xc1, 0x3e, 0xb8, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01,
+ 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f,
+ 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x02, 0x04, 0x30, 0x11, 0x06, 0x0a, 0x2b, 0x06, 0x01,
+ 0x04, 0x01, 0xd6, 0x79, 0x02, 0x01, 0x1e, 0x04, 0x03, 0xa1, 0x01, 0x08, 0x30, 0x0a, 0x06, 0x08,
+ 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x03, 0x48, 0x00, 0x30, 0x45, 0x02, 0x21, 0x00,
+ 0xae, 0xd8, 0x40, 0x9e, 0x37, 0x3e, 0x5c, 0x9c, 0xe2, 0x93, 0x3d, 0x8c, 0xf7, 0x05, 0x10, 0xe7,
+ 0xd1, 0x2b, 0x87, 0x8a, 0xee, 0xd6, 0x1e, 0x6c, 0x3b, 0xd2, 0x91, 0x3e, 0xa5, 0xdf, 0x91, 0x20,
+ 0x02, 0x20, 0x7f, 0x0f, 0x29, 0x54, 0x60, 0x80, 0x07, 0x50, 0x5f, 0x56, 0x6b, 0x9f, 0xe0, 0x94,
+ 0xb4, 0x3f, 0x3b, 0x0f, 0x61, 0xa0, 0x33, 0x40, 0xe6, 0x1a, 0x42, 0xda, 0x4b, 0xa4, 0xfd, 0x92,
+ 0xb9, 0x0f,
+];
+
lazy_static! {
+ static ref FAKE_PROVISIONED_KEY_BLOB_FOR_TESTING: Mutex<Option<Vec<u8>>> = Mutex::new(None);
static ref VFIO_SERVICE: Strong<dyn IVfioHandler> =
wait_for_interface(<BpVfioHandler as IVfioHandler>::get_descriptor())
.expect("Could not connect to VfioHandler");
@@ -169,10 +237,41 @@
Ok(cids)
}
+ fn enableTestAttestation(&self) -> binder::Result<()> {
+ check_manage_access()?;
+ check_use_custom_virtual_machine()?;
+ if !cfg!(remote_attestation) {
+ return Err(Status::new_exception_str(
+ ExceptionCode::UNSUPPORTED_OPERATION,
+ Some(
+ "enableTestAttestation is not supported with the remote_attestation \
+ feature disabled",
+ ),
+ ))
+ .with_log();
+ }
+ let res = generate_ecdsa_p256_key_pair()
+ .context("Failed to generate ECDSA P-256 key pair for testing")
+ .with_log()
+ .or_service_specific_exception(-1)?;
+ match res {
+ Response::GenerateEcdsaP256KeyPair(key_pair) => {
+ FAKE_PROVISIONED_KEY_BLOB_FOR_TESTING
+ .lock()
+ .unwrap()
+ .replace(key_pair.key_blob.to_vec());
+ Ok(())
+ }
+ _ => Err(remote_provisioning::to_service_specific_error(res)),
+ }
+ .with_log()
+ }
+
fn requestAttestation(
&self,
csr: &[u8],
requester_uid: i32,
+ test_mode: bool,
) -> binder::Result<Vec<Certificate>> {
check_manage_access()?;
if !cfg!(remote_attestation) {
@@ -186,14 +285,31 @@
.with_log();
}
info!("Received csr. Requestting attestation...");
- let attestation_key = get_rkpd_attestation_key(
- REMOTELY_PROVISIONED_COMPONENT_SERVICE_NAME,
- requester_uid as u32,
- )
- .context("Failed to retrieve the remotely provisioned keys")
- .with_log()
- .or_service_specific_exception(-1)?;
- let mut certificate_chain = split_x509_certificate_chain(&attestation_key.encodedCertChain)
+ let (key_blob, certificate_chain) = if test_mode {
+ check_use_custom_virtual_machine()?;
+ info!("Using the fake key blob for testing...");
+ (
+ FAKE_PROVISIONED_KEY_BLOB_FOR_TESTING
+ .lock()
+ .unwrap()
+ .clone()
+ .ok_or_else(|| anyhow!("No key blob for testing"))
+ .with_log()
+ .or_service_specific_exception(-1)?,
+ FAKE_CERTIFICATE_FOR_TESTING.to_vec(),
+ )
+ } else {
+ info!("Retrieving the remotely provisioned keys from RKPD...");
+ let attestation_key = get_rkpd_attestation_key(
+ REMOTELY_PROVISIONED_COMPONENT_SERVICE_NAME,
+ requester_uid as u32,
+ )
+ .context("Failed to retrieve the remotely provisioned keys")
+ .with_log()
+ .or_service_specific_exception(-1)?;
+ (attestation_key.keyBlob, attestation_key.encodedCertChain)
+ };
+ let mut certificate_chain = split_x509_certificate_chain(&certificate_chain)
.context("Failed to split the remotely provisioned certificate chain")
.with_log()
.or_service_specific_exception(-1)?;
@@ -206,7 +322,7 @@
}
let certificate = request_attestation(
csr.to_vec(),
- attestation_key.keyBlob,
+ key_blob,
certificate_chain[0].encodedCertificate.clone(),
)
.context("Failed to request attestation")