[rkp] Introduce a new request type for attestation request

The attestation keys will be transmitted to the RKP VM along with
the client VM CSR for attestation, using the newly added request
type later.

The retrieval of remotely provisioned keys has been separated in
a subsequent change to unblock the work of handling the new
request.

Bug: 241428146
Test: m com.android.virt
Test: atest libservice_vm_requests.test rialto_test
Change-Id: I900924996a3f06c13e1d2ca11f7edfc2a518ffc2
diff --git a/service_vm/comm/src/client_vm_csr.cddl b/service_vm/comm/src/client_vm_csr.cddl
new file mode 100644
index 0000000..bbc709a
--- /dev/null
+++ b/service_vm/comm/src/client_vm_csr.cddl
@@ -0,0 +1,62 @@
+; CDDL for the CSR sent from the client VM to the RKP VM for pVM remote attestation.
+
+Csr = [
+    DiceCertChain,      ; The DICE chain containing measurement of the client VM. See
+                        ; keymint/generateCertificateRequestV2.cddl for the DiceCertChain
+                        ; definition.
+    SignedData,
+]
+
+; COSE_Sign [RFC9052 s4.1]
+SignedData = [
+    protected: {},            ; The signing algorithms are specified in each signature
+                              ; separately.
+    unprotected: {},
+    payload: bstr .cbor CsrPayload,
+    Signatures,
+]
+
+CsrPayload = [                    ; CBOR Array defining the payload for CSR
+   challenge: bstr .size (0..64), ; The challenge is provided by the client server.
+                                  ; It will be included in the certificate chain in the
+                                  ; attestation result, serving as proof of the freshness
+                                  ; of the result.
+   PublicKey,                     ; COSE_Key encoded EC P-256 public key [ RFC9053 s7.1.1 ]
+                                  ; to be attested. See keymint/PublicKey.cddl for the
+                                  ; definition, the test flag `-70000` is never used.
+]
+
+Signatures = [
+    dice_cdi_leaf_signature: COSE_Signature_Dice_Cdi_Leaf,
+    attestation_key_signature: COSE_Signature_Attestation_Key,
+]
+
+; COSE_Signature [RFC9052 s4.1]
+COSE_Signature_Dice_Cdi_Leaf = [
+    protected: bstr .cbor { 1: AlgorithmEdDSA },
+    unprotected: {},
+    signature: bstr,                         ; Ed25519(CDI_Leaf_Priv, SigStruct)
+]
+
+; COSE_Signature [RFC9052 s4.1]
+COSE_Signature_Attestation_Key = [
+    protected: bstr .cbor { 1: AlgorithmES256 },
+    unprotected: {},
+    signature: bstr,                         ; ECDSA(PrivateKey, SigStruct)
+]
+
+; Sig_structure for SignedData [ RFC9052 s4.4 ]
+SigStruct = {
+    context: "Signature",
+    external_aad: bstr .size 0,
+    payload: bstr .cbor CsrPayload,
+}
+
+; ASN.1 DER-encoded EC P-256 ECPrivateKey [ RFC 5915 s3 ]:
+; ECPrivateKey ::= SEQUENCE {
+;     version        INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1),
+;     privateKey     OCTET STRING,
+;     parameters [0] ECParameters {{ NamedCurve }} OPTIONAL,
+;     publicKey  [1] BIT STRING OPTIONAL
+;}
+PrivateKey = bstr
diff --git a/service_vm/comm/src/csr.rs b/service_vm/comm/src/csr.rs
index 5e1cbad..e913bb5 100644
--- a/service_vm/comm/src/csr.rs
+++ b/service_vm/comm/src/csr.rs
@@ -21,6 +21,8 @@
 use coset::{self, CborSerializable, CoseError};
 
 /// Represents a CSR sent from the client VM to the service VM for attestation.
