[bssl] Support conversion from COSE_Key to EVP_PKEY

The subject public key in DICE chain will be encoded as EVP_PKEY
in a subsequent cl to verify the DICE chain signatures.

Bug: 314266221
Test: atest libbssl_avf_nostd.test
Change-Id: I489876025d431a794f08cb12999b34e4920d3507
diff --git a/libs/bssl/src/ec_key.rs b/libs/bssl/src/ec_key.rs
index 511b133..0c944cd 100644
--- a/libs/bssl/src/ec_key.rs
+++ b/libs/bssl/src/ec_key.rs
@@ -17,7 +17,9 @@
 
 use crate::cbb::CbbFixed;
 use crate::cbs::Cbs;
-use crate::util::{check_int_result, to_call_failed_error};
+use crate::util::{
+    check_int_result, get_label_value, get_label_value_as_bytes, to_call_failed_error,
+};
 use alloc::vec;
 use alloc::vec::Vec;
 use bssl_avf_error::{ApiName, Error, Result};
@@ -33,7 +35,7 @@
 use core::ptr::{self, NonNull};
 use coset::{
     iana::{self, EnumI64},
-    CborSerializable, CoseKey, CoseKeyBuilder, Label,
+    CborSerializable, CoseKey, CoseKeyBuilder, KeyType, Label,
 };
 use log::error;
 use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};
@@ -79,13 +81,27 @@
     }
 
     /// Constructs an `EcKey` instance from the provided COSE_Key encoded public key slice.
-    pub fn from_cose_public_key(cose_key: &[u8]) -> Result<Self> {
+    pub fn from_cose_public_key_slice(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
         })?;
+        Self::from_cose_public_key(&cose_key)
+    }
+
+    /// Constructs an `EcKey` instance from the provided `COSE_Key`.
+    ///
+    /// The lifetime of the returned `EcKey` is not tied to the lifetime of the `cose_key`,
+    /// because the affine coordinates stored in the `cose_key` are copied into the `EcKey`.
+    ///
+    /// Currently, only the EC P-256 and P-384 curves are supported.
+    pub fn from_cose_public_key(cose_key: &CoseKey) -> Result<Self> {
+        if cose_key.kty != KeyType::Assigned(iana::KeyType::EC2) {
+            error!("Only EC2 keys are supported. Key type in the COSE Key: {:?}", cose_key.kty);
+            return Err(Error::Unimplemented);
+        }
         let ec_key =
-            match get_label_value(&cose_key, Label::Int(iana::Ec2KeyParameter::Crv.to_i64()))? {
+            match get_label_value(cose_key, Label::Int(iana::Ec2KeyParameter::Crv.to_i64()))? {
                 crv if crv == &Value::from(P256_CURVE.to_i64()) => EcKey::new_p256()?,
                 crv if crv == &Value::from(P384_CURVE.to_i64()) => EcKey::new_p384()?,
                 crv => {
@@ -96,8 +112,8 @@
                     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()))?;
+        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()))?;
 
         let group = ec_key.ec_group()?;
         group.check_affine_coordinate_size(x)?;
@@ -309,17 +325,6 @@
     }
 }
 
-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)
-}
-
 /// Wrapper of an `EC_GROUP` reference.
 struct EcGroup<'a>(&'a EC_GROUP);
 
diff --git a/libs/bssl/src/evp.rs b/libs/bssl/src/evp.rs
index 330f317..86f99a8 100644
--- a/libs/bssl/src/evp.rs
+++ b/libs/bssl/src/evp.rs
@@ -17,15 +17,23 @@
 use crate::cbb::CbbFixed;
 use crate::digest::{Digester, DigesterContext};
 use crate::ec_key::EcKey;
-use crate::util::{check_int_result, to_call_failed_error};
+use crate::util::{
+    check_int_result, get_label_value, get_label_value_as_bytes, to_call_failed_error,
+};
 use alloc::vec::Vec;
-use bssl_avf_error::{ApiName, Result};
+use bssl_avf_error::{ApiName, Error, Result};
 use bssl_ffi::{
     CBB_flush, CBB_len, EVP_DigestVerify, EVP_DigestVerifyInit, EVP_PKEY_free, EVP_PKEY_new,
     EVP_PKEY_new_raw_public_key, EVP_PKEY_set1_EC_KEY, EVP_marshal_public_key, EVP_PKEY,
     EVP_PKEY_ED25519, EVP_PKEY_X25519,
 };
+use ciborium::Value;
 use core::ptr::{self, NonNull};
+use coset::{
+    iana::{self, EnumI64},
+    CoseKey, KeyType, Label,
+};
+use log::error;
 
 /// Wrapper of an `EVP_PKEY` object, representing a public or private key.
 pub struct PKey {
@@ -116,6 +124,43 @@
         Ok(Self { pkey, _inner_ec_key: None })
     }
 
