[client-vm] Build client VM CSR and sign the CSR with two keys

This cl builds the CSR that a client VM sends to the RKP VM for
remote attestation and adjusted the API accordingly as discussed
in the doc go/pvm-remote-attestation

The CSR payload is signed with both the CDI_Leaf_Priv of the
client VM's DICE chain and the attestation key. RKP VM should
verify the signature later with the CDI_Leaf_Pub extracted
from the same DICE chain in the CSR and the attestation public
key.

The new unit tests are added to config at cl/577763874.

Bug: 303807447
Test: run ServiceVmClientTestApp
Test: atest libservice_vm_comm.test
Test: atest microdroid_manager_test
Change-Id: Ic2c09e7339d9981edda028e2694fa551c911a274
diff --git a/service_vm/comm/src/csr.rs b/service_vm/comm/src/csr.rs
new file mode 100644
index 0000000..5e1cbad
--- /dev/null
+++ b/service_vm/comm/src/csr.rs
@@ -0,0 +1,121 @@
+// 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 the structs related to the CSR (Certificate Signing Request)
+//! sent from the client VM to the service VM for attestation.
+
+use alloc::vec;
+use alloc::vec::Vec;
+use ciborium::Value;
+use coset::{self, CborSerializable, CoseError};
+
+/// Represents a CSR sent from the client VM to the service VM for attestation.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct Csr {
+    /// The DICE certificate chain of the client VM.
+    pub dice_cert_chain: Vec<u8>,
+
+    /// The signed CSR payload in COSE_Sign structure, which includes two signatures:
+    /// - one by CDI_Leaf_Priv of the client VM's DICE chain,
+    /// - another by the private key corresponding to the public key.
+    pub signed_csr_payload: Vec<u8>,
+}
+
+impl Csr {
+    /// Serializes this object to a CBOR-encoded vector.
+    pub fn into_cbor_vec(self) -> coset::Result<Vec<u8>> {
+        let value = Value::Array(vec![
+            Value::Bytes(self.dice_cert_chain),
+            Value::Bytes(self.signed_csr_payload),
+        ]);
+        value.to_vec()
+    }
+
+    /// Creates an object instance from the provided CBOR-encoded slice.
+    pub fn from_cbor_slice(data: &[u8]) -> coset::Result<Self> {
+        let value = Value::from_slice(data)?;
+        let Value::Array(mut arr) = value else {
+            return Err(CoseError::UnexpectedItem(cbor_value_type(&value), "array"));
+        };
+        if arr.len() != 2 {
+            return Err(CoseError::UnexpectedItem("array", "array with 2 items"));
+        }
+        Ok(Self {
+            signed_csr_payload: try_as_bytes(arr.remove(1))?,
+            dice_cert_chain: try_as_bytes(arr.remove(0))?,
+        })
+    }
+}
+
+/// Represents the data to be signed and sent from the client VM to the service VM
+/// for attestation.
+///
+/// It will be signed by both CDI_Leaf_Priv of the client VM's DICE chain and
+/// the private key corresponding to the public key to be attested.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct CsrPayload {
+    /// COSE_Key encoded EC P-256 public key to be attested.
+    pub public_key: Vec<u8>,
+
+    /// A random array with a length between 0 and 64.
+    /// It will be included in the certificate chain in the attestation result,
+    /// serving as proof of the freshness of the result.
+    pub challenge: Vec<u8>,
+}
+
+impl CsrPayload {
+    /// Serializes this object to a CBOR-encoded vector.
+    pub fn into_cbor_vec(self) -> coset::Result<Vec<u8>> {
+        let value = Value::Array(vec![Value::Bytes(self.public_key), Value::Bytes(self.challenge)]);
+        value.to_vec()
+    }
+
+    /// Creates an object instance from the provided CBOR-encoded slice.
+    pub fn from_cbor_slice(data: &[u8]) -> coset::Result<Self> {
+        let value = Value::from_slice(data)?;
+        let Value::Array(mut arr) = value else {
+            return Err(CoseError::UnexpectedItem(cbor_value_type(&value), "array"));
+        };
+        if arr.len() != 2 {
+            return Err(CoseError::UnexpectedItem("array", "array with 2 items"));
+        }
+        Ok(Self {
+            challenge: try_as_bytes(arr.remove(1))?,
+            public_key: try_as_bytes(arr.remove(0))?,
+        })
+    }
+}
+
+fn try_as_bytes(v: Value) -> coset::Result<Vec<u8>> {
+    if let Value::Bytes(data) = v {
+        Ok(data)
+    } else {
+        Err(CoseError::UnexpectedItem(cbor_value_type(&v), "bytes"))
+    }
+}
+
+fn cbor_value_type(v: &Value) -> &'static str {
+    match v {
+        Value::Integer(_) => "int",
+        Value::Bytes(_) => "bstr",
+        Value::Float(_) => "float",
+        Value::Text(_) => "tstr",
+        Value::Bool(_) => "bool",
+        Value::Null => "nul",
+        Value::Tag(_, _) => "tag",
+        Value::Array(_) => "array",
+        Value::Map(_) => "map",
+        _ => "other",
+    }
+}