[attestation] Build the BoringSSL EcKey from the COSE public key

Bug: 309440321
Test: atest rialto_test
Change-Id: I674d8edf2d806e7455b836b7a50a776ac75bc800
diff --git a/libs/bssl/Android.bp b/libs/bssl/Android.bp
index 0a2f334..ff45af9 100644
--- a/libs/bssl/Android.bp
+++ b/libs/bssl/Android.bp
@@ -23,6 +23,7 @@
     rustlibs: [
         "libbssl_avf_error_nostd",
         "libbssl_ffi_nostd",
+        "libciborium_nostd",
         "libcoset_nostd",
         "liblog_rust_nostd",
         "libzeroize_nostd",
@@ -44,5 +45,6 @@
     defaults: ["libbssl_avf_test_defaults"],
     rustlibs: [
         "libbssl_avf_nostd",
+        "libcoset_nostd",
     ],
 }
diff --git a/libs/bssl/error/src/lib.rs b/libs/bssl/error/src/lib.rs
index 88929af..c81d450 100644
--- a/libs/bssl/error/src/lib.rs
+++ b/libs/bssl/error/src/lib.rs
@@ -34,6 +34,12 @@
 
     /// An unexpected internal error occurred.
     InternalError,
+
+    /// Failed to decode the COSE_Key.
+    CoseKeyDecodingFailed,
+
+    /// Unimplemented operation.
+    Unimplemented,
 }
 
 impl fmt::Display for Error {
@@ -43,6 +49,8 @@
                 write!(f, "Failed to invoke the BoringSSL API: {api_name:?}. Reason: {reason}")
             }
             Self::InternalError => write!(f, "An unexpected internal error occurred"),
+            Self::CoseKeyDecodingFailed => write!(f, "Failed to decode the COSE_Key"),
+            Self::Unimplemented => write!(f, "Unimplemented operation"),
         }
     }
 }
@@ -53,6 +61,7 @@
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
 pub enum ApiName {
     BN_new,
+    BN_bin2bn,
     BN_bn2bin_padded,
     CBB_flush,
     CBB_len,
@@ -64,6 +73,7 @@
     EC_KEY_marshal_private_key,
     EC_KEY_parse_private_key,
     EC_KEY_new_by_curve_name,
+    EC_KEY_set_public_key_affine_coordinates,
     EC_POINT_get_affine_coordinates,
     EVP_AEAD_CTX_new,
     EVP_AEAD_CTX_open,
diff --git a/libs/bssl/src/ec_key.rs b/libs/bssl/src/ec_key.rs
index 4c1ba5c..572ad4b 100644
--- a/libs/bssl/src/ec_key.rs
+++ b/libs/bssl/src/ec_key.rs
@@ -21,16 +21,24 @@
 use alloc::vec::Vec;
 use bssl_avf_error::{ApiName, Error, Result};
 use bssl_ffi::{
-    BN_bn2bin_padded, BN_clear_free, BN_new, CBB_flush, CBB_len, EC_GROUP_new_by_curve_name,
-    EC_KEY_check_key, EC_KEY_free, EC_KEY_generate_key, EC_KEY_get0_group, EC_KEY_get0_public_key,
-    EC_KEY_marshal_private_key, EC_KEY_new_by_curve_name, EC_KEY_parse_private_key,
+    BN_bin2bn, BN_bn2bin_padded, BN_clear_free, BN_new, CBB_flush, CBB_len,
+    EC_GROUP_new_by_curve_name, EC_KEY_check_key, EC_KEY_free, EC_KEY_generate_key,
+    EC_KEY_get0_group, EC_KEY_get0_public_key, EC_KEY_marshal_private_key,
+    EC_KEY_new_by_curve_name, EC_KEY_parse_private_key, EC_KEY_set_public_key_affine_coordinates,
     EC_POINT_get_affine_coordinates, NID_X9_62_prime256v1, BIGNUM, EC_GROUP, EC_KEY, EC_POINT,
 };
+use ciborium::Value;
 use core::ptr::{self, NonNull};
 use core::result;
-use coset::{iana, CoseKey, CoseKeyBuilder};
+use coset::{
+    iana::{self, EnumI64},
+    CborSerializable, CoseKey, CoseKeyBuilder, Label,
+};
+use log::error;
 use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};
 