+///
+/// See client_vm_csr.cddl for the definition of the CSR.
 #[derive(Clone, Debug, Eq, PartialEq)]
 pub struct Csr {
     /// The DICE certificate chain of the client VM.
diff --git a/service_vm/comm/src/lib.rs b/service_vm/comm/src/lib.rs
index 0818f24..bb85a26 100644
--- a/service_vm/comm/src/lib.rs
+++ b/service_vm/comm/src/lib.rs
@@ -25,7 +25,7 @@
 
 pub use csr::{Csr, CsrPayload};
 pub use message::{
-    EcdsaP256KeyPair, GenerateCertificateRequestParams, Request, RequestProcessingError, Response,
-    ServiceVmRequest,
+    ClientVmAttestationParams, EcdsaP256KeyPair, GenerateCertificateRequestParams, Request,
+    RequestProcessingError, Response, ServiceVmRequest,
 };
 pub use vsock::VmType;
diff --git a/service_vm/comm/src/message.rs b/service_vm/comm/src/message.rs
index f8d7420..6dd0ccd 100644
--- a/service_vm/comm/src/message.rs
+++ b/service_vm/comm/src/message.rs
@@ -50,6 +50,22 @@
     /// Creates a certificate signing request to be sent to the
     /// provisioning server.
     GenerateCertificateRequest(GenerateCertificateRequestParams),
+
+    /// Requests the service VM to attest the client VM and issue a certificate
+    /// if the attestation succeeds.
+    RequestClientVmAttestation(ClientVmAttestationParams),
+}
+
+/// Represents the params passed to `Request::RequestClientVmAttestation`.
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub struct ClientVmAttestationParams {
+    /// The CBOR-encoded CSR signed by the CDI_Leaf_Priv of the client VM's DICE chain
+    /// and the private key to be attested.
+    /// See client_vm_csr.cddl for the definition of the CSR.
+    pub csr: Vec<u8>,
+
+    /// The key blob retrieved from RKPD by virtualizationservice.
+    pub remotely_provisioned_key_blob: Vec<u8>,
 }
 
 /// Represents a response to a request sent to the service VM.
@@ -66,6 +82,11 @@
     /// Returns a CBOR Certificate Signing Request (Csr) serialized into a byte array.
     GenerateCertificateRequest(Vec<u8>),
 
+    /// Returns a certificate covering the public key to be attested in the provided CSR.
+    /// The certificate is signed by the remotely provisioned private key and also
+    /// includes an extension that describes the attested client VM.
+    RequestClientVmAttestation(Vec<u8>),
+
     /// Encountered an error during the request processing.
     Err(RequestProcessingError),
 }
@@ -93,6 +114,12 @@
 
     /// The DICE chain of the service VM is missing.
     MissingDiceChain,
+
+    /// Failed to decrypt the remotely provisioned key blob.
+    FailedToDecryptKeyBlob,
+
+    /// The requested operation has not been implemented.
+    OperationUnimplemented,
 }
 
 impl fmt::Display for RequestProcessingError {
@@ -109,6 +136,12 @@
                 write!(f, "An error happened when serializing to/from a CBOR Value.")
             }
             Self::MissingDiceChain => write!(f, "The DICE chain of the service VM is missing"),
+            Self::FailedToDecryptKeyBlob => {
+                write!(f, "Failed to decrypt the remotely provisioned key blob")
+            }
+            Self::OperationUnimplemented => {
+                write!(f, "The requested operation has not been implemented")
+            }
         }
     }
 }
diff --git a/service_vm/requests/src/api.rs b/service_vm/requests/src/api.rs
index eae0370..315d2af 100644
--- a/service_vm/requests/src/api.rs
+++ b/service_vm/requests/src/api.rs
@@ -14,6 +14,7 @@
 
 //! This module contains the main API for the request processing module.
 
+use crate::client_vm;
 use crate::rkp;
 use alloc::vec::Vec;
 use diced_open_dice::DiceArtifacts;
@@ -31,6 +32,8 @@
             rkp::generate_certificate_request(p, dice_artifacts)
                 .map_or_else(Response::Err, Response::GenerateCertificateRequest)
         }
+        Request::RequestClientVmAttestation(p) => client_vm::request_attestation(p, dice_artifacts)
+            .map_or_else(Response::Err, Response::RequestClientVmAttestation),
     }
 }
 
