Refactor entry parsing to make reusable

With support for degenerate chains coming, extract CoseSign1 and field
validation into reusable units.

Test: atest libcert_request_validator_tests
Change-Id: I87c20a94685bc2576cc14e1b0a939788d5e5fe1a
diff --git a/remote_provisioning/cert_validator/src/cbor/dice/chain.rs b/remote_provisioning/cert_validator/src/cbor/dice/chain.rs
index e2dd579..77abd47 100644
--- a/remote_provisioning/cert_validator/src/cbor/dice/chain.rs
+++ b/remote_provisioning/cert_validator/src/cbor/dice/chain.rs
@@ -1,3 +1,4 @@
+use super::entry::Entry;
 use crate::bcc::entry::Payload;
 use crate::bcc::Chain;
 use crate::cbor::{cose_error, value_from_bytes};
@@ -13,60 +14,38 @@
     /// extracted. This does not perform any semantic validation of the data in the
     /// certificates such as the Authority, Config and Code hashes.
     pub fn from_cbor(bytes: &[u8]) -> Result<Self> {
-        /*
-         * CDDL (from keymint/ProtectedData.aidl):
-         *
-         * Bcc = [
-         *     PubKeyEd25519 / PubKeyECDSA256, // DK_pub
-         *     + BccEntry,                     // Root -> leaf (KM_pub)
-         * ]
-         */
+        let (root_public_key, it) = root_and_entries_from_cbor(bytes)?;
+        Self::from_root_and_entries(root_public_key, it)
+    }
 
-        let value = value_from_bytes(bytes).context("Unable to decode top-level CBOR")?;
-        let array = match value {
-            Array(array) if array.len() >= 2 => array,
-            _ => bail!("Invalid BCC. Expected an array of at least length 2, found: {:?}", value),
-        };
-        let mut it = array.into_iter();
-
-        let root_public_key = CoseKey::from_cbor_value(it.next().unwrap())
-            .map_err(cose_error)
-            .context("Error parsing root public key CBOR")?;
-
-        let root_public_key =
-            PublicKey::from_cose_key(&root_public_key).context("Invalid root key")?;
-        let payloads =
-            check_sign1_chain(it, Some(&root_public_key)).context("Invalid certificate chain")?;
-
-        Self::validate(root_public_key, payloads).context("Building chain")
+    fn from_root_and_entries(root: PublicKey, values: std::vec::IntoIter<Value>) -> Result<Self> {
+        let mut payloads = Vec::with_capacity(values.len());
+        let mut previous_public_key = &root;
+        for (n, value) in values.enumerate() {
+            let entry = Entry::verify_cbor_value(value, previous_public_key)
+                .with_context(|| format!("Invalid entry at index {}", n))?;
+            let payload = Payload::from_cbor(entry.payload())
+                .with_context(|| format!("Invalid payload at index {}", n))?;
+            payloads.push(payload);
+            let previous = payloads.last().unwrap();
+            previous_public_key = previous.subject_public_key();
+        }
+        Self::validate(root, payloads).context("Building chain")
     }
 }
 
