• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 //! Support for explicit key chain format & conversion from legacy DiceArtifacts.
18 
19 use ciborium::Value;
20 use coset::{AsCborValue, CborOrdering, CborSerializable, CoseKey};
21 use diced_open_dice::{DiceArtifacts, OwnedDiceArtifacts, CDI_SIZE};
22 use std::fmt;
23 
24 const EXPLICIT_KEY_DICE_CERT_CHAIN_VERSION: u64 = 1;
25 
26 /// Error type thrown in OwnedDiceArtifactsWithExplicitKey struct
27 #[derive(Debug)]
28 pub enum Error {
29     /// Errors originating in the coset library.
30     CoseError(coset::CoseError),
31     /// Unexpected item encountered (got, want).
32     UnexpectedItem(&'static str, &'static str),
33 }
34 
35 impl std::error::Error for Error {}
36 
37 impl std::fmt::Display for Error {
fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result38     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
39         match self {
40             Self::CoseError(e) => write!(f, "Errors originating in the coset library {e:?}"),
41             Self::UnexpectedItem(got, want) => {
42                 write!(f, "Unexpected item - Got:{got}, Expected:{want}")
43             }
44         }
45     }
46 }
47 
48 impl From<coset::CoseError> for Error {
from(e: coset::CoseError) -> Self49     fn from(e: coset::CoseError) -> Self {
50         Self::CoseError(e)
51     }
52 }
53 
54 /// An OwnedDiceArtifactsWithExplicitKey is an OwnedDiceArtifacts that also exposes its
55 /// DICE chain (BCC) in explicit key format.
56 pub struct OwnedDiceArtifactsWithExplicitKey {
57     artifacts: OwnedDiceArtifacts,
58     explicit_key_dice_chain: Option<Vec<u8>>,
59 }
60 
61 impl OwnedDiceArtifactsWithExplicitKey {
62     /// Create an OwnedDiceArtifactsWithExplicitKey from OwnedDiceArtifacts.
from_owned_artifacts(artifacts: OwnedDiceArtifacts) -> Result<Self, Error>63     pub fn from_owned_artifacts(artifacts: OwnedDiceArtifacts) -> Result<Self, Error> {
64         let explicit_key_dice_chain = artifacts.bcc().map(to_explicit_chain).transpose()?;
65         Ok(Self { artifacts, explicit_key_dice_chain })
66     }
67 
68     /// Get the dice chain in Explicit-key DiceCertChain format.
69     ///
70     /// ExplicitKeyDiceCertChain = [
71     ///     1, ; version,
72     ///     DiceCertChainInitialPayload,
73     ///     * DiceChainEntry
74     /// ]
75     /// ; Encoded in accordance with Core Deterministic Encoding Requirements [RFC 8949 s4.2.1]
76     /// DiceCertChainInitialPayload = bstr .cbor PubKeyEd25519 /
77     ///     bstr .cbor PubKeyECDSA256 /
78     ///     bstr .cbor PubKeyECDSA384 ; subjectPublicKey
79     /// Note: Extracted from the spec at ExplicitKeyDiceCertChain.cddl. Keep in sync!
explicit_key_dice_chain(&self) -> Option<&[u8]>80     pub fn explicit_key_dice_chain(&self) -> Option<&[u8]> {
81         self.explicit_key_dice_chain.as_deref()
82     }
83 }
84 
85 impl fmt::Debug for OwnedDiceArtifactsWithExplicitKey {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result86     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87         write!(f, "Sensitive information omitted")
88     }
89 }
90 
91 impl DiceArtifacts for OwnedDiceArtifactsWithExplicitKey {
cdi_attest(&self) -> &[u8; CDI_SIZE]92     fn cdi_attest(&self) -> &[u8; CDI_SIZE] {
93         self.artifacts.cdi_attest()
94     }
95 
cdi_seal(&self) -> &[u8; CDI_SIZE]96     fn cdi_seal(&self) -> &[u8; CDI_SIZE] {
97         self.artifacts.cdi_seal()
98     }
99 
bcc(&self) -> Option<&[u8]>100     fn bcc(&self) -> Option<&[u8]> {
101         self.artifacts.bcc()
102     }
103 }
104 
105 /// Convert a DICE chain to explicit key format. Note that this method checks if the input is
106 /// already in the Explicit-key format & returns it if so. The check is lightweight though.
107 /// A twisted incorrect dice chain input may produce incorrect output.
to_explicit_chain(dice_chain_bytes: &[u8]) -> Result<Vec<u8>, Error>108 pub fn to_explicit_chain(dice_chain_bytes: &[u8]) -> Result<Vec<u8>, Error> {
109     let dice_chain = deserialize_cbor_array(dice_chain_bytes)?;
110     // Check if the dice_chain is already in explicit key format
111     if matches!(&&dice_chain[..], [Value::Integer(_version), Value::Bytes(_public_key), ..]) {
112         return Ok(dice_chain_bytes.to_vec());
113     }
114     let mut res: Vec<Value> = Vec::with_capacity(dice_chain.len() + 1);
115     let mut it = dice_chain.into_iter();
116     res.push(Value::from(EXPLICIT_KEY_DICE_CERT_CHAIN_VERSION));
117     let root_key = it
118         .next()
119         .ok_or(Error::UnexpectedItem("Empty dice chain", "dice_chain with at least 1 node"))?;
120 
121     // Canonicalize the root public key as per Core Deterministic Encoding Requirements
122     let mut root_key = CoseKey::from_cbor_value(root_key)?;
123     root_key.canonicalize(CborOrdering::Lexicographic);
124     // Converts to .bstr .cbor COSE_KEY
125     let root_key = root_key.to_vec()?;
126     res.push(Value::Bytes(root_key));
127     res.extend(it);
128     Ok(Value::Array(res).to_vec()?)
129 }
130 
deserialize_cbor_array(cbor_array: &[u8]) -> Result<Vec<Value>, Error>131 fn deserialize_cbor_array(cbor_array: &[u8]) -> Result<Vec<Value>, Error> {
132     let value = Value::from_slice(cbor_array)?;
133     value.into_array().map_err(|_| Error::UnexpectedItem("-", "Array"))
134 }
135 
136 #[cfg(test)]
137 rdroidtest::test_main!();
138 
139 #[cfg(test)]
140 mod tests {
141     use super::*;
142     use rdroidtest::rdroidtest;
143 
144     #[rdroidtest]
implicit_to_explicit_dice_chain_conversion()145     fn implicit_to_explicit_dice_chain_conversion() {
146         const COMPOS_CHAIN_SIZE_LEGACY: usize = 5;
147         let implicit_dice = include_bytes!("../testdata/compos_chain_legacy");
148         let explicit_chain = to_explicit_chain(implicit_dice).unwrap();
149         let chain = Value::from_slice(&explicit_chain).unwrap();
150         let chain_arr = chain.into_array().unwrap();
151         // There is an added node for version in the explicit key format
152         assert_eq!(chain_arr.len(), COMPOS_CHAIN_SIZE_LEGACY + 1);
153         assert_eq!(chain_arr[0].as_integer().unwrap(), EXPLICIT_KEY_DICE_CERT_CHAIN_VERSION.into());
154         assert!(chain_arr[1].is_bytes());
155     }
156 
157     #[rdroidtest]
explicit_to_explicit_dice_chain_conversion()158     fn explicit_to_explicit_dice_chain_conversion() {
159         let implicit_dice = include_bytes!("../testdata/compos_chain_legacy");
160         let explicit_chain = to_explicit_chain(implicit_dice).unwrap();
161         let converted_chain = to_explicit_chain(&explicit_chain).unwrap();
162         assert_eq!(explicit_chain, converted_chain);
163     }
164 
165     #[rdroidtest]
root_key_deterministic_encoding()166     fn root_key_deterministic_encoding() {
167         // Encoding of
168         // [{"a": 1, 3: -7, 1234: 1, 1: 1}]
169         // Notice the root key map are not ordered (in lexicographic order)!
170         const IMPLICIT_CHAIN: &str = "81A461610103261904D2010101";
171         // Encoding of
172         // [1, h'A4010103261904D201616101']
173         // where A4010103261904D201616101 =
174         //      .cbor {1: 1, 3: -7, 1234: 1, "a": 1}
175         // Notice the root key labels are in lexicographic order!
176         const EXPECTED_EXPLICIT_CHAIN: &str = "82014ca4010103261904d201616101";
177 
178         let explicit_chain = to_explicit_chain(&hex::decode(IMPLICIT_CHAIN).unwrap()).unwrap();
179         assert_eq!(hex::encode(explicit_chain), EXPECTED_EXPLICIT_CHAIN);
180     }
181 }
182