diff --git a/service_vm/requests/src/client_vm.rs b/service_vm/requests/src/client_vm.rs
new file mode 100644
index 0000000..1081f3a
--- /dev/null
+++ b/service_vm/requests/src/client_vm.rs
@@ -0,0 +1,46 @@
+// 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.
+
+//! This module contains functions related to the attestation of the
+//! client VM.
+
+use crate::keyblob::decrypt_private_key;
+use alloc::vec::Vec;
+use core::result;
+use diced_open_dice::DiceArtifacts;
+use log::error;
+use service_vm_comm::{ClientVmAttestationParams, RequestProcessingError};
+
+type Result<T> = result::Result<T, RequestProcessingError>;
+
+pub(super) fn request_attestation(
+    params: ClientVmAttestationParams,
+    dice_artifacts: &dyn DiceArtifacts,
+) -> Result<Vec<u8>> {
+    // TODO(b/309440321): Verify the signatures in the csr.
+
+    // TODO(b/278717513): Compare client VM's DICE chain up to pvmfw cert with
+    // RKP VM's DICE chain.
+
+    let _private_key =
+        decrypt_private_key(&params.remotely_provisioned_key_blob, dice_artifacts.cdi_seal())
+            .map_err(|e| {
+                error!("Failed to decrypt the remotely provisioned key blob: {e}");
+                RequestProcessingError::FailedToDecryptKeyBlob
+            })?;
+
+    // TODO(b/309441500): Build a new certificate signed with the remotely provisioned
+    // private key.
+    Err(RequestProcessingError::OperationUnimplemented)
+}
diff --git a/service_vm/requests/src/keyblob.rs b/service_vm/requests/src/keyblob.rs
index 456c879..1fb7a67 100644
--- a/service_vm/requests/src/keyblob.rs
+++ b/service_vm/requests/src/keyblob.rs
@@ -20,8 +20,6 @@
 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>;
@@ -61,9 +59,6 @@
         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),
@@ -85,7 +80,6 @@
         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()]);
@@ -101,6 +95,15 @@
     }
 }
 
+pub(crate) fn decrypt_private_key(
+    encrypted_key_blob: &[u8],
+    kek_secret: &[u8],
+) -> Result<Zeroizing<Vec<u8>>> {
+    let key_blob: EncryptedKeyBlob = cbor_util::deserialize(encrypted_key_blob)?;
+    let private_key = key_blob.decrypt_private_key(kek_secret)?;
+    Ok(private_key)
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
@@ -127,8 +130,7 @@
     fn decrypting_keyblob_succeeds_with_the_same_kek() -> Result<()> {
         let encrypted_key_blob =
             cbor_util::serialize(&EncryptedKeyBlob::new(&TEST_KEY, &TEST_SECRET1)?)?;
-        let encrypted_key_blob: EncryptedKeyBlob = cbor_util::deserialize(&encrypted_key_blob)?;
-        let decrypted_key = encrypted_key_blob.decrypt_private_key(&TEST_SECRET1)?;
+        let decrypted_key = decrypt_private_key(&encrypted_key_blob, &TEST_SECRET1)?;
 
         assert_eq!(TEST_KEY, decrypted_key.as_slice());
         Ok(())
@@ -138,8 +140,7 @@
     fn decrypting_keyblob_fails_with_a_different_kek() -> Result<()> {
         let encrypted_key_blob =
             cbor_util::serialize(&EncryptedKeyBlob::new(&TEST_KEY, &TEST_SECRET1)?)?;
-        let encrypted_key_blob: EncryptedKeyBlob = cbor_util::deserialize(&encrypted_key_blob)?;
-        let err = encrypted_key_blob.decrypt_private_key(&TEST_SECRET2).unwrap_err();
+        let err = decrypt_private_key(&encrypted_key_blob, &TEST_SECRET2).unwrap_err();
 
         let expected_err: RequestProcessingError =
             Error::CallFailed(ApiName::EVP_AEAD_CTX_open, CipherError::BadDecrypt.into()).into();
diff --git a/service_vm/requests/src/lib.rs b/service_vm/requests/src/lib.rs
index e3c5794..b2db298 100644
--- a/service_vm/requests/src/lib.rs
+++ b/service_vm/requests/src/lib.rs
@@ -19,6 +19,7 @@
 extern crate alloc;
 
 mod api;
+mod client_vm;
 mod keyblob;
 mod pub_key;
 mod rkp;