Merge "Move device tree compile rule in common places" into main
diff --git a/TEST_MAPPING b/TEST_MAPPING
index adf6309..f5d2dda 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -99,6 +99,9 @@
       "path": "packages/modules/Virtualization/rialto"
     },
     {
+      "path": "packages/modules/Virtualization/service_vm/requests"
+    },
+    {
       "path": "packages/modules/Virtualization/vm"
     },
     {
diff --git a/apex/empty-payload-apk/Android.bp b/apex/empty-payload-apk/Android.bp
index 70e6754..8bd138f 100644
--- a/apex/empty-payload-apk/Android.bp
+++ b/apex/empty-payload-apk/Android.bp
@@ -9,8 +9,8 @@
     apex_available: ["com.android.virt"],
     sdk_version: "system_current",
     jni_uses_platform_apis: true,
-    min_sdk_version: "UpsideDownCake",
-    target_sdk_version: "UpsideDownCake",
+    min_sdk_version: "34",
+    target_sdk_version: "34",
     compile_multilib: "first",
     stl: "none",
 }
diff --git a/compos/benchmark/Android.bp b/compos/benchmark/Android.bp
index dc0c01c..93927a2 100644
--- a/compos/benchmark/Android.bp
+++ b/compos/benchmark/Android.bp
@@ -13,7 +13,7 @@
         "androidx.test.ext.junit",
         "MicrodroidDeviceTestHelper",
         "MicrodroidTestHelper",
-        "truth-prebuilt",
+        "truth",
     ],
     sdk_version: "test_current",
     use_embedded_native_libs: true,
diff --git a/libs/bssl/error/src/code.rs b/libs/bssl/error/src/code.rs
index 7fb36c4..9b661e9 100644
--- a/libs/bssl/error/src/code.rs
+++ b/libs/bssl/error/src/code.rs
@@ -91,6 +91,12 @@
     InvalidNonce,
 }
 
+impl From<CipherError> for ReasonCode {
+    fn from(e: CipherError) -> ReasonCode {
+        ReasonCode::Cipher(e)
+    }
+}
+
 impl fmt::Display for CipherError {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         write!(f, "An error occurred in a Cipher function: {self:?}")
diff --git a/libs/bssl/error/src/lib.rs b/libs/bssl/error/src/lib.rs
index 80398c0..3766c41 100644
--- a/libs/bssl/error/src/lib.rs
+++ b/libs/bssl/error/src/lib.rs
@@ -68,4 +68,5 @@
     EVP_AEAD_CTX_seal,
     HKDF,
     HMAC,
+    RAND_bytes,
 }
diff --git a/libs/bssl/src/aead.rs b/libs/bssl/src/aead.rs
index a7d03b9..e0c9fbb 100644
--- a/libs/bssl/src/aead.rs
+++ b/libs/bssl/src/aead.rs
@@ -23,8 +23,13 @@
 };
 use core::ptr::NonNull;
 
+/// BoringSSL spec recommends to use 12-byte nonces.
+///
+/// https://commondatastorage.googleapis.com/chromium-boringssl-docs/aead.h.html#EVP_aead_aes_256_gcm
+pub const AES_GCM_NONCE_LENGTH: usize = 12;
+
 /// Magic value indicating that the default tag length for an AEAD should be used to
-/// initialize `AeadCtx`.
+/// initialize `AeadContext`.
 const AEAD_DEFAULT_TAG_LENGTH: usize = EVP_AEAD_DEFAULT_TAG_LENGTH as usize;
 
 /// Represents an AEAD algorithm.
@@ -60,12 +65,12 @@
 }
 
 /// Represents an AEAD algorithm configuration.
