Merge "[rkp] Introduce a new request type for attestation request" into main
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;