Merge "[Telemetry] Include (compos) job cancellation reason"
diff --git a/libs/apkverify/Android.bp b/libs/apkverify/Android.bp
index 2445dd5..d45a77f 100644
--- a/libs/apkverify/Android.bp
+++ b/libs/apkverify/Android.bp
@@ -24,6 +24,7 @@
     defaults: ["libapkverify.defaults"],
     // TODO(b/204562227): move to host_supported to the defaults to include tests
     host_supported: true,
+    apex_available: ["com.android.virt"],
 }
 
 rust_test {
diff --git a/libs/apkverify/src/lib.rs b/libs/apkverify/src/lib.rs
index 290a79a..c5aa9e5 100644
--- a/libs/apkverify/src/lib.rs
+++ b/libs/apkverify/src/lib.rs
@@ -24,4 +24,4 @@
 mod ziputil;
 
 // TODO(jooyung) fallback to v2 when v3 not found
-pub use v3::{get_public_key_der, verify};
+pub use v3::{get_public_key_der, pick_v4_apk_digest, verify};
diff --git a/libs/apkverify/src/sigutil.rs b/libs/apkverify/src/sigutil.rs
index 009154f..2b2f9da 100644
--- a/libs/apkverify/src/sigutil.rs
+++ b/libs/apkverify/src/sigutil.rs
@@ -282,6 +282,7 @@
     }
 }
 
+/// Rank the signature algorithm according to the preferences of the v4 signing scheme.
 pub fn rank_signature_algorithm(algo: u32) -> Result<u32> {
     rank_content_digest_algorithm(to_content_digest_algorithm(algo)?)
 }
diff --git a/libs/apkverify/src/v3.rs b/libs/apkverify/src/v3.rs
index 16530be..96ca7bc 100644
--- a/libs/apkverify/src/v3.rs
+++ b/libs/apkverify/src/v3.rs
@@ -128,16 +128,43 @@
     })
 }
 