-/// Parse a series of BccEntry certificates, represented as CBOR Values, checking the public key of
-/// any given cert's payload in the series correctly signs the next, and verifying the payloads
-/// are well formed. If root_key is specified then it must be the key used to sign the first (root)
-/// certificate; otherwise that signature is not checked.
-fn check_sign1_chain<T: IntoIterator<Item = Value>>(
-    chain: T,
-    root_key: Option<&PublicKey>,
-) -> Result<Vec<Payload>> {
-    let values = chain.into_iter();
-    let mut payloads = Vec::<Payload>::with_capacity(values.size_hint().0);
-
-    let mut previous_public_key = root_key;
-    let mut expected_issuer: Option<&str> = None;
-
-    for (n, value) in values.enumerate() {
-        let payload = Payload::from_cbor_sign1(previous_public_key, expected_issuer, value)
-            .with_context(|| format!("Invalid BccPayload at index {}", n))?;
-        payloads.push(payload);
-
-        let previous = payloads.last().unwrap();
-        expected_issuer = Some(previous.subject());
-        previous_public_key = Some(previous.subject_public_key());
-    }
-
-    Ok(payloads)
+fn root_and_entries_from_cbor(bytes: &[u8]) -> Result<(PublicKey, std::vec::IntoIter<Value>)> {
+    let value = value_from_bytes(bytes).context("Unable to decode top-level CBOR")?;
+    let array = match value {
+        Array(array) if array.len() >= 2 => array,
+        _ => bail!("Invalid BCC. Expected an array of at least length 2, found: {:?}", value),
+    };
+    let mut it = array.into_iter();
+    let root_public_key = CoseKey::from_cbor_value(it.next().unwrap())
+        .map_err(cose_error)
+        .context("Error parsing root public key CBOR")?;
+    let root_public_key = PublicKey::from_cose_key(&root_public_key).context("Invalid root key")?;
+    Ok((root_public_key, it))
 }
 
 #[cfg(test)]
diff --git a/remote_provisioning/cert_validator/src/cbor/dice/entry.rs b/remote_provisioning/cert_validator/src/cbor/dice/entry.rs
index e34db1a..1c92d66 100644
--- a/remote_provisioning/cert_validator/src/cbor/dice/entry.rs
+++ b/remote_provisioning/cert_validator/src/cbor/dice/entry.rs
@@ -24,18 +24,29 @@
 const COMPONENT_VERSION: i64 = -70003;
 const RESETTABLE: i64 = -70004;
 
-impl Payload {
-    pub(super) fn from_cbor_sign1(
-        public_key: Option<&PublicKey>,
-        expected_issuer: Option<&str>,
-        cbor: Value,
-    ) -> Result<Self> {
-        let entry = CoseSign1::from_cbor_value(cbor)
+pub(super) struct Entry {
+    payload: Vec<u8>,
+}
+
+impl Entry {
+    pub(super) fn verify_cbor_value(cbor: Value, key: &PublicKey) -> Result<Self> {
+        let sign1 = CoseSign1::from_cbor_value(cbor)
             .map_err(cose_error)
             .context("Given CBOR does not appear to be a COSE_sign1")?;
-        let payload = payload_from_sign1(public_key, expected_issuer, &entry)
-            .context("Unable to extract payload from COSE_sign1")?;
-        Ok(payload)
+        let algorithm = Algorithm::Assigned(key.iana_algorithm());
+        check_protected_header(&algorithm, &sign1.protected.header)
+            .context("Validation of bcc entry protected header failed.")?;
+        sign1
+            .verify_signature(b"", |s, m| key.verify(s, m))
+            .context("public key cannot verify cose_sign1 cert")?;
+        match sign1.payload {
+            None => bail!("Missing payload"),
+            Some(payload) => Ok(Self { payload }),
+        }
+    }
+
+    pub(super) fn payload(&self) -> &[u8] {
+        &self.payload
     }
 }
 
@@ -50,121 +61,131 @@
     Ok(())
 }
 
-fn payload_from_sign1(
-    pkey: Option<&PublicKey>,
-    expected_issuer: Option<&str>,
-    sign1: &CoseSign1,
-) -> Result<Payload> {
-    if let Some(pkey) = pkey {
-        check_protected_header(
-            &Algorithm::Assigned(pkey.iana_algorithm()),
-            &sign1.protected.header,
-        )
-        .context("Validation of bcc entry protected header failed.")?;
-        sign1
-            .verify_signature(b"", |s, m| pkey.verify(s, m))
-            .context("public key cannot verify cose_sign1 cert")?;
+impl Payload {
+    pub(super) fn from_cbor(bytes: &[u8]) -> Result<Self> {
+        let f = PayloadFields::from_cbor(bytes)?;
+        PayloadBuilder::with_subject_public_key(f.subject_public_key)
+            .issuer(f.issuer)
+            .subject(f.subject)
+            .mode(f.mode.ok_or_else(|| anyhow!("mode required"))?)
+            .code_desc(f.code_desc)
+            .code_hash(f.code_hash.ok_or_else(|| anyhow!("code hash required"))?)
+            .config_desc(f.config_desc.ok_or_else(|| anyhow!("config desc required"))?)
+            .config_hash(f.config_hash)
+            .authority_desc(f.authority_desc)
+            .authority_hash(f.authority_hash.ok_or_else(|| anyhow!("authority hash required"))?)
+            .build()
+            .context("building payload")
     }
-
-    let bytes = sign1.payload.as_ref().ok_or_else(|| anyhow!("no payload"))?;
-    let payload = payload_from_slice(bytes.as_slice())?;
-    if let Some(expected_issuer) = expected_issuer {
-        ensure!(
-            payload.issuer() == expected_issuer,
-            "COSE_sign1's issuer ({}) does not match the subject of the previous payload in \
-            the chain ({}).",
-            payload.issuer(),
-            expected_issuer
-        );
-    }
-    Ok(payload)
 }
 
-fn payload_from_slice(bytes: &[u8]) -> Result<Payload> {
-    let entries = cbor_map_from_slice(bytes)?;
+struct PayloadFields {
+    issuer: String,
+    subject: String,
+    subject_public_key: PublicKey,
+    mode: Option<DiceMode>,
+    code_desc: Option<Vec<u8>>,
+    code_hash: Option<Vec<u8>>,
+    config_desc: Option<ConfigDesc>,
+    config_hash: Option<Vec<u8>>,
+    authority_desc: Option<Vec<u8>>,
+    authority_hash: Option<Vec<u8>>,
+}
 
-    let mut issuer = FieldValue::new("issuer");
-    let mut subject = FieldValue::new("subject");
-    let mut subject_public_key = FieldValue::new("subject public key");
-    let mut mode = FieldValue::new("mode");
-    let mut code_desc = FieldValue::new("code desc");
-    let mut code_hash = FieldValue::new("code hash");
-    let mut config_desc = FieldValue::new("config desc");
-    let mut config_hash = FieldValue::new("config hash");
-    let mut authority_desc = FieldValue::new("authority desc");
-    let mut authority_hash = FieldValue::new("authority hash");
-    let mut key_usage = FieldValue::new("key usage");
+impl PayloadFields {
+    fn from_cbor(bytes: &[u8]) -> Result<Self> {
+        let mut issuer = FieldValue::new("issuer");
+        let mut subject = FieldValue::new("subject");
+        let mut subject_public_key = FieldValue::new("subject public key");
+        let mut mode = FieldValue::new("mode");
+        let mut code_desc = FieldValue::new("code desc");
+        let mut code_hash = FieldValue::new("code hash");
+        let mut config_desc = FieldValue::new("config desc");
+        let mut config_hash = FieldValue::new("config hash");
+        let mut authority_desc = FieldValue::new("authority desc");
+        let mut authority_hash = FieldValue::new("authority hash");
+        let mut key_usage = FieldValue::new("key usage");
 
-    for (key, value) in entries.into_iter() {
-        if let Some(Ok(key)) = key.as_integer().map(TryInto::try_into) {
-            let field = match key {
-                ISS => &mut issuer,
-                SUB => &mut subject,
-                SUBJECT_PUBLIC_KEY => &mut subject_public_key,
-                MODE => &mut mode,
-                CODE_DESC => &mut code_desc,
-                CODE_HASH => &mut code_hash,
-                CONFIG_DESC => &mut config_desc,
-                CONFIG_HASH => &mut config_hash,
-                AUTHORITY_DESC => &mut authority_desc,
-                AUTHORITY_HASH => &mut authority_hash,
-                KEY_USAGE => &mut key_usage,
-                _ => bail!("Unknown key {}", key),
-            };
-            field.set(value)?;
-        } else {
-            bail!("Invalid key: {:?}", key);
+        let entries = cbor_map_from_slice(bytes)?;
+        for (key, value) in entries.into_iter() {
+            if let Some(Ok(key)) = key.as_integer().map(TryInto::try_into) {
+                let field = match key {
+                    ISS => &mut issuer,
+                    SUB => &mut subject,
+                    SUBJECT_PUBLIC_KEY => &mut subject_public_key,
+                    MODE => &mut mode,
+                    CODE_DESC => &mut code_desc,
+                    CODE_HASH => &mut code_hash,
+                    CONFIG_DESC => &mut config_desc,
+                    CONFIG_HASH => &mut config_hash,
+                    AUTHORITY_DESC => &mut authority_desc,
+                    AUTHORITY_HASH => &mut authority_hash,
+                    KEY_USAGE => &mut key_usage,
+                    _ => bail!("Unknown key {}", key),
+                };
+                field.set(value)?;
+            } else {
+                bail!("Invalid key: {:?}", key);
+            }
         }
+
+        validate_key_usage(key_usage)?;
+
+        Ok(Self {
+            issuer: issuer.into_string().context("issuer")?,
+            subject: subject.into_string().context("subject")?,
+            subject_public_key: validate_subject_public_key(subject_public_key)?,
+            mode: validate_mode(mode).context("mode")?,
+            code_desc: code_desc.into_optional_bytes().context("code descriptor")?,
+            code_hash: code_hash.into_optional_bytes().context("code hash")?,
+            config_desc: validate_config_desc(config_desc).context("config descriptor")?,
+            config_hash: config_hash.into_optional_bytes().context("config hash")?,
+            authority_desc: authority_desc.into_optional_bytes().context("authority descriptor")?,
+            authority_hash: authority_hash.into_optional_bytes().context("authority hash")?,
+        })
     }
+}
 
-    let issuer = issuer.into_string().context("Issuer must be a string")?;
-    let subject = subject.into_string().context("Subject must be a string")?;
-    let subject_public_key =
-        subject_public_key.into_bytes().context("Subject public key must be bytes")?;
-    let mode = mode.into_bytes().context("Mode must be bytes")?;
-    let code_desc = code_desc.into_optional_bytes().context("Code descriptor must be bytes")?;
-    let code_hash = code_hash.into_bytes().context("Code hash must be bytes")?;
-    let config_desc = config_desc.into_bytes().context("Config descriptor must be bytes")?;
-    let config_hash = config_hash.into_optional_bytes().context("Config hash must be bytes")?;
-    let authority_desc =
-        authority_desc.into_optional_bytes().context("Authority descriptor must be bytes")?;
-    let authority_hash = authority_hash.into_bytes().context("Authority hash must be bytes")?;
-    let key_usage = key_usage.into_bytes().context("Key usage must be bytes")?;
+fn validate_key_usage(key_usage: FieldValue) -> Result<()> {
+    let key_usage = key_usage.into_bytes().context("key usage")?;
+    if key_usage.len() != 1 || key_usage[0] != 0x20 {
+        bail!("key usage must be keyCertSign");
+    };
+    Ok(())
+}
 
+fn validate_subject_public_key(subject_public_key: FieldValue) -> Result<PublicKey> {
+    let subject_public_key = subject_public_key.into_bytes().context("Subject public")?;
     let subject_public_key = CoseKey::from_slice(&subject_public_key)
         .map_err(cose_error)
-        .context("Error parsing subject public key from bytes")?;
-    let subject_public_key = PublicKey::from_cose_key(&subject_public_key)
-        .context("Error parsing subject public key from COSE_key")?;
-    if mode.len() != 1 {
-        bail!("Expected mode to be a single byte, actual byte count: {}", mode.len())
-    };
-    let mode = match mode[0] {
-        1 => DiceMode::Normal,
-        2 => DiceMode::Debug,
-        3 => DiceMode::Recovery,
-        _ => DiceMode::NotConfigured,
-    };
+        .context("parsing subject public key from bytes")?;
+    PublicKey::from_cose_key(&subject_public_key)
+        .context("parsing subject public key from COSE_key")
+}
 
-    let config_desc = config_desc_from_slice(&config_desc)
-        .context("Error parsing config descriptor from bytes")?;
+fn validate_mode(mode: FieldValue) -> Result<Option<DiceMode>> {
+    let mode = mode.into_optional_bytes()?;
+    mode.map(|mode| {
+        if mode.len() != 1 {
+            bail!("Expected mode to be a single byte, actual byte count: {}", mode.len())
+        };
+        Ok(match mode[0] {
+            1 => DiceMode::Normal,
+            2 => DiceMode::Debug,
+            3 => DiceMode::Recovery,
+            _ => DiceMode::NotConfigured,
+        })
+    })
+    .transpose()
+}
 
-    if key_usage.len() != 1 || key_usage[0] != 0x20 {
-        bail!("key usage must be keyCertSign")
-    };
-
-    PayloadBuilder::with_subject_public_key(subject_public_key)
-        .issuer(issuer)
-        .subject(subject)
-        .mode(mode)
-        .code_desc(code_desc)
-        .code_hash(code_hash)
-        .config_desc(config_desc)
-        .config_hash(config_hash)
-        .authority_desc(authority_desc)
-        .authority_hash(authority_hash)
-        .build()
-        .context("building payload")
+fn validate_config_desc(config_desc: FieldValue) -> Result<Option<ConfigDesc>> {
+    let config_desc = config_desc.into_optional_bytes()?;
+    config_desc
+        .map(|config_desc| {
+            config_desc_from_slice(&config_desc).context("parsing config descriptor")
+        })
+        .transpose()
 }
 
 fn cbor_map_from_slice(bytes: &[u8]) -> Result<Vec<(Value, Value)>> {