Diced: Add service diced an implementation of android.security.dice

Bug: 198197213
Test: diced_test
Change-Id: I7075c2e7ac8e48a13f4eb177f2e989ff1e6695a2
diff --git a/diced/src/utils.rs b/diced/src/utils.rs
new file mode 100644
index 0000000..abb8a7b
--- /dev/null
+++ b/diced/src/utils.rs
@@ -0,0 +1,445 @@
+// Copyright 2021, 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.
+
+//! Implements utility functions and types for diced and the dice HAL.
+
+use android_hardware_security_dice::aidl::android::hardware::security::dice::{
+    Bcc::Bcc, BccHandover::BccHandover, InputValues::InputValues as BinderInputValues,
+    Mode::Mode as BinderMode,
+};
+use anyhow::{anyhow, Context, Result};
+use dice::ContextImpl;
+use diced_open_dice_cbor as dice;
+use keystore2_crypto::ZVec;
+use std::convert::{TryFrom, TryInto};
+
+/// This new type wraps a reference to BinderInputValues and implements the open dice
+/// InputValues trait.
+#[derive(Debug)]
+pub struct InputValues<'a>(&'a BinderInputValues);
+
+impl<'a> TryFrom<&'a BinderInputValues> for InputValues<'a> {
+    type Error = anyhow::Error;
+
+    fn try_from(input_values: &'a BinderInputValues) -> Result<InputValues<'a>> {
+        if input_values.codeHash.len() != dice::HASH_SIZE {
+            return Err(anyhow!(format!(
+                "In try_from: Code hash has invalid size: {}",
+                input_values.codeHash.len()
+            )));
+        }
+        if input_values.authorityHash.len() != dice::HASH_SIZE {
+            return Err(anyhow!(format!(
+                "In try_from: Authority hash has invalid size: {}",
+                input_values.authorityHash.len()
+            )));
+        }
+        if input_values.hidden.len() != dice::HIDDEN_SIZE {
+            return Err(anyhow!(format!(
+                "In try_from: Hidden has invalid size: {}",
+                input_values.hidden.len()
+            )));
+        }
+
+        Ok(Self(input_values))
+    }
+}
+
+impl From<&InputValues<'_>> for BinderInputValues {
+    fn from(input_values: &InputValues) -> BinderInputValues {
+        input_values.0.clone()
+    }
+}
+impl From<InputValues<'_>> for BinderInputValues {
+    fn from(input_values: InputValues) -> BinderInputValues {
+        input_values.0.clone()
+    }
+}
+
+impl dice::InputValues for InputValues<'_> {
+    fn code_hash(&self) -> &[u8; dice::HASH_SIZE] {
+        // If `self` was created using try_from the length was checked and this cannot panic.
+        self.0.codeHash.as_slice().try_into().unwrap()
+    }
+
+    fn config(&self) -> dice::Config {
+        dice::Config::Descriptor(self.0.config.desc.as_slice())
+    }
+
+    fn authority_hash(&self) -> &[u8; dice::HASH_SIZE] {
+        // If `self` was created using try_from the length was checked and this cannot panic.
+        self.0.authorityHash.as_slice().try_into().unwrap()
+    }
+
+    fn authority_descriptor(&self) -> Option<&[u8]> {
+        self.0.authorityDescriptor.as_deref()
+    }
+
+    fn mode(&self) -> dice::Mode {
+        match self.0.mode {
+            BinderMode::NOT_INITIALIZED => dice::Mode::NotConfigured,
+            BinderMode::NORMAL => dice::Mode::Normal,
+            BinderMode::DEBUG => dice::Mode::Debug,
+            BinderMode::RECOVERY => dice::Mode::Recovery,
+            _ => dice::Mode::NotConfigured,
+        }
+    }
+
+    fn hidden(&self) -> &[u8; dice::HIDDEN_SIZE] {
+        // If `self` was created using try_from the length was checked and this cannot panic.
+        self.0.hidden.as_slice().try_into().unwrap()
+    }
+}
+
+/// Initializes an aidl defined BccHandover object with the arguments `cdi_attest`, `cdi_seal`,
+/// and `bcc`.
+pub fn make_bcc_handover(
+    cdi_attest: &[u8; dice::CDI_SIZE],
+    cdi_seal: &[u8; dice::CDI_SIZE],
+    bcc: &[u8],
+) -> Result<BccHandover> {
+    Ok(BccHandover {
+        cdiAttest: cdi_attest.to_vec(),
+        cdiSeal: cdi_seal.to_vec(),
+        bcc: Bcc { data: bcc.to_vec() },
+    })
+}
+
+/// ResidentArtifacts stores a set of dice artifacts comprising CDI_ATTEST, CDI_SEAL,
+/// and the BCC formatted attestation certificate chain. The sensitive secrets are
+/// stored in zeroing vectors, and it implements functionality to perform DICE
+/// derivation steps using libopen-dice-cbor.
+pub struct ResidentArtifacts {
+    cdi_attest: ZVec,
+    cdi_seal: ZVec,
+    bcc: Vec<u8>,
+}
+
+impl ResidentArtifacts {
+    /// Create a ResidentArtifacts object. The parameters ensure that the stored secrets
+    /// can only have the appropriate size, so that subsequent casts to array references
+    /// cannot fail.
+    pub fn new(
+        cdi_attest: &[u8; dice::CDI_SIZE],
+        cdi_seal: &[u8; dice::CDI_SIZE],
+        bcc: &[u8],
+    ) -> Result<Self> {
+        Ok(ResidentArtifacts {
+            cdi_attest: cdi_attest[..]
+                .try_into()
+                .context("In ResidentArtifacts::new: Trying to convert cdi_attest to ZVec.")?,
+            cdi_seal: cdi_seal[..]
+                .try_into()
+                .context("In ResidentArtifacts::new: Trying to convert cdi_seal to ZVec.")?,
+            bcc: bcc.to_vec(),
+        })
+    }
+
+    /// Attempts to clone the artifacts. This operation is fallible due to the fallible
+    /// nature of ZVec.
+    pub fn try_clone(&self) -> Result<Self> {
+        Ok(ResidentArtifacts {
+            cdi_attest: self
+                .cdi_attest
+                .try_clone()
+                .context("In ResidentArtifacts::new: Trying to clone cdi_attest.")?,
+            cdi_seal: self
+                .cdi_seal
+                .try_clone()
+                .context("In ResidentArtifacts::new: Trying to clone cdi_seal.")?,
+            bcc: self.bcc.clone(),
+        })
+    }
+
+    /// Deconstruct the Artifacts into a tuple.
+    /// (CDI_ATTEST, CDI_SEAL, BCC)
+    pub fn into_tuple(self) -> (ZVec, ZVec, Vec<u8>) {
+        let ResidentArtifacts { cdi_attest, cdi_seal, bcc } = self;
+        (cdi_attest, cdi_seal, bcc)
+    }
+
+    fn execute_step(self, input_values: &dyn dice::InputValues) -> Result<Self> {
+        let ResidentArtifacts { cdi_attest, cdi_seal, bcc } = self;
+
+        let (cdi_attest, cdi_seal, bcc) = dice::OpenDiceCborContext::new()
+            .bcc_main_flow(
+                cdi_attest[..].try_into().with_context(|| {
+                    format!("Trying to convert cdi_attest. (length: {})", cdi_attest.len())
+                })?,
+                cdi_seal[..].try_into().with_context(|| {
+                    format!("Trying to convert cdi_seal. (length: {})", cdi_seal.len())
+                })?,
+                &bcc,
+                input_values,
+            )
+            .context("In ResidentArtifacts::execute_step:")?;
+        Ok(ResidentArtifacts { cdi_attest, cdi_seal, bcc })
+    }
+
+    /// Iterate through the iterator of dice input values performing one
+    /// BCC main flow step on each element.
+    pub fn execute_steps<'a, Iter>(self, input_values: Iter) -> Result<Self>
+    where
+        Iter: IntoIterator<Item = &'a dyn dice::InputValues>,
+    {
+        input_values
+            .into_iter()
+            .try_fold(self, |acc, input_values| acc.execute_step(input_values))
+            .context("In ResidentArtifacts::execute_step:")
+    }
+}
+
+/// This submodule implements a limited set of CBOR generation functionality. Essentially,
+/// a cbor header generator and some convenience functions for number and BSTR encoding.
+pub mod cbor {
+    use anyhow::{anyhow, Context, Result};
+    use std::convert::TryInto;
+    use std::io::Write;
+
+    /// CBOR encodes a positive number.
+    pub fn encode_number(n: u64, buffer: &mut dyn Write) -> Result<()> {
+        encode_header(0, n, buffer)
+    }
+
+    /// CBOR encodes a binary string.
+    pub fn encode_bstr(bstr: &[u8], buffer: &mut dyn Write) -> Result<()> {
+        encode_header(
+            2,
+            bstr.len().try_into().context("In encode_bstr: Failed to convert usize to u64.")?,
+            buffer,
+        )
+        .context("In encode_bstr: While writing header.")?;
+        let written = buffer.write(bstr).context("In encode_bstr: While writing payload.")?;
+        if written != bstr.len() {
+            return Err(anyhow!("In encode_bstr: Buffer too small. ({}, {})", written, bstr.len()));
+        }
+        Ok(())
+    }
+
+    /// Formats a CBOR header. `t` is the type, and n is the header argument.
+    pub fn encode_header(t: u8, n: u64, buffer: &mut dyn Write) -> Result<()> {
+        match n {
+            n if n < 24 => {
+                let written = buffer
+                    .write(&u8::to_be_bytes(((t as u8) << 5) | (n as u8 & 0x1F)))
+                    .with_context(|| {
+                    format!("In encode_header: Failed to write header ({}, {})", t, n)
+                })?;
+                if written != 1 {
+                    return Err(anyhow!("In encode_header: Buffer to small. ({}, {})", t, n));
+                }
+            }
+            n if n <= 0xFF => {
+                let written =
+                    buffer.write(&u8::to_be_bytes(((t as u8) << 5) | (24u8 & 0x1F))).with_context(
+                        || format!("In encode_header: Failed to write header ({}, {})", t, n),
+                    )?;
+                if written != 1 {
+                    return Err(anyhow!("In encode_header: Buffer to small. ({}, {})", t, n));
+                }
+                let written = buffer.write(&u8::to_be_bytes(n as u8)).with_context(|| {
+                    format!("In encode_header: Failed to write size ({}, {})", t, n)
+                })?;
+                if written != 1 {
+                    return Err(anyhow!(
+                        "In encode_header while writing size: Buffer to small. ({}, {})",
+                        t,
+                        n
+                    ));
+                }
+            }
+            n if n <= 0xFFFF => {
+                let written =
+                    buffer.write(&u8::to_be_bytes(((t as u8) << 5) | (25u8 & 0x1F))).with_context(
+                        || format!("In encode_header: Failed to write header ({}, {})", t, n),
+                    )?;
+                if written != 1 {
+                    return Err(anyhow!("In encode_header: Buffer to small. ({}, {})", t, n));
+                }
+                let written = buffer.write(&u16::to_be_bytes(n as u16)).with_context(|| {
+                    format!("In encode_header: Failed to write size ({}, {})", t, n)
+                })?;
+                if written != 2 {
+                    return Err(anyhow!(
+                        "In encode_header while writing size: Buffer to small. ({}, {})",
+                        t,
+                        n
+                    ));
+                }
+            }
+            n if n <= 0xFFFFFFFF => {
+                let written =
+                    buffer.write(&u8::to_be_bytes(((t as u8) << 5) | (26u8 & 0x1F))).with_context(
+                        || format!("In encode_header: Failed to write header ({}, {})", t, n),
+                    )?;
+                if written != 1 {
+                    return Err(anyhow!("In encode_header: Buffer to small. ({}, {})", t, n));
+                }
+                let written = buffer.write(&u32::to_be_bytes(n as u32)).with_context(|| {
+                    format!("In encode_header: Failed to write size ({}, {})", t, n)
+                })?;
+                if written != 4 {
+                    return Err(anyhow!(
+                        "In encode_header while writing size: Buffer to small. ({}, {})",
+                        t,
+                        n
+                    ));
+                }
+            }
+            n => {
+                let written =
+                    buffer.write(&u8::to_be_bytes(((t as u8) << 5) | (27u8 & 0x1F))).with_context(
+                        || format!("In encode_header: Failed to write header ({}, {})", t, n),
+                    )?;
+                if written != 1 {
+                    return Err(anyhow!("In encode_header: Buffer to small. ({}, {})", t, n));
+                }
+                let written = buffer.write(&u64::to_be_bytes(n as u64)).with_context(|| {
+                    format!("In encode_header: Failed to write size ({}, {})", t, n)
+                })?;
+                if written != 8 {
+                    return Err(anyhow!(
+                        "In encode_header while writing size: Buffer to small. ({}, {})",
+                        t,
+                        n
+                    ));
+                }
+            }
+        }
+        Ok(())
+    }
+
+    #[cfg(test)]
+    mod test {
+        use super::*;
+
+        fn encode_header_helper(t: u8, n: u64) -> Vec<u8> {
+            let mut b: Vec<u8> = vec![];
+            encode_header(t, n, &mut b).unwrap();
+            b
+        }
+
+        #[test]
+        fn encode_header_test() {
+            assert_eq!(&encode_header_helper(0, 0), &[0b000_00000]);
+            assert_eq!(&encode_header_helper(0, 23), &[0b000_10111]);
+            assert_eq!(&encode_header_helper(0, 24), &[0b000_11000, 24]);
+            assert_eq!(&encode_header_helper(0, 0xff), &[0b000_11000, 0xff]);
+            assert_eq!(&encode_header_helper(0, 0x100), &[0b000_11001, 0x01, 0x00]);
+            assert_eq!(&encode_header_helper(0, 0xffff), &[0b000_11001, 0xff, 0xff]);
+            assert_eq!(&encode_header_helper(0, 0x10000), &[0b000_11010, 0x00, 0x01, 0x00, 0x00]);
+            assert_eq!(
+                &encode_header_helper(0, 0xffffffff),
+                &[0b000_11010, 0xff, 0xff, 0xff, 0xff]
+            );
+            assert_eq!(
+                &encode_header_helper(0, 0x100000000),
+                &[0b000_11011, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00]
+            );
+            assert_eq!(
+                &encode_header_helper(0, 0xffffffffffffffff),
+                &[0b000_11011, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]
+            );
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use android_hardware_security_dice::aidl::android::hardware::security::dice::{
+        Config::Config as BinderConfig, InputValues::InputValues as BinderInputValues,
+    };
+    use dice::InputValues as DiceInputValues;
+    use diced_open_dice_cbor as dice;
+
+    static CODE_HASH_TEST_VECTOR: [u8; dice::HASH_SIZE] = [1u8; dice::HASH_SIZE];
+    static CONFIG_DESCRIPTOR_TEST_VECTOR: &[u8] = &[3, 2, 1];
+    static AUTHORITY_HASH_TEST_VECTOR: [u8; dice::HASH_SIZE] = [3u8; dice::HASH_SIZE];
+    static AUTHORITY_DESCRIPTOR_TEST_VECTOR: &[u8] = &[1, 2, 3];
+    static HIDDEN_TEST_VECTOR: [u8; dice::HIDDEN_SIZE] = [4u8; dice::HIDDEN_SIZE];
+
+    #[test]
+    fn try_from_input_values_binder() {
+        let input_values_good = BinderInputValues {
+            codeHash: CODE_HASH_TEST_VECTOR.to_vec(),
+            config: BinderConfig { desc: CONFIG_DESCRIPTOR_TEST_VECTOR.to_vec() },
+            authorityHash: AUTHORITY_HASH_TEST_VECTOR.to_vec(),
+            authorityDescriptor: Some(AUTHORITY_DESCRIPTOR_TEST_VECTOR.to_vec()),
+            mode: BinderMode::NORMAL,
+            hidden: HIDDEN_TEST_VECTOR.to_vec(),
+        };
+
+        let converted_input_values: InputValues =
+            (&input_values_good).try_into().expect("This should succeed.");
+        assert_eq!(*converted_input_values.code_hash(), CODE_HASH_TEST_VECTOR);
+        assert_eq!(
+            converted_input_values.config(),
+            dice::Config::Descriptor(CONFIG_DESCRIPTOR_TEST_VECTOR)
+        );
+        assert_eq!(*converted_input_values.authority_hash(), AUTHORITY_HASH_TEST_VECTOR);
+        assert_eq!(
+            converted_input_values.authority_descriptor(),
+            Some(AUTHORITY_DESCRIPTOR_TEST_VECTOR)
+        );
+        assert_eq!(converted_input_values.mode(), dice::Mode::Normal);
+        assert_eq!(*converted_input_values.hidden(), HIDDEN_TEST_VECTOR);
+
+        // One more time without authority descriptor.
+        let input_values_still_good_without_authority_descriptor =
+            BinderInputValues { authorityDescriptor: None, ..input_values_good.clone() };
+
+        let converted_input_values: InputValues =
+            (&input_values_still_good_without_authority_descriptor)
+                .try_into()
+                .expect("This should succeed.");
+        assert_eq!(*converted_input_values.code_hash(), CODE_HASH_TEST_VECTOR);
+        assert_eq!(
+            converted_input_values.config(),
+            dice::Config::Descriptor(CONFIG_DESCRIPTOR_TEST_VECTOR)
+        );
+        assert_eq!(*converted_input_values.authority_hash(), AUTHORITY_HASH_TEST_VECTOR);
+        assert_eq!(converted_input_values.authority_descriptor(), None);
+        assert_eq!(converted_input_values.mode(), dice::Mode::Normal);
+        assert_eq!(*converted_input_values.hidden(), HIDDEN_TEST_VECTOR);
+
+        // Now check the failure cases.
+        // Wrong sized codeHash.
+        let input_values_bad_code_hash = BinderInputValues {
+            codeHash: vec![1u8; dice::HASH_SIZE + 1],
+            ..input_values_good.clone()
+        };
+
+        InputValues::try_from(&input_values_bad_code_hash)
+            .expect_err("Conversion of input values with wrong sized code hash succeeded.");
+
+        // Wrong sized authority hash.
+        let input_values_bad_authority_hash = BinderInputValues {
+            authorityHash: vec![1u8; dice::HASH_SIZE + 1],
+            ..input_values_good.clone()
+        };
+
+        InputValues::try_from(&input_values_bad_authority_hash)
+            .expect_err("Conversion of input values with wrong sized authority hash succeeded.");
+
+        // Wrong sized hidden.
+        let input_values_bad_hidden = BinderInputValues {
+            authorityHash: vec![1u8; dice::HASH_SIZE + 1],
+            ..input_values_good.clone()
+        };
+
+        InputValues::try_from(&input_values_bad_hidden)
+            .expect_err("Conversion of input values with wrong sized hidden succeeded.");
+    }
+}