Merge changes I817bce28,I2189ea4b,I674d8edf into main

* changes:
  [bssl] Implement nostd bssl wrapper for ECDSA sign/verify
  [bssl] Map BoringSSL error code in EC/ECDSA libraries to Error type
  [attestation] Build the BoringSSL EcKey from the COSE public key
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/code.rs b/libs/bssl/error/src/code.rs
index 9b661e9..a318a07 100644
--- a/libs/bssl/error/src/code.rs
+++ b/libs/bssl/error/src/code.rs
@@ -25,6 +25,8 @@
     NoError,
     Global(GlobalError),
     Cipher(CipherError),
+    Ec(EcError),
+    Ecdsa(EcdsaError),
     Unknown(BsslReasonCode, BsslLibraryCode),
 }
 
@@ -102,3 +104,86 @@
         write!(f, "An error occurred in a Cipher function: {self:?}")
     }
 }
+
+/// Errors occurred in the EC functions.
+///
+/// The values are from:
+/// boringssl/src/include/openssl/ec.h
+#[allow(missing_docs)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
+pub enum EcError {
+    BufferTooSmall,
+    CoordinatesOutOfRange,
+    D2IEcpkparametersFailure,
+    EcGroupNewByNameFailure,
+    Group2PkparametersFailure,
+    I2DEcpkparametersFailure,
+    IncompatibleObjects,
+    InvalidCompressedPoint,
+    InvalidCompressionBit,
+    InvalidEncoding,
+    InvalidField,
+    InvalidForm,
+    InvalidGroupOrder,
+    InvalidPrivateKey,
+    MissingParameters,
+    MissingPrivateKey,
+    NonNamedCurve,
+    NotInitialized,
+    Pkparameters2GroupFailure,
+    PointAtInfinity,
+    PointIsNotOnCurve,
+    SlotFull,
+    UndefinedGenerator,
+    UnknownGroup,
+    UnknownOrder,
+    WrongOrder,
+    BignumOutOfRange,
+    WrongCurveParameters,
+    DecodeError,
+    EncodeError,
+    GroupMismatch,
+    InvalidCofactor,
+    PublicKeyValidationFailed,
+    InvalidScalar,
+}
+
+impl From<EcError> for ReasonCode {
+    fn from(e: EcError) -> ReasonCode {
+        ReasonCode::Ec(e)
+    }
+}
+
+impl fmt::Display for EcError {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "An error occurred in an EC function: {self:?}")
+    }
+}
+
+/// Errors occurred in the ECDSA functions.
+///
+/// The values are from:
+/// boringssl/src/include/openssl/ecdsa.h
+#[allow(missing_docs)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
+pub enum EcdsaError {
+    BadSignature,
+    MissingParameters,
+    NeedNewSetupValues,
+    NotImplemented,
+    RandomNumberGenerationFailed,
+    EncodeError,
+    TooManyIterations,
+}
+
+impl From<EcdsaError> for ReasonCode {
+    fn from(e: EcdsaError) -> ReasonCode {
+        ReasonCode::Ecdsa(e)
+    }
+}
+
+impl fmt::Display for EcdsaError {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "An error occurred in an ECDSA function: {self:?}")
+    }
+}
diff --git a/libs/bssl/error/src/lib.rs b/libs/bssl/error/src/lib.rs
index 88929af..89865d4 100644
--- a/libs/bssl/error/src/lib.rs
+++ b/libs/bssl/error/src/lib.rs
@@ -21,7 +21,7 @@
 use core::{fmt, result};
 use serde::{Deserialize, Serialize};
 
-pub use crate::code::{CipherError, GlobalError, ReasonCode};
+pub use crate::code::{CipherError, EcError, EcdsaError, GlobalError, ReasonCode};
 
 /// libbssl_avf result type.
 pub type Result<T> = result::Result<T, Error>;
@@ -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,11 +73,16 @@
     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,
+    ECDSA_sign,
+    ECDSA_size,
+    ECDSA_verify,
     EVP_AEAD_CTX_new,
     EVP_AEAD_CTX_open,
     EVP_AEAD_CTX_seal,
     HKDF,
     HMAC,
     RAND_bytes,
+    SHA256,
 }