+/// Gets the APK digest.
+pub fn pick_v4_apk_digest<R: Read + Seek>(apk: R) -> Result<(u32, Box<[u8]>)> {
+    let mut sections = ApkSections::new(apk)?;
+    let mut block = sections.find_signature(APK_SIGNATURE_SCHEME_V3_BLOCK_ID)?;
+    let signers = block.read::<Signers>()?;
+    if signers.len() != 1 {
+        bail!("should only have one signer");
+    }
+    signers[0].pick_v4_apk_digest()
+}
+
 impl Signer {
-    fn verify<R: Read + Seek>(&self, sections: &mut ApkSections<R>) -> Result<Box<[u8]>> {
-        // 1. Choose the strongest supported signature algorithm ID from signatures. The strength
-        //    ordering is up to each implementation/platform version.
-        let strongest: &Signature = self
+    /// Select the signature that uses the strongest algorithm according to the preferences of the
+    /// v4 signing scheme.
+    fn strongest_signature(&self) -> Result<&Signature> {
+        Ok(self
             .signatures
             .iter()
             .filter(|sig| is_supported_signature_algorithm(sig.signature_algorithm_id))
             .max_by_key(|sig| rank_signature_algorithm(sig.signature_algorithm_id).unwrap())
-            .ok_or_else(|| anyhow!("No supported signatures found"))?;
+            .ok_or_else(|| anyhow!("No supported signatures found"))?)
+    }
+
+    fn pick_v4_apk_digest(&self) -> Result<(u32, Box<[u8]>)> {
+        let strongest = self.strongest_signature()?;
+        let signed_data: SignedData = self.signed_data.slice(..).read()?;
+        let digest = signed_data
+            .digests
+            .iter()
+            .find(|&dig| dig.signature_algorithm_id == strongest.signature_algorithm_id)
+            .ok_or_else(|| anyhow!("Digest not found"))?;
+        Ok((digest.signature_algorithm_id, digest.digest.as_ref().to_vec().into_boxed_slice()))
+    }
+
+    fn verify<R: Read + Seek>(&self, sections: &mut ApkSections<R>) -> Result<Box<[u8]>> {
+        // 1. Choose the strongest supported signature algorithm ID from signatures.
+        let strongest = self.strongest_signature()?;
 
         // 2. Verify the corresponding signature from signatures against signed data using public key.
         //    (It is now safe to parse signed data.)
diff --git a/libs/idsig/Android.bp b/libs/idsig/Android.bp
index 2e9c663..25eeae4 100644
--- a/libs/idsig/Android.bp
+++ b/libs/idsig/Android.bp
@@ -10,6 +10,7 @@
     prefer_rlib: true,
     rustlibs: [
         "libanyhow",
+        "libapkverify",
         "libbyteorder",
         "libnum_traits",
         "libopenssl",
diff --git a/libs/idsig/src/apksigv4.rs b/libs/idsig/src/apksigv4.rs
index 3004ed1..db8a8c6 100644
--- a/libs/idsig/src/apksigv4.rs
+++ b/libs/idsig/src/apksigv4.rs
@@ -15,6 +15,7 @@
  */
 
 use anyhow::{anyhow, bail, Context, Result};
+use apkverify::pick_v4_apk_digest;
 use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
 use num_derive::{FromPrimitive, ToPrimitive};
 use num_traits::{FromPrimitive, ToPrimitive};
@@ -190,9 +191,12 @@
         ret.hashing_info.raw_root_hash = hash_tree.root_hash.into_boxed_slice();
         ret.hashing_info.log2_blocksize = log2(block_size);
 
-        // TODO(jiyong): fill the signing_info struct by reading the APK file. The information,
-        // especially `apk_digest` is needed to check if `V4Signature` is outdated, in which case
-        // it needs to be created from the updated APK.
+        apk.seek(SeekFrom::Start(start))?;
+        let (signature_algorithm_id, apk_digest) = pick_v4_apk_digest(apk)?;
+        ret.signing_info.signature_algorithm_id =
+            SignatureAlgorithmId::from(signature_algorithm_id)?;
+        ret.signing_info.apk_digest = apk_digest;
+        // TODO(jiyong): add a signature to the signing_info struct
 
         Ok(ret)
     }
diff --git a/microdroid/bootconfig.common b/microdroid/bootconfig.common
index eda95a2..362ff23 100644
--- a/microdroid/bootconfig.common
+++ b/microdroid/bootconfig.common
@@ -1,2 +1,3 @@
 androidboot.first_stage_console = 1
 androidboot.hardware = microdroid
+kernel.8250.nr_uarts = 2
diff --git a/microdroid/init.rc b/microdroid/init.rc
index 93cced8..ff3d68e 100644
--- a/microdroid/init.rc
+++ b/microdroid/init.rc
@@ -173,7 +173,7 @@
     mkdir /data/local 0751 root root
     mkdir /data/local/tmp 0771 shell shell
 
-service tombstone_transmit /system/bin/tombstone_transmit.microdroid -cid 2 -port 2000
+service tombstone_transmit /system/bin/tombstone_transmit.microdroid -cid 2 -port 2000 -remove_tombstones_after_transmitting
     user root
     group system
 
diff --git a/microdroid_manager/Android.bp b/microdroid_manager/Android.bp
index 203e889..8343691 100644
--- a/microdroid_manager/Android.bp
+++ b/microdroid_manager/Android.bp
@@ -24,14 +24,15 @@
         "libidsig",
         "libitertools",
         "libkernlog",
+        "libkeystore2_crypto_rust",
         "liblibc",
         "liblog_rust",
         "libmicrodroid_metadata",
         "libmicrodroid_payload_config",
         "libnix",
         "libonce_cell",
+        "libopenssl",
         "libprotobuf",
-        "libring",
         "librustutils",
         "libserde",
         "libserde_cbor",
diff --git a/microdroid_manager/src/instance.rs b/microdroid_manager/src/instance.rs
index 267a0e3..76c8b23 100644
--- a/microdroid_manager/src/instance.rs
+++ b/microdroid_manager/src/instance.rs
@@ -39,8 +39,10 @@
 use anyhow::{anyhow, bail, Context, Result};
 use binder::wait_for_interface;
 use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
-use ring::aead::{Aad, Algorithm, LessSafeKey, Nonce, UnboundKey, AES_256_GCM};
-use ring::hkdf::{Salt, HKDF_SHA256};
+use keystore2_crypto::ZVec;
+use openssl::hkdf::hkdf;
+use openssl::md::Md;
+use openssl::symm::{decrypt_aead, encrypt_aead, Cipher};
 use serde::{Deserialize, Serialize};
 use std::fs::{File, OpenOptions};
 use std::io::{Read, Seek, SeekFrom, Write};
@@ -62,8 +64,11 @@
 /// UUID of the partition that microdroid manager uses
 const MICRODROID_PARTITION_UUID: &str = "cf9afe9a-0662-11ec-a329-c32663a09d75";
 
-/// Encryption algorithm used to cipher payload
-static ENCRYPT_ALG: &Algorithm = &AES_256_GCM;
+/// Size of the AES256-GCM tag
+const AES_256_GCM_TAG_LENGTH: usize = 16;
+
+/// Size of the AES256-GCM nonce
+const AES_256_GCM_NONCE_LENGTH: usize = 12;
 
 /// Handle to the instance disk
 pub struct InstanceDisk {
@@ -118,28 +123,31 @@
         let payload_offset = offset + PARTITION_HEADER_SIZE;
         self.file.seek(SeekFrom::Start(payload_offset))?;
 
-        // Read the 12-bytes nonce (unencrypted)
-        let mut nonce = [0; 12];
+        // Read the nonce (unencrypted)
+        let mut nonce = [0; AES_256_GCM_NONCE_LENGTH];
         self.file.read_exact(&mut nonce)?;
-        let nonce = Nonce::assume_unique_for_key(nonce);
 
         // Read the encrypted payload
-        let payload_size = header.payload_size - 12; // we already have read the nonce
-        let mut data = vec![0; payload_size as usize];
+        let payload_size =
+            header.payload_size as usize - AES_256_GCM_NONCE_LENGTH - AES_256_GCM_TAG_LENGTH;
+        let mut data = vec![0; payload_size];
         self.file.read_exact(&mut data)?;
 
+        // Read the tag
+        let mut tag = [0; AES_256_GCM_TAG_LENGTH];
+        self.file.read_exact(&mut tag)?;
+
         // Read the header as well because it's part of the signed data (though not encrypted).
         let mut header = [0; PARTITION_HEADER_SIZE as usize];
         self.file.seek(SeekFrom::Start(offset))?;
         self.file.read_exact(&mut header)?;
 
-        // Decrypt and authenticate the data (along with the header). The data is decrypted in
-        // place. `open_in_place` returns slice to the decrypted part in the buffer.
-        let plaintext_len = get_key()?.open_in_place(nonce, Aad::from(&header), &mut data)?.len();
-        // Truncate to remove the tag
-        data.truncate(plaintext_len);
+        // Decrypt and authenticate the data (along with the header).
+        let key = get_key()?;
+        let plaintext =
+            decrypt_aead(Cipher::aes_256_gcm(), &key, Some(&nonce), &header, &data, &tag)?;
 
-        let microdroid_data = serde_cbor::from_slice(data.as_slice())?;
+        let microdroid_data = serde_cbor::from_slice(plaintext.as_slice())?;
         Ok(Some(microdroid_data))
     }
 
@@ -148,12 +156,12 @@
     pub fn write_microdroid_data(&mut self, microdroid_data: &MicrodroidData) -> Result<()> {
         let (header, offset) = self.locate_microdroid_header()?;
 
-        let mut data = serde_cbor::to_vec(microdroid_data)?;
+        let data = serde_cbor::to_vec(microdroid_data)?;
 
         // By encrypting and signing the data, tag will be appended. The tag also becomes part of
-        // the encrypted payload which will be written. In addition, a 12-bytes nonce will be
-        // prepended (non-encrypted).
-        let payload_size = (data.len() + ENCRYPT_ALG.tag_len() + 12) as u64;
+        // the encrypted payload which will be written. In addition, a nonce will be prepended
+        // (non-encrypted).
+        let payload_size = (AES_256_GCM_NONCE_LENGTH + data.len() + AES_256_GCM_TAG_LENGTH) as u64;
 
         // If the partition exists, make sure we don't change the partition size. If not (i.e.
         // partition is not found), write the header at the empty place.
@@ -172,16 +180,19 @@
         self.file.read_exact(&mut header)?;
 
         // Generate a nonce randomly and recorde it on the disk first.
-        let nonce = Nonce::assume_unique_for_key(rand::random::<[u8; 12]>());
+        let nonce = rand::random::<[u8; AES_256_GCM_NONCE_LENGTH]>();
         self.file.seek(SeekFrom::Start(offset + PARTITION_HEADER_SIZE))?;
         self.file.write_all(nonce.as_ref())?;
 
-        // Then encrypt and sign the data. The non-encrypted input data is copied to a vector
-        // because it is encrypted in place, and also the tag is appended.
-        get_key()?.seal_in_place_append_tag(nonce, Aad::from(&header), &mut data)?;
+        // Then encrypt and sign the data.
+        let key = get_key()?;
+        let mut tag = [0; AES_256_GCM_TAG_LENGTH];
+        let ciphertext =
+            encrypt_aead(Cipher::aes_256_gcm(), &key, Some(&nonce), &header, &data, &mut tag)?;
 
-        // Persist the encrypted payload data
-        self.file.write_all(&data)?;
+        // Persist the encrypted payload data and the tag.
+        self.file.write_all(&ciphertext)?;
+        self.file.write_all(&tag)?;
         ioutil::blkflsbuf(&mut self.file)?;
 
         Ok(())
@@ -257,63 +268,21 @@
     Ok(ret)
 }
 
-struct ZeroOnDropKey(LessSafeKey);
-
-impl Drop for ZeroOnDropKey {
-    fn drop(&mut self) {
-        // Zeroize the key by overwriting it with a key constructed from zeros of same length
-        // This works because the raw key bytes are allocated inside the struct, not on the heap
-        let zero = [0; 32];
-        let zero_key = LessSafeKey::new(UnboundKey::new(ENCRYPT_ALG, &zero).unwrap());
-        unsafe {
-            ::std::ptr::write_volatile::<LessSafeKey>(&mut self.0, zero_key);
-        }
-    }
-}
-
-impl std::ops::Deref for ZeroOnDropKey {
-    type Target = LessSafeKey;
-    fn deref(&self) -> &LessSafeKey {
-        &self.0
-    }
-}
-
 /// Returns the key that is used to encrypt the microdroid manager partition. It is derived from
 /// the sealing CDI of the previous stage, which is Android Boot Loader (ABL).
-fn get_key() -> Result<ZeroOnDropKey> {
+fn get_key() -> Result<ZVec> {
     // Sealing CDI from the previous stage.
     let diced = wait_for_interface::<dyn IDiceNode>("android.security.dice.IDiceNode")
         .context("IDiceNode service not found")?;
     let bcc_handover = diced.derive(&[]).context("Failed to get BccHandover")?;
-
-    // Derive a key from the Sealing CDI
-    // Step 1 is extraction: https://datatracker.ietf.org/doc/html/rfc5869#section-2.2 where a
-    // pseduo random key (PRK) is extracted from (Input Keying Material - IKM, which is secret) and
-    // optional salt.
-    let salt = Salt::new(HKDF_SHA256, &[]); // use 0 as salt
-    let prk = salt.extract(&bcc_handover.cdiSeal); // Sealing CDI as IKM
-
-    // Step 2 is expansion: https://datatracker.ietf.org/doc/html/rfc5869#section-2.3 where the PRK
-    // (optionally with the `info` which gives contextual information) is expanded into the output
-    // keying material (OKM). Note that the process fails only when the size of OKM is longer than
-    // 255 * SHA256_HASH_SIZE (32), which isn't the case here.
-    let info = [b"microdroid_manager_key".as_ref()];
-    let okm = prk.expand(&info, HKDF_SHA256).unwrap(); // doesn't fail as explained above
-    let mut key = [0; 32];
-    okm.fill(&mut key).unwrap(); // doesn't fail as explained above
-
-    // The term LessSafe might be misleading here. LessSafe here just means that the API can
-    // possibly accept same nonces for different messages. However, since we encrypt/decrypt only a
-    // single message (the microdroid_manager partition payload) with a randomly generated nonce,
-    // this is safe enough.
-    let ret = ZeroOnDropKey(LessSafeKey::new(UnboundKey::new(ENCRYPT_ALG, &key).unwrap()));
-
-    // Don't forget to zeroize the raw key array as well
-    unsafe {
-        ::std::ptr::write_volatile::<[u8; 32]>(&mut key, [0; 32]);
-    }
-
-    Ok(ret)
+    // Deterministically derive another key to use for encrypting the data, rather than using the
+    // CDI directly, so we have the chance to rotate the key if needed. A salt isn't needed as the
+    // input key material is already cryptographically strong.
+    let salt = &[];
+    let info = b"microdroid_manager_key".as_ref();
+    let mut key = ZVec::new(32)?;
+    hkdf(&mut key, Md::sha256(), &bcc_handover.cdiSeal, salt, info)?;
+    Ok(key)
 }
 
 #[derive(Debug, Serialize, Deserialize, PartialEq)]
diff --git a/microdroid_manager/src/main.rs b/microdroid_manager/src/main.rs
index f9b4cf7..929a96b 100644
--- a/microdroid_manager/src/main.rs
+++ b/microdroid_manager/src/main.rs
@@ -34,9 +34,9 @@
 use log::{error, info};
 use microdroid_metadata::{write_metadata, Metadata};
 use microdroid_payload_config::{Task, TaskType, VmPayloadConfig};
+use openssl::sha::Sha512;
 use payload::{get_apex_data_from_payload, load_metadata, to_metadata};
 use rand::Fill;
-use ring::digest;
 use rustutils::system_properties;
 use rustutils::system_properties::PropertyWatcher;
 use std::convert::TryInto;
@@ -148,8 +148,8 @@
 
 fn dice_derivation(verified_data: &MicrodroidData, payload_config_path: &str) -> Result<()> {
     // Calculate compound digests of code and authorities
-    let mut code_hash_ctx = digest::Context::new(&digest::SHA512);
-    let mut authority_hash_ctx = digest::Context::new(&digest::SHA512);
+    let mut code_hash_ctx = Sha512::new();
+    let mut authority_hash_ctx = Sha512::new();
     code_hash_ctx.update(verified_data.apk_data.root_hash.as_ref());
     authority_hash_ctx.update(verified_data.apk_data.pubkey.as_ref());
     for extra_apk in &verified_data.extra_apks_data {
@@ -160,8 +160,8 @@
         code_hash_ctx.update(apex.root_digest.as_ref());
         authority_hash_ctx.update(apex.public_key.as_ref());
     }
-    let code_hash = code_hash_ctx.finish().as_ref().try_into().unwrap();
-    let authority_hash = authority_hash_ctx.finish().as_ref().try_into().unwrap();
+    let code_hash = code_hash_ctx.finish();
+    let authority_hash = authority_hash_ctx.finish();
 
     // {
     //   -70002: "Microdroid payload",
diff --git a/vmbase/example/Android.bp b/vmbase/example/Android.bp
index 9c19693..0acef2b 100644
--- a/vmbase/example/Android.bp
+++ b/vmbase/example/Android.bp
@@ -61,3 +61,29 @@
         },
     },
 }
+
+rust_test {
+    name: "vmbase_example.integration_test",
+    crate_name: "vmbase_example_test",
+    srcs: ["tests/test.rs"],
+    prefer_rlib: true,
+    edition: "2021",
+    rustlibs: [
+        "android.system.virtualizationservice-rust",
+        "libanyhow",
+        "libenv_logger",
+        "liblibc",
+        "liblog_rust",
+        "libvmclient",
+    ],
+    data: [
+        ":vmbase_example",
+    ],
+    test_suites: ["general-tests"],
+    enabled: false,
+    target: {
+        android_arm64: {
+            enabled: true,
+        },
+    },
+}
diff --git a/vmbase/example/tests/test.rs b/vmbase/example/tests/test.rs
new file mode 100644
index 0000000..4928846
--- /dev/null
+++ b/vmbase/example/tests/test.rs
@@ -0,0 +1,91 @@
+// 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.
+
+//! Integration test for VM bootloader.
+
+use android_system_virtualizationservice::{
+    aidl::android::system::virtualizationservice::{
+        VirtualMachineConfig::VirtualMachineConfig,
+        VirtualMachineRawConfig::VirtualMachineRawConfig,
+    },
+    binder::{ParcelFileDescriptor, ProcessState},
+};
+use anyhow::{Context, Error};
+use log::info;
+use std::{
+    fs::File,
+    io,
+    os::unix::io::{AsRawFd, FromRawFd},
+};
+use vmclient::{DeathReason, VmInstance};
+
+const VMBASE_EXAMPLE_PATH: &str =
+    "/data/local/tmp/vmbase_example.integration_test/arm64/vmbase_example.bin";
+
+/// Runs the vmbase_example VM as an unprotected VM via VirtualizationService.
+#[test]
+fn test_run_example_vm() -> Result<(), Error> {
+    env_logger::init();
+
+    // We need to start the thread pool for Binder to work properly, especially link_to_death.
+    ProcessState::start_thread_pool();
+
+    let service = vmclient::connect().context("Failed to find VirtualizationService")?;
+
+    // Start example VM.
+    let bootloader = ParcelFileDescriptor::new(
+        File::open(VMBASE_EXAMPLE_PATH)
+            .with_context(|| format!("Failed to open VM image {}", VMBASE_EXAMPLE_PATH))?,
+    );
+    let config = VirtualMachineConfig::RawConfig(VirtualMachineRawConfig {
+        kernel: None,
+        initrd: None,
+        params: None,
+        bootloader: Some(bootloader),
+        disks: vec![],
+        protectedVm: false,
+        memoryMib: 300,
+        numCpus: 1,
+        cpuAffinity: None,
+        platformVersion: "~1.0".to_string(),
+        taskProfiles: vec![],
+    });
+    let console = duplicate_stdout()?;
+    let log = duplicate_stdout()?;
+    let vm = VmInstance::create(service.as_ref(), &config, Some(console), Some(log))
+        .context("Failed to create VM")?;
+    vm.start().context("Failed to start VM")?;
+    info!("Started example VM.");
+
+    // Wait for VM to finish, and check that it shut down cleanly.
+    let death_reason = vm.wait_for_death();
+    assert_eq!(death_reason, DeathReason::Shutdown);
+
+    Ok(())
+}
+
+/// Safely duplicate the standard output file descriptor.
+fn duplicate_stdout() -> io::Result<File> {
+    let stdout_fd = io::stdout().as_raw_fd();
+    // Safe because this just duplicates a file descriptor which we know to be valid, and we check
+    // for an error.
+    let dup_fd = unsafe { libc::dup(stdout_fd) };
+    if dup_fd < 0 {
+        Err(io::Error::last_os_error())
+    } else {
+        // Safe because we have just duplicated the file descriptor so we own it, and `from_raw_fd`
+        // takes ownership of it.
+        Ok(unsafe { File::from_raw_fd(dup_fd) })
+    }
+}