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