+const ES256_ALGO: iana::Algorithm = iana::Algorithm::ES256;
+const P256_CURVE: iana::EllipticCurve = iana::EllipticCurve::P_256;
 const P256_AFFINE_COORDINATE_SIZE: usize = 32;
 
 type Coordinate = [u8; P256_AFFINE_COORDINATE_SIZE];
@@ -53,10 +61,46 @@
         let ec_key = unsafe {
             EC_KEY_new_by_curve_name(NID_X9_62_prime256v1) // EC P-256 CURVE Nid
         };
-        let mut ec_key = NonNull::new(ec_key)
+        NonNull::new(ec_key)
             .map(Self)
-            .ok_or(to_call_failed_error(ApiName::EC_KEY_new_by_curve_name))?;
-        ec_key.generate_key()?;
+            .ok_or(to_call_failed_error(ApiName::EC_KEY_new_by_curve_name))
+    }
+
+    /// Constructs an `EcKey` instance from the provided COSE_Key encoded public key slice.
+    pub fn from_cose_public_key(cose_key: &[u8]) -> Result<Self> {
+        let cose_key = CoseKey::from_slice(cose_key).map_err(|e| {
+            error!("Failed to deserialize COSE_Key: {e:?}");
+            Error::CoseKeyDecodingFailed
+        })?;
+        if cose_key.alg != Some(coset::Algorithm::Assigned(ES256_ALGO)) {
+            error!(
+                "Only ES256 algorithm is supported. Algo type in the COSE Key: {:?}",
+                cose_key.alg
+            );
+            return Err(Error::Unimplemented);
+        }
+        let crv = get_label_value(&cose_key, Label::Int(iana::Ec2KeyParameter::Crv.to_i64()))?;
+        if &Value::from(P256_CURVE.to_i64()) != crv {
+            error!("Only EC P-256 curve is supported. Curve type in the COSE Key: {crv:?}");
+            return Err(Error::Unimplemented);
+        }
+
+        let x = get_label_value_as_bytes(&cose_key, Label::Int(iana::Ec2KeyParameter::X.to_i64()))?;
+        let y = get_label_value_as_bytes(&cose_key, Label::Int(iana::Ec2KeyParameter::Y.to_i64()))?;
+
+        check_p256_affine_coordinate_size(x)?;
+        check_p256_affine_coordinate_size(y)?;
+
+        let x = BigNum::from_slice(x)?;
+        let y = BigNum::from_slice(y)?;
+
+        let ec_key = EcKey::new_p256()?;
+        // SAFETY: All the parameters are checked non-null and initialized.
+        // The function only reads the coordinates x and y within their bounds.
+        let ret = unsafe {
+            EC_KEY_set_public_key_affine_coordinates(ec_key.0.as_ptr(), x.as_ref(), y.as_ref())
+        };
+        check_int_result(ret, ApiName::EC_KEY_set_public_key_affine_coordinates)?;
         Ok(ec_key)
     }
 
@@ -70,9 +114,23 @@
         check_int_result(ret, ApiName::EC_KEY_check_key)
     }
 
+    /// Verifies the DER-encoded ECDSA `signature` of the `message` with the current `EcKey`.
+    pub fn ecdsa_verify(&self, _signature: &[u8], _message: &[u8]) -> Result<()> {
+        // TODO(b/310634099): Implement ECDSA sign with `bssl::ECDSA_do_sign`.
+        Ok(())
+    }
+
+    /// Signs the `message` with the current `EcKey` using ECDSA.
+    ///
+    /// Returns the DER-encoded ECDSA signature.
+    pub fn ecdsa_sign(&self, _message: &[u8]) -> Result<Vec<u8>> {
+        // TODO(b/310634099): Implement ECDSA verify with `bssl::ECDSA_do_verify`.
+        Ok(Vec::new())
+    }
+
     /// Generates a random, private key, calculates the corresponding public key and stores both
     /// in the `EC_KEY`.