diff --git a/libs/bssl/src/ec_key.rs b/libs/bssl/src/ec_key.rs
index 4c1ba5c..7e677c4 100644
--- a/libs/bssl/src/ec_key.rs
+++ b/libs/bssl/src/ec_key.rs
@@ -18,19 +18,28 @@
 use crate::cbb::CbbFixed;
 use crate::cbs::Cbs;
 use crate::util::{check_int_result, to_call_failed_error};
+use alloc::vec;
 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, ECDSA_sign, ECDSA_size,
+    ECDSA_verify, 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 +62,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 +115,73 @@
         check_int_result(ret, ApiName::EC_KEY_check_key)
     }
 
+    /// Verifies the DER-encoded ECDSA `signature` of the `digest` with the current `EcKey`.
+    ///
+    /// Returns Ok(()) if the verification succeeds, otherwise an error will be returned.
+    pub fn ecdsa_verify(&self, signature: &[u8], digest: &[u8]) -> Result<()> {
+        // The `type` argument should be 0 as required in the BoringSSL spec.
+        const TYPE: i32 = 0;
+
+        // SAFETY: This function only reads the given data within its bounds.
+        // The `EC_KEY` passed to this function has been initialized and checked non-null.
+        let ret = unsafe {
+            ECDSA_verify(
+                TYPE,
+                digest.as_ptr(),
+                digest.len(),
+                signature.as_ptr(),
+                signature.len(),
+                self.0.as_ptr(),
+            )
+        };
+        check_int_result(ret, ApiName::ECDSA_verify)
+    }
+
+    /// Signs the `digest` with the current `EcKey` using ECDSA.
+    ///
+    /// Returns the DER-encoded ECDSA signature.
+    pub fn ecdsa_sign(&self, digest: &[u8]) -> Result<Vec<u8>> {
+        // The `type` argument should be 0 as required in the BoringSSL spec.
+        const TYPE: i32 = 0;
+
+        let mut signature = vec![0u8; self.ecdsa_size()?];
+        let mut signature_len = 0;
+        // SAFETY: This function only reads the given data within its bounds.
+        // The `EC_KEY` passed to this function has been initialized and checked non-null.
+        let ret = unsafe {
+            ECDSA_sign(
+                TYPE,
+                digest.as_ptr(),
+                digest.len(),
+                signature.as_mut_ptr(),
+                &mut signature_len,
+                self.0.as_ptr(),
+            )
+        };
+        check_int_result(ret, ApiName::ECDSA_sign)?;
+        if signature.len() < (signature_len as usize) {
+            Err(to_call_failed_error(ApiName::ECDSA_sign))
+        } else {
+            signature.truncate(signature_len as usize);
+            Ok(signature)
+        }
+    }
+
+    /// Returns the maximum size of an ECDSA signature using the current `EcKey`.
+    fn ecdsa_size(&self) -> Result<usize> {
+        // SAFETY: This function only reads the `EC_KEY` that has been initialized
+        // and checked non-null when this instance is created.
+        let size = unsafe { ECDSA_size(self.0.as_ptr()) };
+        if size == 0 {
+            Err(to_call_failed_error(ApiName::ECDSA_size))
+        } else {
+            Ok(size)
+        }
+    }
+
     /// 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 +191,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 +290,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 +341,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 +359,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/src/err.rs b/libs/bssl/src/err.rs
index 1ee40c9..7040441 100644
--- a/libs/bssl/src/err.rs
+++ b/libs/bssl/src/err.rs
@@ -14,7 +14,7 @@
 
 //! Wrappers of the error handling functions in BoringSSL err.h.
 
-use bssl_avf_error::{CipherError, GlobalError, ReasonCode};
+use bssl_avf_error::{CipherError, EcError, EcdsaError, GlobalError, ReasonCode};
 use bssl_ffi::{self, ERR_get_error, ERR_GET_LIB_RUST, ERR_GET_REASON_RUST};
 
 const NO_ERROR_REASON_CODE: i32 = 0;
@@ -75,6 +75,8 @@
 fn map_library_reason_code(reason: i32, lib: i32) -> Option<ReasonCode> {
     u32::try_from(lib).ok().and_then(|x| match x {
         bssl_ffi::ERR_LIB_CIPHER => map_cipher_reason_code(reason).map(ReasonCode::Cipher),
+        bssl_ffi::ERR_LIB_EC => map_ec_reason_code(reason).map(ReasonCode::Ec),
+        bssl_ffi::ERR_LIB_ECDSA => map_ecdsa_reason_code(reason).map(ReasonCode::Ecdsa),
         _ => None,
     })
 }
