Read the algorithm of the leaf public key in DICE chain in pvmfw

The leaf key algorithm will be used to select the authority key
algorithm in the multi-algorithm DICE handover derivation.

Bug: 357008987
Test: atest MicrodroidHostTests
Change-Id: I7cb93167cb97a7831fd26a2ffbf4cff6bc5748e1
diff --git a/guest/pvmfw/Android.bp b/guest/pvmfw/Android.bp
index 4586cca..a5b7494 100644
--- a/guest/pvmfw/Android.bp
+++ b/guest/pvmfw/Android.bp
@@ -16,6 +16,7 @@
         "libcbor_util_nostd",
         "libciborium_nostd",
         "libciborium_io_nostd",
+        "libcoset_nostd",
         "libcstr",
         "libdiced_open_dice_nostd",
         "liblibfdt_nostd",
diff --git a/guest/pvmfw/src/bcc.rs b/guest/pvmfw/src/bcc.rs
index 5317ce9..9260d7f 100644
--- a/guest/pvmfw/src/bcc.rs
+++ b/guest/pvmfw/src/bcc.rs
@@ -21,6 +21,7 @@
 use ciborium::value::Value;
 use core::fmt;
 use core::mem::size_of;
+use coset::{iana, Algorithm, CborSerializable, CoseKey};
 use diced_open_dice::{BccHandover, Cdi, DiceArtifacts, DiceMode};
 use log::trace;
 
@@ -29,16 +30,24 @@
 pub enum BccError {
     CborDecodeError,
     CborEncodeError,
+    CosetError(coset::CoseError),
     DiceError(diced_open_dice::DiceError),
     MalformedBcc(&'static str),
     MissingBcc,
 }
 
+impl From<coset::CoseError> for BccError {
+    fn from(e: coset::CoseError) -> Self {
+        Self::CosetError(e)
+    }
+}
+
 impl fmt::Display for BccError {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {
             Self::CborDecodeError => write!(f, "Error parsing BCC CBOR"),
             Self::CborEncodeError => write!(f, "Error encoding BCC CBOR"),
+            Self::CosetError(e) => write!(f, "Encountered an error with coset: {e}"),
             Self::DiceError(e) => write!(f, "Dice error: {e:?}"),
             Self::MalformedBcc(s) => {
                 write!(f, "BCC does not have the expected CBOR structure: {s}")
@@ -84,6 +93,7 @@
 /// Represents a (partially) decoded BCC DICE chain.
 pub struct Bcc {
     is_debug_mode: bool,
+    leaf_subject_pubkey: PublicKey,
 }
 
 impl Bcc {
@@ -117,12 +127,18 @@
             .collect::<Result<Vec<_>>>()?;
 
         let is_debug_mode = is_any_payload_debug_mode(&payloads)?;
-        Ok(Self { is_debug_mode })
+        // Safe to unwrap because we checked the length above.
+        let leaf_subject_pubkey = payloads.last().unwrap().subject_public_key()?;
+        Ok(Self { is_debug_mode, leaf_subject_pubkey })
     }
 
     pub fn is_debug_mode(&self) -> bool {
         self.is_debug_mode
     }
+
+    pub fn leaf_subject_pubkey(&self) -> &PublicKey {
+        &self.leaf_subject_pubkey
+    }
 }
 
 fn is_any_payload_debug_mode(payloads: &[BccPayload]) -> Result<bool> {
@@ -144,6 +160,13 @@
 #[repr(transparent)]
 struct BccPayload(Value);
 
+#[derive(Debug, Clone)]
+pub struct PublicKey {
+    /// The COSE key algorithm for the public key, representing the value of the `alg`
+    /// field in the COSE key format of the public key. See RFC 8152, section 7 for details.
+    pub cose_alg: iana::Algorithm,
+}
+
 impl BccEntry {
     pub fn new(entry: Value) -> Self {
         Self(entry)
@@ -178,6 +201,7 @@
 
 const KEY_MODE: i32 = -4670551;
 const MODE_DEBUG: u8 = DiceMode::kDiceModeDebug as u8;
+const SUBJECT_PUBLIC_KEY: i32 = -4670552;
 
 impl BccPayload {
     pub fn is_debug_mode(&self) -> Result<bool> {
@@ -204,6 +228,21 @@
         Ok(mode == MODE_DEBUG.into())
     }
 
+    fn subject_public_key(&self) -> Result<PublicKey> {
+        // BccPayload = {                             ; CWT [RFC8392]
+        // ...
+        //   -4670552 : bstr .cbor PubKeyEd25519 /
+        //              bstr .cbor PubKeyECDSA256 /
+        //              bstr .cbor PubKeyECDSA384,    ; Subject Public Key
+        // ...
+        // }
+        self.value_from_key(SUBJECT_PUBLIC_KEY)
+            .ok_or(BccError::MalformedBcc("Subject public key missing"))?
+            .as_bytes()
+            .ok_or(BccError::MalformedBcc("Subject public key is not a byte string"))
+            .and_then(|v| PublicKey::from_slice(v))
+    }
+
     fn value_from_key(&self, key: i32) -> Option<&Value> {
         // BccPayload is just a map; we only use integral keys, but in general it's legitimate
         // for other things to be present, or for the key we care about not to be present.
@@ -218,3 +257,13 @@
         None
     }
 }
+
+impl PublicKey {
+    fn from_slice(slice: &[u8]) -> Result<Self> {
+        let key = CoseKey::from_slice(slice)?;
+        let Some(Algorithm::Assigned(cose_alg)) = key.alg else {
+            return Err(BccError::MalformedBcc("Invalid algorithm in public key"));
+        };
+        Ok(Self { cose_alg })
+    }
+}
diff --git a/guest/pvmfw/src/main.rs b/guest/pvmfw/src/main.rs
index 1e88c4b..aeced51 100644
--- a/guest/pvmfw/src/main.rs
+++ b/guest/pvmfw/src/main.rs
@@ -200,6 +200,8 @@
         Cow::Owned(truncated_bcc_handover)
     };
 
+    trace!("BCC leaf subject public key algorithm: {:?}", bcc.leaf_subject_pubkey().cose_alg);
+
     dice_inputs
         .write_next_bcc(
             new_bcc_handover.as_ref(),