+    /// Creates a `PKey` from the given `cose_key`.
+    ///
+    /// The lifetime of the returned instance is not tied to the lifetime of the `cose_key` as the
+    /// data of `cose_key` is copied into the `EVP_PKEY` or `EC_KEY` object.
+    pub fn from_cose_public_key(cose_key: &CoseKey) -> Result<Self> {
+        match &cose_key.kty {
+            KeyType::Assigned(iana::KeyType::EC2) => {
+                EcKey::from_cose_public_key(cose_key)?.try_into()
+            }
+            KeyType::Assigned(iana::KeyType::OKP) => {
+                let curve_type =
+                    get_label_value(cose_key, Label::Int(iana::OkpKeyParameter::Crv.to_i64()))?;
+                let curve_type = match curve_type {
+                    crv if crv == &Value::from(iana::EllipticCurve::Ed25519.to_i64()) => {
+                        PKeyType::ED25519
+                    }
+                    crv if crv == &Value::from(iana::EllipticCurve::X25519.to_i64()) => {
+                        PKeyType::X25519
+                    }
+                    crv => {
+                        error!("Unsupported curve type in OKP COSE key: {:?}", crv);
+                        return Err(Error::Unimplemented);
+                    }
+                };
+                let x = get_label_value_as_bytes(
+                    cose_key,
+                    Label::Int(iana::OkpKeyParameter::X.to_i64()),
+                )?;
+                Self::new_raw_public_key(x, curve_type)
+            }
+            kty => {
+                error!("Unsupported key type in COSE key: {:?}", kty);
+                Err(Error::Unimplemented)
+            }
+        }
+    }
+
     /// Verifies the given `signature` of the `message` using the current public key.
     ///
     /// The `message` will be hashed using the given `digester` before verification.
diff --git a/libs/bssl/src/util.rs b/libs/bssl/src/util.rs
index 880c85b..7473fe1 100644
--- a/libs/bssl/src/util.rs
+++ b/libs/bssl/src/util.rs
@@ -16,6 +16,8 @@
 
 use crate::err::get_error_reason_code;
 use bssl_avf_error::{ApiName, Error, Result};
+use ciborium::Value;
+use coset::{CoseKey, Label};
 use log::error;
 
 pub(crate) fn check_int_result(ret: i32, api_name: ApiName) -> Result<()> {
@@ -35,3 +37,14 @@
 pub(crate) fn to_call_failed_error(api_name: ApiName) -> Error {
     Error::CallFailed(api_name, get_error_reason_code())
 }
+
+pub(crate) 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
+    })?)
+}
+
+pub(crate) fn get_label_value(key: &CoseKey, label: Label) -> Result<&Value> {
+    Ok(&key.params.iter().find(|(k, _)| k == &label).ok_or(Error::CoseKeyDecodingFailed)?.1)
+}
diff --git a/libs/bssl/tests/eckey_test.rs b/libs/bssl/tests/eckey_test.rs
index 265dee7..9c7eb4f 100644
--- a/libs/bssl/tests/eckey_test.rs
+++ b/libs/bssl/tests/eckey_test.rs
@@ -72,7 +72,7 @@
     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)?;
+    let deserialized_ec_key = EcKey::from_cose_public_key_slice(&cose_key_data)?;
 
     assert_eq!(cose_key, deserialized_ec_key.cose_public_key()?);
     Ok(())
@@ -88,7 +88,9 @@
 
     let signature = ec_key.ecdsa_sign(&digest)?;
     ec_key.ecdsa_verify(&signature, &digest)?;
-    let pkey: PKey = ec_key.try_into()?;
+    // Building a `PKey` from a temporary `CoseKey` should work as the lifetime
+    // of the `PKey` is not tied to the lifetime of the `CoseKey`.
+    let pkey = PKey::from_cose_public_key(&ec_key.cose_public_key()?)?;
     pkey.verify(&signature, MESSAGE1, Some(digester))
 }
 
@@ -101,7 +103,7 @@
 
     let signature = ec_key.ecdsa_sign(&digest)?;
     ec_key.ecdsa_verify(&signature, &digest)?;
-    let pkey: PKey = ec_key.try_into()?;
+    let pkey = PKey::from_cose_public_key(&ec_key.cose_public_key()?)?;
     pkey.verify(&signature, MESSAGE1, Some(digester))
 }
 
diff --git a/rialto/tests/test.rs b/rialto/tests/test.rs
index c1a8394..029895f 100644
--- a/rialto/tests/test.rs
+++ b/rialto/tests/test.rs
@@ -226,7 +226,8 @@
     parent_certificate: &X509Certificate,
 ) -> Result<()> {
     let cose_mac = CoseMac0::from_slice(maced_public_key)?;
-    let authority_public_key = EcKey::from_cose_public_key(&cose_mac.payload.unwrap()).unwrap();
+    let authority_public_key =
+        EcKey::from_cose_public_key_slice(&cose_mac.payload.unwrap()).unwrap();
     let (remaining, cert) = X509Certificate::from_der(certificate)?;
     assert!(remaining.is_empty());
 
@@ -244,7 +245,7 @@
     let cose_sign = CoseSign::from_slice(&csr.signed_csr_payload)?;
     let csr_payload =
         cose_sign.payload.as_ref().and_then(|v| CsrPayload::from_cbor_slice(v).ok()).unwrap();
-    let subject_public_key = EcKey::from_cose_public_key(&csr_payload.public_key).unwrap();
+    let subject_public_key = EcKey::from_cose_public_key_slice(&csr_payload.public_key).unwrap();
     let expected_spki_data =
         PKey::try_from(subject_public_key).unwrap().subject_public_key_info().unwrap();
     let (remaining, expected_spki) = SubjectPublicKeyInfo::from_der(&expected_spki_data)?;
diff --git a/service_vm/requests/src/client_vm.rs b/service_vm/requests/src/client_vm.rs
index ddf230b..4e87136 100644
--- a/service_vm/requests/src/client_vm.rs
+++ b/service_vm/requests/src/client_vm.rs
@@ -62,7 +62,7 @@
     // the DICE chain in `cose_sign`.
 
     // Verifies the second signature with the public key in the CSR payload.
-    let ec_public_key = EcKey::from_cose_public_key(&csr_payload.public_key)?;
+    let ec_public_key = EcKey::from_cose_public_key_slice(&csr_payload.public_key)?;
     cose_sign.verify_signature(ATTESTATION_KEY_SIGNATURE_INDEX, aad, |signature, message| {
         ecdsa_verify(&ec_public_key, signature, message)
     })?;