-pub struct AeadCtx {
+pub struct AeadContext {
     ctx: NonNull<EVP_AEAD_CTX>,
     aead: Aead,
 }
 
-impl Drop for AeadCtx {
+impl Drop for AeadContext {
     fn drop(&mut self) {
         // SAFETY: It is safe because the pointer has been created with `EVP_AEAD_CTX_new`
         // and isn't used after this.
@@ -73,8 +78,8 @@
     }
 }
 
-impl AeadCtx {
-    /// Creates a new `AeadCtx` with the given `Aead` algorithm, `key` and `tag_len`.
+impl AeadContext {
+    /// Creates a new `AeadContext` with the given `Aead` algorithm, `key` and `tag_len`.
     ///
     /// The default tag length will be used if `tag_len` is None.
     pub fn new(aead: Aead, key: &[u8], tag_len: Option<usize>) -> Result<Self> {
@@ -153,7 +158,7 @@
         out.get(0..out_len).ok_or(to_call_failed_error(ApiName::EVP_AEAD_CTX_open))
     }
 
-    /// Returns the `Aead` represented by this `AeadCtx`.
+    /// Returns the `Aead` represented by this `AeadContext`.
     pub fn aead(&self) -> Aead {
         self.aead
     }
diff --git a/libs/bssl/src/lib.rs b/libs/bssl/src/lib.rs
index 898e16c..709e8ad 100644
--- a/libs/bssl/src/lib.rs
+++ b/libs/bssl/src/lib.rs
@@ -25,13 +25,15 @@
 mod err;
 mod hkdf;
 mod hmac;
+mod rand;
 mod util;
 
 pub use bssl_avf_error::{ApiName, CipherError, Error, ReasonCode, Result};
 
-pub use aead::{Aead, AeadCtx};
+pub use aead::{Aead, AeadContext, AES_GCM_NONCE_LENGTH};
 pub use cbb::CbbFixed;
 pub use digest::Digester;
 pub use ec_key::{EcKey, ZVec};
 pub use hkdf::hkdf;
 pub use hmac::hmac_sha256;
+pub use rand::rand_bytes;
diff --git a/libs/bssl/src/rand.rs b/libs/bssl/src/rand.rs
new file mode 100644
index 0000000..9343284
--- /dev/null
+++ b/libs/bssl/src/rand.rs
@@ -0,0 +1,26 @@
+// Copyright 2023, 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.
+
+//! Wrappers of the randon number generations functions in BoringSSL rand.h.
+
+use crate::util::check_int_result;
+use bssl_avf_error::{ApiName, Result};
+use bssl_ffi::RAND_bytes;
+
+/// Fills the given `dest` with random data.
+pub fn rand_bytes(dest: &mut [u8]) -> Result<()> {
+    // SAFETY: This function only writes to the given buffer within its bounds.
+    let ret = unsafe { RAND_bytes(dest.as_mut_ptr(), dest.len()) };
+    check_int_result(ret, ApiName::RAND_bytes)
+}
diff --git a/libs/bssl/tests/aead_test.rs b/libs/bssl/tests/aead_test.rs
index 8ac3f12..8bdb0e7 100644
--- a/libs/bssl/tests/aead_test.rs
+++ b/libs/bssl/tests/aead_test.rs
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use bssl_avf::{Aead, AeadCtx, ApiName, CipherError, Error, ReasonCode, Result};
+use bssl_avf::{Aead, AeadContext, ApiName, CipherError, Error, ReasonCode, Result};
 
 /// The following vectors are generated randomly with:
 /// `hexdump -vn32 -e'32/1 "0x%02x, " 1 "\n"' /dev/urandom`
@@ -38,7 +38,7 @@
     let tag_len = None;
 
     let ad = &[];
-    let aead_ctx = AeadCtx::new(Aead::aes_256_gcm(), &KEY1, tag_len)?;
+    let aead_ctx = AeadContext::new(Aead::aes_256_gcm(), &KEY1, tag_len)?;
     let mut out = vec![0u8; ciphertext.len()];
 
     let plaintext = aead_ctx.open(&ciphertext, &AES_256_GCM_NONCE1, ad, &mut out)?;
@@ -50,7 +50,7 @@
 #[test]
 fn aes_256_gcm_fails_to_encrypt_with_invalid_nonce() -> Result<()> {
     let tag_len = None;
-    let aead_ctx = AeadCtx::new(Aead::aes_256_gcm(), &KEY1, tag_len)?;
+    let aead_ctx = AeadContext::new(Aead::aes_256_gcm(), &KEY1, tag_len)?;
     let nonce = &[];
     let ad = &[];
     let mut out = vec![0u8; MESSAGE.len() + aead_ctx.aead().max_overhead()];
@@ -71,7 +71,7 @@
     let tag_len = None;
 
     let ad = &[];
-    let aead_ctx2 = AeadCtx::new(Aead::aes_256_gcm(), &KEY2, tag_len)?;
+    let aead_ctx2 = AeadContext::new(Aead::aes_256_gcm(), &KEY2, tag_len)?;
     let mut plaintext = vec![0u8; ciphertext.len()];
 
     let err = aead_ctx2.open(&ciphertext, &AES_256_GCM_NONCE1, ad, &mut plaintext).unwrap_err();
@@ -88,7 +88,7 @@
     let tag_len = None;
 
     let ad2 = &[1];
-    let aead_ctx = AeadCtx::new(Aead::aes_256_gcm(), &KEY1, tag_len)?;
+    let aead_ctx = AeadContext::new(Aead::aes_256_gcm(), &KEY1, tag_len)?;
     let mut plaintext = vec![0u8; ciphertext.len()];
 
     let err = aead_ctx.open(&ciphertext, &AES_256_GCM_NONCE1, ad2, &mut plaintext).unwrap_err();
@@ -105,7 +105,7 @@
     let tag_len = None;
 
     let ad = &[];
-    let aead_ctx = AeadCtx::new(Aead::aes_256_gcm(), &KEY1, tag_len)?;
+    let aead_ctx = AeadContext::new(Aead::aes_256_gcm(), &KEY1, tag_len)?;
     let mut plaintext = vec![0u8; ciphertext.len()];
 
     let err = aead_ctx.open(&ciphertext, &AES_256_GCM_NONCE2, ad, &mut plaintext).unwrap_err();
@@ -123,7 +123,7 @@
     let tag_len = None;
 
     let ad = &[];
-    let aead_ctx = AeadCtx::new(Aead::aes_256_gcm(), &KEY1, tag_len)?;
+    let aead_ctx = AeadContext::new(Aead::aes_256_gcm(), &KEY1, tag_len)?;
     let mut plaintext = vec![0u8; ciphertext.len()];
 
     let err = aead_ctx.open(&ciphertext, &AES_256_GCM_NONCE1, ad, &mut plaintext).unwrap_err();
@@ -136,7 +136,7 @@
 
 fn aes_256_gcm_encrypt(message: &[u8]) -> Result<Vec<u8>> {
     let tag_len = None;
-    let aead_ctx = AeadCtx::new(Aead::aes_256_gcm(), &KEY1, tag_len)?;
+    let aead_ctx = AeadContext::new(Aead::aes_256_gcm(), &KEY1, tag_len)?;
     let mut out = vec![0u8; message.len() + aead_ctx.aead().max_overhead()];
 
     assert_eq!(aead_ctx.aead().nonce_length(), AES_256_GCM_NONCE1.len());
diff --git a/rialto/tests/test.rs b/rialto/tests/test.rs
index 6a6dcf4..ee7ecb4 100644
--- a/rialto/tests/test.rs
+++ b/rialto/tests/test.rs
@@ -77,8 +77,9 @@
     info!("Received response: {response:?}.");
 
     match response {
-        Response::GenerateEcdsaP256KeyPair(EcdsaP256KeyPair { maced_public_key, .. }) => {
-            assert_array_has_nonzero(&maced_public_key[..]);
+        Response::GenerateEcdsaP256KeyPair(EcdsaP256KeyPair { maced_public_key, key_blob }) => {
+            assert_array_has_nonzero(&maced_public_key);
+            assert_array_has_nonzero(&key_blob);
             Ok(maced_public_key)
         }
         _ => bail!("Incorrect response type: {response:?}"),
diff --git a/service_vm/requests/Android.bp b/service_vm/requests/Android.bp
index 4b9b46f..f85064a 100644
--- a/service_vm/requests/Android.bp
+++ b/service_vm/requests/Android.bp
@@ -3,7 +3,7 @@
 }
 
 rust_defaults {
-    name: "libservice_vm_requests_defaults",
+    name: "libservice_vm_requests_nostd_defaults",
     crate_name: "service_vm_requests",
     defaults: ["avf_build_flags_rust"],
     srcs: ["src/lib.rs"],
@@ -11,11 +11,6 @@
     apex_available: [
         "com.android.virt",
     ],
-}
-
-rust_library_rlib {
-    name: "libservice_vm_requests_nostd",
-    defaults: ["libservice_vm_requests_defaults"],
     no_stdlibs: true,
     stdlibs: [
         "libcore.rust_sysroot",
@@ -32,3 +27,14 @@
         "libzeroize_nostd",
     ],
 }
+
+rust_library_rlib {
+    name: "libservice_vm_requests_nostd",
+    defaults: ["libservice_vm_requests_nostd_defaults"],
+}
+
+rust_test {
+    name: "libservice_vm_requests.test",
+    defaults: ["libservice_vm_requests_nostd_defaults"],
+    test_suites: ["general-tests"],
+}
diff --git a/service_vm/requests/TEST_MAPPING b/service_vm/requests/TEST_MAPPING
new file mode 100644
index 0000000..c95f9e3
--- /dev/null
+++ b/service_vm/requests/TEST_MAPPING
@@ -0,0 +1,9 @@
+// When adding or removing tests here, don't forget to amend _all_modules list in
+// wireless/android/busytown/ath_config/configs/prod/avf/tests.gcl
+{
+  "avf-presubmit" : [
+    {
+      "name" : "libservice_vm_requests.test"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/service_vm/requests/src/cbor.rs b/service_vm/requests/src/cbor.rs
new file mode 100644
index 0000000..36492e5
--- /dev/null
+++ b/service_vm/requests/src/cbor.rs
@@ -0,0 +1,37 @@
+// Copyright 2023, 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.
+
+//! Utility functions for CBOR serialization/deserialization.
+
+use alloc::vec::Vec;
+use coset::{CoseError, Result};
+use serde::{de::DeserializeOwned, Serialize};
+
+/// Serializes the given data to a CBOR-encoded byte vector.
+pub(crate) fn serialize<T: ?Sized + Serialize>(v: &T) -> Result<Vec<u8>> {
+    let mut data = Vec::new();
+    ciborium::into_writer(v, &mut data)?;
+    Ok(data)
+}
+
+/// Deserializes the given type from a CBOR-encoded byte slice, failing if any extra
+/// data remains after the type has been read.
+pub(crate) fn deserialize<T: DeserializeOwned>(mut data: &[u8]) -> Result<T> {
+    let res = ciborium::from_reader(&mut data)?;
+    if data.is_empty() {
+        Ok(res)
+    } else {
+        Err(CoseError::ExtraneousData)
+    }
+}
diff --git a/service_vm/requests/src/keyblob.rs b/service_vm/requests/src/keyblob.rs
new file mode 100644
index 0000000..a714edd
--- /dev/null
+++ b/service_vm/requests/src/keyblob.rs
@@ -0,0 +1,158 @@
+// Copyright 2023, 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 encryption and decryption of the key blob.
+
+use crate::cbor;
+use alloc::vec;
+use alloc::vec::Vec;
+use bssl_avf::{hkdf, rand_bytes, Aead, AeadContext, Digester, AES_GCM_NONCE_LENGTH};
+use core::result;
+use serde::{Deserialize, Serialize};
+use service_vm_comm::RequestProcessingError;
+// TODO(b/241428146): This will be used once the retrieval mechanism is available.
+#[cfg(test)]
+use zeroize::Zeroizing;
+
+type Result<T> = result::Result<T, RequestProcessingError>;
+
+/// The KEK (Key Encryption Key) info is used as information to derive the KEK using HKDF.
+const KEK_INFO: &[u8] = b"rialto keyblob kek";
+
+/// An all-zero nonce is utilized to encrypt the private key. This is because each key
+/// undergoes encryption using a distinct KEK, which is derived from a secret and a random
+/// salt. Since the uniqueness of the IV/key combination is already guaranteed by the uniqueness
+/// of the KEK, there is no need for an additional random nonce.
+const PRIVATE_KEY_NONCE: &[u8; AES_GCM_NONCE_LENGTH] = &[0; AES_GCM_NONCE_LENGTH];
+
+/// Since Rialto functions as both the sender and receiver of the message, no additional data is
+/// needed.
+const PRIVATE_KEY_AD: &[u8] = &[];
+
+// Encrypted key blob.
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub(crate) enum EncryptedKeyBlob {
+    /// Version 1 key blob.
+    V1(EncryptedKeyBlobV1),
+}
+
+/// Encrypted key blob version 1.
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub(crate) struct EncryptedKeyBlobV1 {
+    /// Salt used to derive the KEK.
+    kek_salt: [u8; 32],
+
+    /// Private key encrypted with AES-256-GCM.
+    encrypted_private_key: Vec<u8>,
+}
+
+impl EncryptedKeyBlob {
+    pub(crate) fn new(private_key: &[u8], kek_secret: &[u8]) -> Result<Self> {
+        EncryptedKeyBlobV1::new(private_key, kek_secret).map(Self::V1)
+    }
+
+    // TODO(b/241428146): Use this function to decrypt the retrieved keyblob once the retrieval
+    // mechanism is available.
+    #[cfg(test)]
+    pub(crate) fn decrypt_private_key(&self, kek_secret: &[u8]) -> Result<Zeroizing<Vec<u8>>> {
+        match self {
+            Self::V1(blob) => blob.decrypt_private_key(kek_secret),
+        }
+    }
+
+    // TODO(b/241428146): This function will be used once the retrieval mechanism is available.
+    #[cfg(test)]
+    pub(crate) fn from_cbor_slice(slice: &[u8]) -> coset::Result<Self> {
+        cbor::deserialize(slice)
+    }
+
+    pub(crate) fn to_cbor_vec(&self) -> coset::Result<Vec<u8>> {
+        cbor::serialize(&self)
+    }
+}
+
+impl EncryptedKeyBlobV1 {
+    fn new(private_key: &[u8], kek_secret: &[u8]) -> Result<Self> {
+        let mut kek_salt = [0u8; 32];
+        rand_bytes(&mut kek_salt)?;
+        let kek = hkdf::<32>(kek_secret, &kek_salt, KEK_INFO, Digester::sha512())?;
+
+        let tag_len = None;
+        let aead_ctx = AeadContext::new(Aead::aes_256_gcm(), kek.as_slice(), tag_len)?;
+        let mut out = vec![0u8; private_key.len() + aead_ctx.aead().max_overhead()];
+        let ciphertext = aead_ctx.seal(private_key, PRIVATE_KEY_NONCE, PRIVATE_KEY_AD, &mut out)?;
+
+        Ok(Self { kek_salt, encrypted_private_key: ciphertext.to_vec() })
+    }
+
+    #[cfg(test)]
+    fn decrypt_private_key(&self, kek_secret: &[u8]) -> Result<Zeroizing<Vec<u8>>> {
+        let kek = hkdf::<32>(kek_secret, &self.kek_salt, KEK_INFO, Digester::sha512())?;
+        let mut out = Zeroizing::new(vec![0u8; self.encrypted_private_key.len()]);
+        let tag_len = None;
+        let aead_ctx = AeadContext::new(Aead::aes_256_gcm(), kek.as_slice(), tag_len)?;
+        let plaintext = aead_ctx.open(
+            &self.encrypted_private_key,
+            PRIVATE_KEY_NONCE,
+            PRIVATE_KEY_AD,
+            &mut out,
+        )?;
+        Ok(Zeroizing::new(plaintext.to_vec()))
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use bssl_avf::{ApiName, CipherError, Error};
+
+    /// The test data are generated randomly with /dev/urandom.
+    const TEST_KEY: [u8; 32] = [
+        0x76, 0xf7, 0xd5, 0x36, 0x1f, 0x78, 0x58, 0x2e, 0x55, 0x2f, 0x88, 0x9d, 0xa3, 0x3e, 0xba,
+        0xfb, 0xc1, 0x2b, 0x17, 0x85, 0x24, 0xdc, 0x0e, 0xc4, 0xbf, 0x6d, 0x2e, 0xe8, 0xa8, 0x36,
+        0x93, 0x62,
+    ];
+    const TEST_SECRET1: [u8; 32] = [
+        0xac, 0xb1, 0x6b, 0xdf, 0x45, 0x30, 0x20, 0xa5, 0x60, 0x6d, 0x81, 0x07, 0x30, 0x68, 0x6e,
+        0x01, 0x3d, 0x5e, 0x86, 0xd6, 0xc6, 0x17, 0xfa, 0xd6, 0xe0, 0xff, 0xd4, 0xf0, 0xb0, 0x7c,
+        0x5c, 0x8f,
+    ];
+    const TEST_SECRET2: [u8; 32] = [
+        0x04, 0x6e, 0xca, 0x30, 0x5e, 0x6c, 0x8f, 0xe5, 0x1a, 0x47, 0x12, 0xbc, 0x45, 0xd7, 0xa8,
+        0x38, 0xfb, 0x06, 0xc6, 0x44, 0xa1, 0x21, 0x40, 0x0b, 0x48, 0x88, 0xe2, 0x31, 0x64, 0x42,
+        0x9d, 0x1c,
+    ];
+
+    #[test]
+    fn decrypting_keyblob_succeeds_with_the_same_kek() -> Result<()> {
+        let encrypted_key_blob = EncryptedKeyBlob::new(&TEST_KEY, &TEST_SECRET1)?.to_cbor_vec()?;
+        let encrypted_key_blob = EncryptedKeyBlob::from_cbor_slice(&encrypted_key_blob)?;
+        let decrypted_key = encrypted_key_blob.decrypt_private_key(&TEST_SECRET1)?;
+
+        assert_eq!(TEST_KEY, decrypted_key.as_slice());
+        Ok(())
+    }
+
+    #[test]
+    fn decrypting_keyblob_fails_with_a_different_kek() -> Result<()> {
+        let encrypted_key_blob = EncryptedKeyBlob::new(&TEST_KEY, &TEST_SECRET1)?.to_cbor_vec()?;
+        let encrypted_key_blob = EncryptedKeyBlob::from_cbor_slice(&encrypted_key_blob)?;
+        let err = encrypted_key_blob.decrypt_private_key(&TEST_SECRET2).unwrap_err();
+
+        let expected_err: RequestProcessingError =
+            Error::CallFailed(ApiName::EVP_AEAD_CTX_open, CipherError::BadDecrypt.into()).into();
+        assert_eq!(expected_err, err);
+        Ok(())
+    }
+}
diff --git a/service_vm/requests/src/lib.rs b/service_vm/requests/src/lib.rs
index fc0c87d..6fa6e0b 100644
--- a/service_vm/requests/src/lib.rs
+++ b/service_vm/requests/src/lib.rs
@@ -19,6 +19,8 @@
 extern crate alloc;
 
 mod api;
+mod cbor;
+mod keyblob;
 mod pub_key;
 mod rkp;
 
diff --git a/service_vm/requests/src/rkp.rs b/service_vm/requests/src/rkp.rs
index f96b85d..2d80f13 100644
--- a/service_vm/requests/src/rkp.rs
+++ b/service_vm/requests/src/rkp.rs
@@ -15,7 +15,9 @@
 //! This module contains functions related to the attestation of the
 //! service VM via the RKP (Remote Key Provisioning) server.
 
-use super::pub_key::{build_maced_public_key, validate_public_key};
+use crate::cbor;
+use crate::keyblob::EncryptedKeyBlob;
+use crate::pub_key::{build_maced_public_key, validate_public_key};
 use alloc::string::String;
 use alloc::vec;
 use alloc::vec::Vec;
@@ -36,7 +38,7 @@
     0x82, 0x80, 0xFA, 0xD3, 0xA8, 0x0A, 0x9A, 0x4B, 0xF7, 0xA5, 0x7D, 0x7B, 0xE9, 0xC3, 0xAB, 0x13,
     0x89, 0xDC, 0x7B, 0x46, 0xEE, 0x71, 0x22, 0xB4, 0x5F, 0x4C, 0x3F, 0xE2, 0x40, 0x04, 0x3B, 0x6C,
 ];
-const HMAC_KEY_INFO: &[u8] = b"rialto hmac key";
+const HMAC_KEY_INFO: &[u8] = b"rialto hmac wkey";
 const HMAC_KEY_LENGTH: usize = 32;
 
 pub(super) fn generate_ecdsa_p256_key_pair(
@@ -44,13 +46,12 @@
 ) -> Result<EcdsaP256KeyPair> {
     let hmac_key = derive_hmac_key(dice_artifacts)?;
     let ec_key = EcKey::new_p256()?;
+
     let maced_public_key = build_maced_public_key(ec_key.cose_public_key()?, hmac_key.as_ref())?;
+    let key_blob =
+        EncryptedKeyBlob::new(ec_key.private_key()?.as_slice(), dice_artifacts.cdi_seal())?;
 
-    // TODO(b/279425980): Encrypt the private key in a key blob.
-    // Remove the printing of the private key.
-    log::debug!("Private key: {:?}", ec_key.private_key()?.as_slice());
-
-    let key_pair = EcdsaP256KeyPair { maced_public_key, key_blob: Vec::new() };
+    let key_pair = EcdsaP256KeyPair { maced_public_key, key_blob: key_blob.to_cbor_vec()? };
     Ok(key_pair)
 }
 
@@ -80,7 +81,7 @@
         // TODO(b/299256925): Add device info in CBOR format here.
         Value::Array(public_keys),
     ])?;
-    let csr_payload = cbor_to_vec(&csr_payload)?;
+    let csr_payload = cbor::serialize(&csr_payload)?;
 
     // Builds `SignedData`.
     let signed_data_payload =
@@ -91,17 +92,15 @@
     // Currently `UdsCerts` is left empty because it is only needed for Samsung devices.
     // Check http://b/301574013#comment3 for more information.
     let uds_certs = Value::Map(Vec::new());
-    let dice_cert_chain = dice_artifacts
-        .bcc()
-        .map(read_to_value)
-        .ok_or(RequestProcessingError::MissingDiceChain)??;
+    let dice_cert_chain = dice_artifacts.bcc().ok_or(RequestProcessingError::MissingDiceChain)?;
+    let dice_cert_chain: Value = cbor::deserialize(dice_cert_chain)?;
     let auth_req = cbor!([
         Value::Integer(AUTH_REQ_SCHEMA_V1.into()),
         uds_certs,
         dice_cert_chain,
         signed_data,
     ])?;
-    cbor_to_vec(&auth_req)
+    Ok(cbor::serialize(&auth_req)?)
 }
 
 fn derive_hmac_key(dice_artifacts: &dyn DiceArtifacts) -> Result<Zeroizing<[u8; HMAC_KEY_LENGTH]>> {
@@ -123,7 +122,7 @@
     let protected = HeaderBuilder::new().algorithm(signing_algorithm).build();
     let signed_data = CoseSign1Builder::new()
         .protected(protected)
-        .payload(cbor_to_vec(payload)?)
+        .payload(cbor::serialize(payload)?)
         .try_create_signature(&[], |message| sign_message(message, &cdi_leaf_priv))?
         .build();
     Ok(signed_data)
@@ -142,24 +141,3 @@
         })?
         .to_vec())
 }
-
-fn cbor_to_vec(v: &Value) -> Result<Vec<u8>> {
-    let mut data = Vec::new();
-    ciborium::into_writer(v, &mut data).map_err(coset::CoseError::from)?;
-    Ok(data)
-}
-
-/// Read a CBOR `Value` from a byte slice, failing if any extra data remains
-/// after the `Value` has been read.
-fn read_to_value(mut data: &[u8]) -> Result<Value> {
-    let value = ciborium::from_reader(&mut data).map_err(|e| {
-        error!("Failed to deserialize the data into CBOR value: {e}");
-        RequestProcessingError::CborValueError
-    })?;
-    if data.is_empty() {
-        Ok(value)
-    } else {
-        error!("CBOR input has extra data.");
-        Err(RequestProcessingError::CborValueError)
-    }
-}
diff --git a/tests/benchmark/Android.bp b/tests/benchmark/Android.bp
index 80fdff7..657241c 100644
--- a/tests/benchmark/Android.bp
+++ b/tests/benchmark/Android.bp
@@ -14,7 +14,7 @@
         "androidx.test.runner",
         "androidx.test.ext.junit",
         "com.android.microdroid.testservice-java",
-        "truth-prebuilt",
+        "truth",
     ],
     jni_libs: [
         "MicrodroidBenchmarkNativeLib",
diff --git a/tests/helper/Android.bp b/tests/helper/Android.bp
index 6f07efd..614c70c 100644
--- a/tests/helper/Android.bp
+++ b/tests/helper/Android.bp
@@ -17,7 +17,7 @@
         "androidx.test.ext.junit",
         "com.android.microdroid.testservice-java",
         "MicrodroidTestHelper",
-        "truth-prebuilt",
+        "truth",
     ],
     sdk_version: "test_current",
 }
diff --git a/tests/hostside/helper/Android.bp b/tests/hostside/helper/Android.bp
index e8b6f36..75553d0 100644
--- a/tests/hostside/helper/Android.bp
+++ b/tests/hostside/helper/Android.bp
@@ -9,7 +9,7 @@
         "androidx.annotation_annotation",
         "compatibility-tradefed",
         "tradefed",
-        "truth-prebuilt",
+        "truth",
     ],
     static_libs: [
         "MicrodroidTestHelper",
diff --git a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
index 8e12114..692b1b8 100644
--- a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
+++ b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
@@ -190,7 +190,7 @@
         long start = System.currentTimeMillis();
         while ((System.currentTimeMillis() - start < timeoutMillis)
                 && !matcher.matches(callable.call())) {
-            Thread.sleep(500);
+            RunUtil.getDefault().sleep(500);
         }
         assertThat(callable.call(), matcher);
     }
diff --git a/tests/no_avf/Android.bp b/tests/no_avf/Android.bp
index fd0d5e2..22d099e 100644
--- a/tests/no_avf/Android.bp
+++ b/tests/no_avf/Android.bp
@@ -13,9 +13,9 @@
         "androidx.test.runner",
         "androidx.test.ext.junit",
         "compatibility-common-util-devicesidelib",
-        "truth-prebuilt",
+        "truth",
     ],
     sdk_version: "test_current",
     compile_multilib: "both",
-    min_sdk_version: "UpsideDownCake",
+    min_sdk_version: "34",
 }
diff --git a/tests/testapk/Android.bp b/tests/testapk/Android.bp
index 526f240..6e8216f 100644
--- a/tests/testapk/Android.bp
+++ b/tests/testapk/Android.bp
@@ -41,7 +41,7 @@
         "androidx.test.ext.junit",
         "authfs_test_apk_assets",
         "cbor-java",
-        "truth-prebuilt",
+        "truth",
         "compatibility-common-util-devicesidelib",
         "measure_io_as_jar",
     ],
diff --git a/tests/vmshareapp/Android.bp b/tests/vmshareapp/Android.bp
index 6c2c9e4..5f6dc57 100644
--- a/tests/vmshareapp/Android.bp
+++ b/tests/vmshareapp/Android.bp
@@ -12,5 +12,5 @@
         // Defined in ../testapk/Android.bp
         "MicrodroidPayloadInOtherAppNativeLib",
     ],
-    min_sdk_version: "UpsideDownCake",
+    min_sdk_version: "34",
 }