[rkp] Encrypt/decrypt the private key with AES-256-GCM

This cl implements the private key encryption and decryption
with AES-256-GCM.
The KEK is derived from the sealing CDI with a random salt
generated with TRNG.

The test is added to the busy town config at cl/570947834.

Bug: 279425980
Test: atest rialto_test
Test: atest libservice_vm_requests.test
Change-Id: I214ee37c64cb8508083b02376c8a398ca6049e3b
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/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..74bde84 100644
--- a/libs/bssl/src/aead.rs
+++ b/libs/bssl/src/aead.rs
@@ -23,6 +23,11 @@
 };
 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`.
 const AEAD_DEFAULT_TAG_LENGTH: usize = EVP_AEAD_DEFAULT_TAG_LENGTH as usize;
diff --git a/libs/bssl/src/lib.rs b/libs/bssl/src/lib.rs
index 898e16c..ba4ec1f 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, AeadCtx, 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/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/keyblob.rs b/service_vm/requests/src/keyblob.rs
new file mode 100644
index 0000000..5558836
--- /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, AeadCtx, 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 = AeadCtx::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 = AeadCtx::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 eec0253..6fa6e0b 100644
--- a/service_vm/requests/src/lib.rs
+++ b/service_vm/requests/src/lib.rs
@@ -20,6 +20,7 @@
 
 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 bbb688e..2d80f13 100644
--- a/service_vm/requests/src/rkp.rs
+++ b/service_vm/requests/src/rkp.rs
@@ -16,6 +16,7 @@
 //! service VM via the RKP (Remote Key Provisioning) server.
 
 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;
@@ -37,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(
@@ -45,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)
 }