blob: 99a6dc9d65630b3c34c0f042638ea7686379e1d6 [file] [log] [blame]
Janis Danisevskisc51dff82021-10-20 09:51:16 -07001// Copyright 2021, The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! A resident dice node keeps CDI_attest and CDI_seal memory resident and can serve
16//! its clients directly by performing all crypto operations including derivations and
17//! certificate generation itself.
18
19use crate::DiceNodeImpl;
20use android_hardware_security_dice::aidl::android::hardware::security::dice::{
21 Bcc::Bcc, BccHandover::BccHandover, InputValues::InputValues as BinderInputValues,
22 Signature::Signature,
23};
24use anyhow::{Context, Result};
25use dice::{ContextImpl, OpenDiceCborContext};
26use diced_open_dice_cbor as dice;
27use diced_utils::{self as utils, InputValues, ResidentArtifacts};
28use std::collections::HashMap;
29use std::convert::TryInto;
30use std::sync::RwLock;
31
32/// The ResidentNode implements a IDiceNode backend with memory resident DICE secrets.
33pub struct ResidentNode {
34 artifacts: RwLock<ResidentArtifacts>,
35 demotion_db: RwLock<HashMap<BinderInputValues, Vec<BinderInputValues>>>,
36}
37
38impl ResidentNode {
39 /// Creates a new Resident node with the given dice secrets and certificate chain.
40 pub fn new(
41 cdi_attest: &[u8; dice::CDI_SIZE],
42 cdi_seal: &[u8; dice::CDI_SIZE],
43 bcc: Vec<u8>,
44 ) -> Result<Self> {
45 Ok(ResidentNode {
46 artifacts: RwLock::new(
47 ResidentArtifacts::new(cdi_attest, cdi_seal, &bcc)
48 .context("In ResidentNode::new: Trying to initialize ResidentArtifacts")?,
49 ),
50 demotion_db: Default::default(),
51 })
52 }
53
54 fn get_effective_artifacts(
55 &self,
56 client: BinderInputValues,
57 input_values: &[BinderInputValues],
58 ) -> Result<ResidentArtifacts> {
59 let artifacts = self.artifacts.read().unwrap().try_clone()?;
60 let demotion_db = self.demotion_db.read().unwrap();
61
62 let client_arr = [client];
63
64 let input_values: Vec<utils::InputValues> = demotion_db
65 .get(&client_arr[0])
66 .map(|v| v.iter())
67 .unwrap_or_else(|| client_arr.iter())
68 .chain(input_values.iter())
Janis Danisevskisd9513f42021-12-16 17:15:13 -080069 .map(|v| v.into())
70 .collect();
Janis Danisevskisc51dff82021-10-20 09:51:16 -070071
72 artifacts
73 .execute_steps(input_values.iter().map(|v| v as &dyn dice::InputValues))
74 .context("In get_effective_artifacts:")
75 }
76}
77
78impl DiceNodeImpl for ResidentNode {
79 fn sign(
80 &self,
81 client: BinderInputValues,
82 input_values: &[BinderInputValues],
83 message: &[u8],
84 ) -> Result<Signature> {
85 let (cdi_attest, _, _) = self
86 .get_effective_artifacts(client, input_values)
87 .context("In ResidentNode::sign: Failed to get effective_artifacts.")?
88 .into_tuple();
89 let mut dice = OpenDiceCborContext::new();
90 let seed = dice
91 .derive_cdi_private_key_seed(cdi_attest[..].try_into().with_context(|| {
92 format!(
93 "In ResidentNode::sign: Failed to convert cdi_attest (length: {}).",
94 cdi_attest.len()
95 )
96 })?)
97 .context("In ResidentNode::sign: Failed to derive seed from cdi_attest.")?;
98 let (_public_key, private_key) = dice
99 .keypair_from_seed(seed[..].try_into().with_context(|| {
100 format!("In ResidentNode::sign: Failed to convert seed (length: {}).", seed.len())
101 })?)
102 .context("In ResidentNode::sign: Failed to derive keypair from seed.")?;
103 Ok(Signature {
104 data: dice
105 .sign(
106 message,
107 private_key[..].try_into().with_context(|| {
108 format!(
109 "In ResidentNode::sign: Failed to convert private_key (length: {}).",
110 private_key.len()
111 )
112 })?,
113 )
114 .context("In ResidentNode::sign: Failed to sign.")?,
115 })
116 }
117
118 fn get_attestation_chain(
119 &self,
120 client: BinderInputValues,
121 input_values: &[BinderInputValues],
122 ) -> Result<Bcc> {
123 let (_, _, bcc) = self
124 .get_effective_artifacts(client, input_values)
125 .context("In ResidentNode::get_attestation_chain: Failed to get effective_artifacts.")?
126 .into_tuple();
127
128 Ok(Bcc { data: bcc })
129 }
130
131 fn derive(
132 &self,
133 client: BinderInputValues,
134 input_values: &[BinderInputValues],
135 ) -> Result<BccHandover> {
136 let (cdi_attest, cdi_seal, bcc) =
137 self.get_effective_artifacts(client, input_values)?.into_tuple();
138
139 utils::make_bcc_handover(
140 &cdi_attest[..]
141 .try_into()
142 .context("In ResidentNode::derive: Trying to convert cdi_attest to sized array.")?,
143 &cdi_seal[..]
144 .try_into()
145 .context("In ResidentNode::derive: Trying to convert cdi_attest to sized array.")?,
146 &bcc,
147 )
148 .context("In ResidentNode::derive: Trying to format bcc handover.")
149 }
150
151 fn demote(&self, client: BinderInputValues, input_values: &[BinderInputValues]) -> Result<()> {
152 let mut demotion_db = self.demotion_db.write().unwrap();
153
154 let client_arr = [client];
155
156 // The following statement consults demotion database which yields an optional demotion
157 // path. It then constructs an iterator over the following elements, then clones and
158 // collects them into a new vector:
159 // [ demotion path | client ], input_values
160 let new_path: Vec<BinderInputValues> = demotion_db
161 .get(&client_arr[0])
162 .map(|v| v.iter())
163 .unwrap_or_else(|| client_arr.iter())
164 .chain(input_values)
165 .cloned()
166 .collect();
167
168 let [client] = client_arr;
169 demotion_db.insert(client, new_path);
170 Ok(())
171 }
172
173 fn demote_self(&self, input_values: &[BinderInputValues]) -> Result<()> {
174 let mut artifacts = self.artifacts.write().unwrap();
175
176 let input_values = input_values
177 .iter()
178 .map(|v| {
179 v.try_into().with_context(|| format!("Failed to convert input values: {:#?}", v))
180 })
181 .collect::<Result<Vec<InputValues>>>()
182 .context("In ResidentNode::demote_self:")?;
183
184 *artifacts = artifacts
185 .try_clone()
186 .context("In ResidentNode::demote_self: Failed to clone resident artifacts")?
187 .execute_steps(input_values.iter().map(|v| v as &dyn dice::InputValues))
188 .context("In ResidentNode::demote_self:")?;
189 Ok(())
190 }
191}