@@ -110,3 +112,60 @@
     };
     Some(error)
 }
+
+fn map_ec_reason_code(reason: i32) -> Option<EcError> {
+    let error = match reason {
+        bssl_ffi::EC_R_BUFFER_TOO_SMALL => EcError::BufferTooSmall,
+        bssl_ffi::EC_R_COORDINATES_OUT_OF_RANGE => EcError::CoordinatesOutOfRange,
+        bssl_ffi::EC_R_D2I_ECPKPARAMETERS_FAILURE => EcError::D2IEcpkparametersFailure,
+        bssl_ffi::EC_R_EC_GROUP_NEW_BY_NAME_FAILURE => EcError::EcGroupNewByNameFailure,
+        bssl_ffi::EC_R_GROUP2PKPARAMETERS_FAILURE => EcError::Group2PkparametersFailure,
+        bssl_ffi::EC_R_I2D_ECPKPARAMETERS_FAILURE => EcError::I2DEcpkparametersFailure,
+        bssl_ffi::EC_R_INCOMPATIBLE_OBJECTS => EcError::IncompatibleObjects,
+        bssl_ffi::EC_R_INVALID_COMPRESSED_POINT => EcError::InvalidCompressedPoint,
+        bssl_ffi::EC_R_INVALID_COMPRESSION_BIT => EcError::InvalidCompressionBit,
+        bssl_ffi::EC_R_INVALID_ENCODING => EcError::InvalidEncoding,
+        bssl_ffi::EC_R_INVALID_FIELD => EcError::InvalidField,
+        bssl_ffi::EC_R_INVALID_FORM => EcError::InvalidForm,
+        bssl_ffi::EC_R_INVALID_GROUP_ORDER => EcError::InvalidGroupOrder,
+        bssl_ffi::EC_R_INVALID_PRIVATE_KEY => EcError::InvalidPrivateKey,
+        bssl_ffi::EC_R_MISSING_PARAMETERS => EcError::MissingParameters,
+        bssl_ffi::EC_R_MISSING_PRIVATE_KEY => EcError::MissingPrivateKey,
+        bssl_ffi::EC_R_NON_NAMED_CURVE => EcError::NonNamedCurve,
+        bssl_ffi::EC_R_NOT_INITIALIZED => EcError::NotInitialized,
+        bssl_ffi::EC_R_PKPARAMETERS2GROUP_FAILURE => EcError::Pkparameters2GroupFailure,
+        bssl_ffi::EC_R_POINT_AT_INFINITY => EcError::PointAtInfinity,
+        bssl_ffi::EC_R_POINT_IS_NOT_ON_CURVE => EcError::PointIsNotOnCurve,
+        bssl_ffi::EC_R_SLOT_FULL => EcError::SlotFull,
+        bssl_ffi::EC_R_UNDEFINED_GENERATOR => EcError::UndefinedGenerator,
+        bssl_ffi::EC_R_UNKNOWN_GROUP => EcError::UnknownGroup,
+        bssl_ffi::EC_R_UNKNOWN_ORDER => EcError::UnknownOrder,
+        bssl_ffi::EC_R_WRONG_ORDER => EcError::WrongOrder,
+        bssl_ffi::EC_R_BIGNUM_OUT_OF_RANGE => EcError::BignumOutOfRange,
+        bssl_ffi::EC_R_WRONG_CURVE_PARAMETERS => EcError::WrongCurveParameters,
+        bssl_ffi::EC_R_DECODE_ERROR => EcError::DecodeError,
+        bssl_ffi::EC_R_ENCODE_ERROR => EcError::EncodeError,
+        bssl_ffi::EC_R_GROUP_MISMATCH => EcError::GroupMismatch,
+        bssl_ffi::EC_R_INVALID_COFACTOR => EcError::InvalidCofactor,
+        bssl_ffi::EC_R_PUBLIC_KEY_VALIDATION_FAILED => EcError::PublicKeyValidationFailed,
+        bssl_ffi::EC_R_INVALID_SCALAR => EcError::InvalidScalar,
+        _ => return None,
+    };
+    Some(error)
+}
+
+fn map_ecdsa_reason_code(reason: i32) -> Option<EcdsaError> {
+    let error = match reason {
+        bssl_ffi::ECDSA_R_BAD_SIGNATURE => EcdsaError::BadSignature,
+        bssl_ffi::ECDSA_R_MISSING_PARAMETERS => EcdsaError::MissingParameters,
+        bssl_ffi::ECDSA_R_NEED_NEW_SETUP_VALUES => EcdsaError::NeedNewSetupValues,
+        bssl_ffi::ECDSA_R_NOT_IMPLEMENTED => EcdsaError::NotImplemented,
+        bssl_ffi::ECDSA_R_RANDOM_NUMBER_GENERATION_FAILED => {
+            EcdsaError::RandomNumberGenerationFailed
+        }
+        bssl_ffi::ECDSA_R_ENCODE_ERROR => EcdsaError::EncodeError,
+        bssl_ffi::ECDSA_R_TOO_MANY_ITERATIONS => EcdsaError::TooManyIterations,
+        _ => return None,
+    };
+    Some(error)
+}
diff --git a/libs/bssl/src/hmac.rs b/libs/bssl/src/hmac.rs
index ddbbe4a..1b3a403 100644
--- a/libs/bssl/src/hmac.rs
+++ b/libs/bssl/src/hmac.rs
@@ -15,15 +15,14 @@
 //! Wrappers of the HMAC functions in BoringSSL hmac.h.
 
 use crate::digest::Digester;