-    fn generate_key(&mut self) -> Result<()> {
+    pub fn generate_key(&mut self) -> Result<()> {
         // SAFETY: The non-null pointer is created with `EC_KEY_new_by_curve_name` and should
         // point to a valid `EC_KEY`.
         // The randomness is provided by `getentropy()` in `vmbase`.
@@ -82,12 +140,10 @@
 
     /// Returns the `CoseKey` for the public key.
     pub fn cose_public_key(&self) -> Result<CoseKey> {
-        const ALGO: iana::Algorithm = iana::Algorithm::ES256;
-        const CURVE: iana::EllipticCurve = iana::EllipticCurve::P_256;
-
         let (x, y) = self.public_key_coordinates()?;
-        let key =
-            CoseKeyBuilder::new_ec2_pub_key(CURVE, x.to_vec(), y.to_vec()).algorithm(ALGO).build();
+        let key = CoseKeyBuilder::new_ec2_pub_key(P256_CURVE, x.to_vec(), y.to_vec())
+            .algorithm(ES256_ALGO)
+            .build();
         Ok(key)
     }
 
@@ -183,6 +239,30 @@
     }
 }
 
+fn get_label_value_as_bytes(key: &CoseKey, label: Label) -> Result<&[u8]> {
+    Ok(get_label_value(key, label)?.as_bytes().ok_or_else(|| {
+        error!("Value not a bstr.");
+        Error::CoseKeyDecodingFailed
+    })?)
+}
+
+fn get_label_value(key: &CoseKey, label: Label) -> Result<&Value> {
+    Ok(&key.params.iter().find(|(k, _)| k == &label).ok_or(Error::CoseKeyDecodingFailed)?.1)
+}
+
+fn check_p256_affine_coordinate_size(coordinate: &[u8]) -> Result<()> {
+    if P256_AFFINE_COORDINATE_SIZE == coordinate.len() {
+        Ok(())
+    } else {
+        error!(
+            "The size of the affine coordinate '{}' does not match the expected size '{}'",
+            coordinate.len(),
+            P256_AFFINE_COORDINATE_SIZE
+        );
+        Err(Error::CoseKeyDecodingFailed)
+    }
+}
+
 /// A u8 vector that is zeroed when dropped.
 #[derive(Zeroize, ZeroizeOnDrop)]
 pub struct ZVec(Vec<u8>);
@@ -210,6 +290,13 @@
 }
 
 impl BigNum {
+    fn from_slice(x: &[u8]) -> Result<Self> {
+        // SAFETY: The function reads `x` within its bounds, and the returned
+        // pointer is checked below.
+        let bn = unsafe { BN_bin2bn(x.as_ptr(), x.len(), ptr::null_mut()) };
+        NonNull::new(bn).map(Self).ok_or(to_call_failed_error(ApiName::BN_bin2bn))
+    }
+
     fn new() -> Result<Self> {
         // SAFETY: The returned pointer is checked below.
         let bn = unsafe { BN_new() };
@@ -221,6 +308,14 @@
     }
 }
 