+use crate::sha::SHA256_DIGEST_LENGTH;
 use crate::util::to_call_failed_error;
 use bssl_avf_error::{ApiName, Result};
-use bssl_ffi::{HMAC, SHA256_DIGEST_LENGTH};
-
-const SHA256_LEN: usize = SHA256_DIGEST_LENGTH as usize;
+use bssl_ffi::HMAC;
 
 /// Computes the HMAC using SHA-256 for the given `data` with the given `key`.
-pub fn hmac_sha256(key: &[u8], data: &[u8]) -> Result<[u8; SHA256_LEN]> {
-    hmac::<SHA256_LEN>(key, data, Digester::sha256())
+pub fn hmac_sha256(key: &[u8], data: &[u8]) -> Result<[u8; SHA256_DIGEST_LENGTH]> {
+    hmac::<SHA256_DIGEST_LENGTH>(key, data, Digester::sha256())
 }
 
 /// Computes the HMAC for the given `data` with the given `key` and `digester`.
diff --git a/libs/bssl/src/lib.rs b/libs/bssl/src/lib.rs
index de81368..8e3abcf 100644
--- a/libs/bssl/src/lib.rs
+++ b/libs/bssl/src/lib.rs
@@ -27,9 +27,10 @@
 mod hkdf;
 mod hmac;
 mod rand;
+mod sha;
 mod util;
 
-pub use bssl_avf_error::{ApiName, CipherError, Error, ReasonCode, Result};
+pub use bssl_avf_error::{ApiName, CipherError, EcError, EcdsaError, Error, ReasonCode, Result};
 
 pub use aead::{Aead, AeadContext, AES_GCM_NONCE_LENGTH};
 pub use cbb::CbbFixed;
@@ -39,3 +40,4 @@
 pub use hkdf::hkdf;
 pub use hmac::hmac_sha256;
 pub use rand::rand_bytes;
+pub use sha::sha256;
diff --git a/libs/bssl/src/sha.rs b/libs/bssl/src/sha.rs
new file mode 100644
index 0000000..6c65d7f
--- /dev/null
+++ b/libs/bssl/src/sha.rs
@@ -0,0 +1,35 @@
+// 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 SHA functions in BoringSSL sha.h.
+
+use crate::util::to_call_failed_error;
+use bssl_avf_error::{ApiName, Result};
+use bssl_ffi::SHA256;
+
+/// The length of a SHA256 digest.
+pub(crate) const SHA256_DIGEST_LENGTH: usize = bssl_ffi::SHA256_DIGEST_LENGTH as usize;
+
+/// Computes the SHA256 digest of the provided `data``.
+pub fn sha256(data: &[u8]) -> Result<[u8; SHA256_DIGEST_LENGTH]> {
+    let mut out = [0u8; SHA256_DIGEST_LENGTH];
+    // SAFETY: This function reads `data` and writes to `out` within its bounds.
+    // `out` has `SHA256_DIGEST_LENGTH` bytes of space for write.
+    let ret = unsafe { SHA256(data.as_ptr(), data.len(), out.as_mut_ptr()) };
+    if ret.is_null() {
+        Err(to_call_failed_error(ApiName::SHA256))
+    } else {
+        Ok(out)
+    }
+}
diff --git a/libs/bssl/tests/eckey_test.rs b/libs/bssl/tests/eckey_test.rs
index a013fba..3dd243c 100644
--- a/libs/bssl/tests/eckey_test.rs
+++ b/libs/bssl/tests/eckey_test.rs
@@ -12,14 +12,70 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use bssl_avf::{EcKey, Result};
+use bssl_avf::{sha256, ApiName, EcKey, EcdsaError, Error, Result};
+use coset::CborSerializable;
+
+const MESSAGE1: &[u8] = b"test message 1";
+const MESSAGE2: &[u8] = b"test message 2";
 
 #[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(())
+}
+
+#[test]
+fn ecdsa_p256_signing_and_verification_succeed() -> Result<()> {
+    let mut ec_key = EcKey::new_p256()?;
+    ec_key.generate_key()?;
+    let digest = sha256(MESSAGE1)?;
+
+    let signature = ec_key.ecdsa_sign(&digest)?;
+    ec_key.ecdsa_verify(&signature, &digest)
+}
+
+#[test]
+fn verifying_ecdsa_p256_signed_with_a_different_key_fails() -> Result<()> {
+    let mut ec_key1 = EcKey::new_p256()?;
+    ec_key1.generate_key()?;
+    let digest = sha256(MESSAGE1)?;
+    let signature = ec_key1.ecdsa_sign(&digest)?;
+
+    let mut ec_key2 = EcKey::new_p256()?;
+    ec_key2.generate_key()?;
+    let err = ec_key2.ecdsa_verify(&signature, &digest).unwrap_err();
+    let expected_err = Error::CallFailed(ApiName::ECDSA_verify, EcdsaError::BadSignature.into());
+    assert_eq!(expected_err, err);
+    Ok(())
+}
+
+#[test]
+fn verifying_ecdsa_p256_signed_with_a_different_message_fails() -> Result<()> {
+    let mut ec_key = EcKey::new_p256()?;
+    ec_key.generate_key()?;
+    let digest1 = sha256(MESSAGE1)?;
+    let signature = ec_key.ecdsa_sign(&digest1)?;
+    let digest2 = sha256(MESSAGE2)?;
+
+    let err = ec_key.ecdsa_verify(&signature, &digest2).unwrap_err();
+    let expected_err = Error::CallFailed(ApiName::ECDSA_verify, EcdsaError::BadSignature.into());
+    assert_eq!(expected_err, err);
+    Ok(())
+}
diff --git a/service_vm/requests/src/client_vm.rs b/service_vm/requests/src/client_vm.rs
index 74c26d3..612605f 100644
--- a/service_vm/requests/src/client_vm.rs
+++ b/service_vm/requests/src/client_vm.rs
@@ -17,22 +17,39 @@
 
 use crate::keyblob::decrypt_private_key;
 use alloc::vec::Vec;
-use bssl_avf::EcKey;
+use bssl_avf::{sha256, EcKey};
 use core::result;
 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| {
+        ecdsa_verify(&ec_public_key, signature, message)
+    })?;
 
     // TODO(b/278717513): Compare client VM's DICE chain in the `csr` up to pvmfw
     // cert with RKP VM's DICE chain.
@@ -49,3 +66,9 @@
     // `_private_key`.
     Err(RequestProcessingError::OperationUnimplemented)
 }
+
+fn ecdsa_verify(key: &EcKey, signature: &[u8], message: &[u8]) -> bssl_avf::Result<()> {
+    // The message was signed with ECDSA with curve P-256 and SHA-256 at the signature generation.
+    let digest = sha256(message)?;
+    key.ecdsa_verify(signature, &digest)
+}
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 =