+impl AsRef<BIGNUM> for BigNum {
+    fn as_ref(&self) -> &BIGNUM {
+        // SAFETY: The pointer is valid and points to an initialized instance of `BIGNUM`
+        // when the instance was created.
+        unsafe { self.0.as_ref() }
+    }
+}
+
 /// Converts the `BigNum` to a big-endian integer. The integer is padded with leading zeros up to
 /// size `N`. The conversion fails if `N` is smaller thanthe size of the integer.
 impl<const N: usize> TryFrom<BigNum> for [u8; N] {
diff --git a/libs/bssl/tests/eckey_test.rs b/libs/bssl/tests/eckey_test.rs
index a013fba..da176ae 100644
--- a/libs/bssl/tests/eckey_test.rs
+++ b/libs/bssl/tests/eckey_test.rs
@@ -13,13 +13,27 @@
 // limitations under the License.
 
 use bssl_avf::{EcKey, Result};
+use coset::CborSerializable;
 
 #[test]
 fn ec_private_key_serialization() -> Result<()> {
-    let ec_key = EcKey::new_p256()?;
+    let mut ec_key = EcKey::new_p256()?;
+    ec_key.generate_key()?;
     let der_encoded_ec_private_key = ec_key.ec_private_key()?;
     let deserialized_ec_key = EcKey::from_ec_private_key(der_encoded_ec_private_key.as_slice())?;
 
     assert_eq!(ec_key.cose_public_key()?, deserialized_ec_key.cose_public_key()?);
     Ok(())
 }
+
+#[test]
+fn cose_public_key_serialization() -> Result<()> {
+    let mut ec_key = EcKey::new_p256()?;
+    ec_key.generate_key()?;
+    let cose_key = ec_key.cose_public_key()?;
+    let cose_key_data = cose_key.clone().to_vec().unwrap();
+    let deserialized_ec_key = EcKey::from_cose_public_key(&cose_key_data)?;
+
+    assert_eq!(cose_key, deserialized_ec_key.cose_public_key()?);
+    Ok(())
+}
diff --git a/service_vm/requests/src/client_vm.rs b/service_vm/requests/src/client_vm.rs
index 74c26d3..b1fd03d 100644
--- a/service_vm/requests/src/client_vm.rs
+++ b/service_vm/requests/src/client_vm.rs
@@ -22,17 +22,34 @@
 use coset::{CborSerializable, CoseSign};
 use diced_open_dice::DiceArtifacts;
 use log::error;
-use service_vm_comm::{ClientVmAttestationParams, Csr, RequestProcessingError};
+use service_vm_comm::{ClientVmAttestationParams, Csr, CsrPayload, RequestProcessingError};
 
 type Result<T> = result::Result<T, RequestProcessingError>;
 
+const ATTESTATION_KEY_SIGNATURE_INDEX: usize = 1;
+
 pub(super) fn request_attestation(
     params: ClientVmAttestationParams,
     dice_artifacts: &dyn DiceArtifacts,
 ) -> Result<Vec<u8>> {
     let csr = Csr::from_cbor_slice(&params.csr)?;
-    let _cose_sign = CoseSign::from_slice(&csr.signed_csr_payload)?;
-    // TODO(b/309440321): Verify the signatures in the `_cose_sign`.
+    let cose_sign = CoseSign::from_slice(&csr.signed_csr_payload)?;
+    let csr_payload = cose_sign.payload.as_ref().ok_or_else(|| {
+        error!("No CsrPayload found in the CSR");
+        RequestProcessingError::InternalError
+    })?;
+    let csr_payload = CsrPayload::from_cbor_slice(csr_payload)?;
+
+    // AAD is empty as defined in service_vm/comm/client_vm_csr.cddl.
+    let aad = &[];
+
+    // TODO(b/310931749): Verify the first signature with CDI_Leaf_Pub of
+    // the DICE chain in `cose_sign`.
+
+    let ec_public_key = EcKey::from_cose_public_key(&csr_payload.public_key)?;
+    cose_sign.verify_signature(ATTESTATION_KEY_SIGNATURE_INDEX, aad, |signature, message| {
+        ec_public_key.ecdsa_verify(signature, message)
+    })?;
 
     // TODO(b/278717513): Compare client VM's DICE chain in the `csr` up to pvmfw
     // cert with RKP VM's DICE chain.
diff --git a/service_vm/requests/src/rkp.rs b/service_vm/requests/src/rkp.rs
index c2c13b3..2c01451 100644
--- a/service_vm/requests/src/rkp.rs
+++ b/service_vm/requests/src/rkp.rs
@@ -44,7 +44,8 @@
     dice_artifacts: &dyn DiceArtifacts,
 ) -> Result<EcdsaP256KeyPair> {
     let hmac_key = derive_hmac_key(dice_artifacts)?;
-    let ec_key = EcKey::new_p256()?;
+    let mut ec_key = EcKey::new_p256()?;
+    ec_key.generate_key()?;
 
     let maced_public_key = build_maced_public_key(ec_key.cose_public_key()?, hmac_key.as_ref())?;
     let key_blob =