Alice Wang | d3a9640 | 2023-11-24 15:37:39 +0000 | [diff] [blame] | 1 | // Copyright 2023, 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 | //! This module contains functions related to DICE. |
| 16 | |
Alice Wang | 1cc1350 | 2023-12-05 11:05:34 +0000 | [diff] [blame] | 17 | use alloc::string::String; |
Alice Wang | d3a9640 | 2023-11-24 15:37:39 +0000 | [diff] [blame] | 18 | use alloc::vec::Vec; |
Alice Wang | 3397b36 | 2023-12-01 13:57:10 +0000 | [diff] [blame] | 19 | use bssl_avf::{ed25519_verify, Digester, EcKey}; |
Alice Wang | dd29c5d | 2023-12-07 09:56:23 +0000 | [diff] [blame] | 20 | use cbor_util::{ |
Alice Wang | 3397b36 | 2023-12-01 13:57:10 +0000 | [diff] [blame] | 21 | cbor_value_type, get_label_value, get_label_value_as_bytes, value_to_array, |
| 22 | value_to_byte_array, value_to_bytes, value_to_map, value_to_num, value_to_text, |
Alice Wang | dd29c5d | 2023-12-07 09:56:23 +0000 | [diff] [blame] | 23 | }; |
| 24 | use ciborium::value::Value; |
Alice Wang | d3a9640 | 2023-11-24 15:37:39 +0000 | [diff] [blame] | 25 | use core::cell::OnceCell; |
| 26 | use core::result; |
| 27 | use coset::{ |
Alice Wang | 3397b36 | 2023-12-01 13:57:10 +0000 | [diff] [blame] | 28 | self, |
| 29 | iana::{self, EnumI64}, |
| 30 | Algorithm, AsCborValue, CborSerializable, CoseError, CoseKey, CoseSign1, KeyOperation, KeyType, |
| 31 | Label, |
Alice Wang | d3a9640 | 2023-11-24 15:37:39 +0000 | [diff] [blame] | 32 | }; |
| 33 | use diced_open_dice::{DiceMode, HASH_SIZE}; |
Alice Wang | 9eebbab | 2024-04-10 14:57:27 +0000 | [diff] [blame] | 34 | use log::{debug, error, info}; |
Alice Wang | dd29c5d | 2023-12-07 09:56:23 +0000 | [diff] [blame] | 35 | use service_vm_comm::RequestProcessingError; |
Alice Wang | d3a9640 | 2023-11-24 15:37:39 +0000 | [diff] [blame] | 36 | |
| 37 | type Result<T> = result::Result<T, RequestProcessingError>; |
| 38 | |
| 39 | const CODE_HASH: i64 = -4670545; |
| 40 | const CONFIG_DESC: i64 = -4670548; |
| 41 | const AUTHORITY_HASH: i64 = -4670549; |
| 42 | const MODE: i64 = -4670551; |
| 43 | const SUBJECT_PUBLIC_KEY: i64 = -4670552; |
| 44 | |
Alice Wang | 1cc1350 | 2023-12-05 11:05:34 +0000 | [diff] [blame] | 45 | const CONFIG_DESC_COMPONENT_NAME: i64 = -70002; |
| 46 | const CONFIG_DESC_SUB_COMPONENTS: i64 = -71002; |
| 47 | |
| 48 | const SUB_COMPONENT_NAME: i64 = 1; |
| 49 | const SUB_COMPONENT_VERSION: i64 = 2; |
| 50 | const SUB_COMPONENT_CODE_HASH: i64 = 3; |
| 51 | const SUB_COMPONENT_AUTHORITY_HASH: i64 = 4; |
| 52 | |
Alice Wang | 9eebbab | 2024-04-10 14:57:27 +0000 | [diff] [blame] | 53 | const KERNEL_COMPONENT_NAME: &str = "vm_entry"; |
| 54 | const VENDOR_PARTITION_COMPONENT_NAME: &str = "Microdroid vendor"; |
Alice Wang | 1cc1350 | 2023-12-05 11:05:34 +0000 | [diff] [blame] | 55 | const MICRODROID_PAYLOAD_COMPONENT_NAME: &str = "Microdroid payload"; |
| 56 | |
Alice Wang | d3a9640 | 2023-11-24 15:37:39 +0000 | [diff] [blame] | 57 | /// Represents a partially decoded `DiceCertChain` from the client VM. |
| 58 | /// The whole chain is defined as following: |
| 59 | /// |
| 60 | /// DiceCertChain = [ |
| 61 | /// PubKeyEd25519 / PubKeyECDSA256 / PubKeyECDSA384, ; UDS_Pub |
| 62 | /// + DiceChainEntry, ; First CDI_Certificate -> Last CDI_Certificate |
| 63 | /// ] |
| 64 | #[derive(Debug, Clone)] |
| 65 | pub(crate) struct ClientVmDiceChain { |
Alice Wang | 3397b36 | 2023-12-01 13:57:10 +0000 | [diff] [blame] | 66 | payloads: Vec<DiceChainEntryPayload>, |
Alice Wang | 9eebbab | 2024-04-10 14:57:27 +0000 | [diff] [blame] | 67 | /// The index of the vendor partition entry in the DICE chain if it exists. |
| 68 | vendor_partition_index: Option<usize>, |
| 69 | /// The index of the kernel entry in the DICE chain. |
| 70 | kernel_index: usize, |
Alice Wang | d3a9640 | 2023-11-24 15:37:39 +0000 | [diff] [blame] | 71 | } |
| 72 | |
| 73 | impl ClientVmDiceChain { |
| 74 | /// Validates the signatures of the entries in the `client_vm_dice_chain` as following: |
| 75 | /// |
| 76 | /// - The first entry of the `client_vm_dice_chain` must be signed with the root public key. |
| 77 | /// - After the first entry, each entry of the `client_vm_dice_chain` must be signed with the |
| 78 | /// subject public key of the previous entry. |
| 79 | /// |
| 80 | /// Returns a partially decoded client VM's DICE chain if the verification succeeds. |
| 81 | pub(crate) fn validate_signatures_and_parse_dice_chain( |
| 82 | mut client_vm_dice_chain: Vec<Value>, |
Alice Wang | 9eebbab | 2024-04-10 14:57:27 +0000 | [diff] [blame] | 83 | service_vm_dice_chain_len: usize, |
Alice Wang | d3a9640 | 2023-11-24 15:37:39 +0000 | [diff] [blame] | 84 | ) -> Result<Self> { |
Alice Wang | 9eebbab | 2024-04-10 14:57:27 +0000 | [diff] [blame] | 85 | let has_vendor_partition = |
| 86 | vendor_partition_exists(client_vm_dice_chain.len(), service_vm_dice_chain_len)?; |
| 87 | |
Alice Wang | d3a9640 | 2023-11-24 15:37:39 +0000 | [diff] [blame] | 88 | let root_public_key = |
| 89 | CoseKey::from_cbor_value(client_vm_dice_chain.remove(0))?.try_into()?; |
| 90 | |
| 91 | let mut payloads = Vec::with_capacity(client_vm_dice_chain.len()); |
| 92 | let mut previous_public_key = &root_public_key; |
| 93 | for (i, value) in client_vm_dice_chain.into_iter().enumerate() { |
| 94 | let payload = DiceChainEntryPayload::validate_cose_signature_and_extract_payload( |
| 95 | value, |
| 96 | previous_public_key, |
| 97 | ) |
| 98 | .map_err(|e| { |
| 99 | error!("Failed to verify the DICE chain entry {}: {:?}", i, e); |
| 100 | e |
| 101 | })?; |
| 102 | payloads.push(payload); |
| 103 | previous_public_key = &payloads.last().unwrap().subject_public_key; |
| 104 | } |
Alice Wang | 9eebbab | 2024-04-10 14:57:27 +0000 | [diff] [blame] | 105 | |
| 106 | Self::build(payloads, has_vendor_partition) |
Alice Wang | 1cc1350 | 2023-12-05 11:05:34 +0000 | [diff] [blame] | 107 | } |
| 108 | |
Alice Wang | 9eebbab | 2024-04-10 14:57:27 +0000 | [diff] [blame] | 109 | fn build( |
| 110 | dice_entry_payloads: Vec<DiceChainEntryPayload>, |
| 111 | has_vendor_partition: bool, |
| 112 | ) -> Result<Self> { |
| 113 | let microdroid_payload_name = |
| 114 | &dice_entry_payloads[dice_entry_payloads.len() - 1].config_descriptor.component_name; |
Alice Wang | 1cc1350 | 2023-12-05 11:05:34 +0000 | [diff] [blame] | 115 | if MICRODROID_PAYLOAD_COMPONENT_NAME != microdroid_payload_name { |
| 116 | error!( |
| 117 | "The last entry in the client VM DICE chain must describe the Microdroid \ |
Alice Wang | 9eebbab | 2024-04-10 14:57:27 +0000 | [diff] [blame] | 118 | payload. Got '{}'", |
Alice Wang | 1cc1350 | 2023-12-05 11:05:34 +0000 | [diff] [blame] | 119 | microdroid_payload_name |
| 120 | ); |
| 121 | return Err(RequestProcessingError::InvalidDiceChain); |
| 122 | } |
Alice Wang | 9eebbab | 2024-04-10 14:57:27 +0000 | [diff] [blame] | 123 | |
| 124 | let (vendor_partition_index, kernel_index) = if has_vendor_partition { |
| 125 | let index = dice_entry_payloads.len() - 2; |
| 126 | let vendor_partition_name = |
| 127 | &dice_entry_payloads[index].config_descriptor.component_name; |
| 128 | if VENDOR_PARTITION_COMPONENT_NAME != vendor_partition_name { |
| 129 | error!( |
| 130 | "The vendor partition entry in the client VM DICE chain must describe the \ |
| 131 | vendor partition. Got '{}'", |
| 132 | vendor_partition_name, |
| 133 | ); |
| 134 | return Err(RequestProcessingError::InvalidDiceChain); |
| 135 | } |
| 136 | (Some(index), index - 1) |
| 137 | } else { |
| 138 | (None, dice_entry_payloads.len() - 2) |
| 139 | }; |
| 140 | |
| 141 | let kernel_name = &dice_entry_payloads[kernel_index].config_descriptor.component_name; |
| 142 | if KERNEL_COMPONENT_NAME != kernel_name { |
| 143 | error!( |
| 144 | "The microdroid kernel entry in the client VM DICE chain must describe the \ |
| 145 | Microdroid kernel. Got '{}'", |
| 146 | kernel_name, |
| 147 | ); |
| 148 | return Err(RequestProcessingError::InvalidDiceChain); |
| 149 | } |
| 150 | |
| 151 | debug!("All entries in the client VM DICE chain have correct component names"); |
| 152 | Ok(Self { payloads: dice_entry_payloads, vendor_partition_index, kernel_index }) |
Alice Wang | 1cc1350 | 2023-12-05 11:05:34 +0000 | [diff] [blame] | 153 | } |
| 154 | |
Alice Wang | 4e09310 | 2023-12-13 09:16:29 +0000 | [diff] [blame] | 155 | pub(crate) fn microdroid_kernel(&self) -> &DiceChainEntryPayload { |
Alice Wang | 9eebbab | 2024-04-10 14:57:27 +0000 | [diff] [blame] | 156 | &self.payloads[self.kernel_index] |
| 157 | } |
| 158 | |
| 159 | pub(crate) fn vendor_partition(&self) -> Option<&DiceChainEntryPayload> { |
| 160 | self.vendor_partition_index.map(|i| &self.payloads[i]) |
Alice Wang | 1cc1350 | 2023-12-05 11:05:34 +0000 | [diff] [blame] | 161 | } |
| 162 | |
Alice Wang | 3397b36 | 2023-12-01 13:57:10 +0000 | [diff] [blame] | 163 | pub(crate) fn microdroid_payload(&self) -> &DiceChainEntryPayload { |
Alice Wang | 1cc1350 | 2023-12-05 11:05:34 +0000 | [diff] [blame] | 164 | &self.payloads[self.payloads.len() - 1] |
| 165 | } |
| 166 | |
| 167 | pub(crate) fn microdroid_payload_components(&self) -> Option<&Vec<SubComponent>> { |
| 168 | self.microdroid_payload().config_descriptor.sub_components.as_ref() |
Alice Wang | d3a9640 | 2023-11-24 15:37:39 +0000 | [diff] [blame] | 169 | } |
| 170 | |
| 171 | /// Returns true if all payloads in the DICE chain are in normal mode. |
| 172 | pub(crate) fn all_entries_are_secure(&self) -> bool { |
| 173 | self.payloads.iter().all(|p| p.mode == DiceMode::kDiceModeNormal) |
| 174 | } |
| 175 | } |
| 176 | |
Alice Wang | 9eebbab | 2024-04-10 14:57:27 +0000 | [diff] [blame] | 177 | fn vendor_partition_exists( |
| 178 | client_vm_dice_chain_len: usize, |
| 179 | service_vm_dice_chain_len: usize, |
| 180 | ) -> Result<bool> { |
| 181 | let entries_up_to_pvmfw_len = service_vm_dice_chain_len - 1; |
| 182 | // Client VM DICE chain = entries_up_to_pvmfw |
| 183 | // + Vendor module entry (exists only when the vendor partition is present) |
| 184 | // + Microdroid kernel entry (added in pvmfw) |
| 185 | // + Apk/Apexes entry (added in microdroid) |
| 186 | match client_vm_dice_chain_len.checked_sub(entries_up_to_pvmfw_len) { |
| 187 | Some(2) => { |
| 188 | debug!("The vendor partition entry is not present in the client VM's DICE chain"); |
| 189 | Ok(false) |
| 190 | } |
| 191 | Some(3) => { |
| 192 | info!("The vendor partition entry is present in the client VM's DICE chain"); |
| 193 | Ok(true) |
| 194 | } |
| 195 | _ => { |
| 196 | error!( |
| 197 | "The client VM's DICE chain must contain two or three extra entries. \ |
| 198 | Service VM DICE chain: {} entries, client VM DICE chain: {} entries", |
| 199 | service_vm_dice_chain_len, client_vm_dice_chain_len |
| 200 | ); |
| 201 | Err(RequestProcessingError::InvalidDiceChain) |
| 202 | } |
Alice Wang | d3a9640 | 2023-11-24 15:37:39 +0000 | [diff] [blame] | 203 | } |
Alice Wang | d3a9640 | 2023-11-24 15:37:39 +0000 | [diff] [blame] | 204 | } |
| 205 | |
| 206 | #[derive(Debug, Clone)] |
| 207 | pub(crate) struct PublicKey(CoseKey); |
| 208 | |
| 209 | impl TryFrom<CoseKey> for PublicKey { |
| 210 | type Error = RequestProcessingError; |
| 211 | |
| 212 | fn try_from(key: CoseKey) -> Result<Self> { |
| 213 | if !key.key_ops.contains(&KeyOperation::Assigned(iana::KeyOperation::Verify)) { |
| 214 | error!("Public key does not support verification"); |
| 215 | return Err(RequestProcessingError::InvalidDiceChain); |
| 216 | } |
| 217 | Ok(Self(key)) |
| 218 | } |
| 219 | } |
| 220 | |
Alice Wang | 3397b36 | 2023-12-01 13:57:10 +0000 | [diff] [blame] | 221 | impl PublicKey { |
| 222 | /// Verifies the signature of the provided message with the public key. |
| 223 | /// |
| 224 | /// This function supports the following key/algorithm types as specified in |
| 225 | /// hardware/interfaces/security/rkp/aidl/android/hardware/security/keymint/ |
| 226 | /// generateCertificateRequestV2.cddl: |
| 227 | /// |
| 228 | /// PubKeyEd25519 / PubKeyECDSA256 / PubKeyECDSA384 |
| 229 | pub(crate) fn verify(&self, signature: &[u8], message: &[u8]) -> Result<()> { |
| 230 | match &self.0.kty { |
| 231 | KeyType::Assigned(iana::KeyType::EC2) => { |
| 232 | let public_key = EcKey::from_cose_public_key(&self.0)?; |
| 233 | let Some(Algorithm::Assigned(alg)) = self.0.alg else { |
| 234 | error!("Invalid algorithm in COSE key {:?}", self.0.alg); |
| 235 | return Err(RequestProcessingError::InvalidDiceChain); |
| 236 | }; |
| 237 | let digester = match alg { |
| 238 | iana::Algorithm::ES256 => Digester::sha256(), |
| 239 | iana::Algorithm::ES384 => Digester::sha384(), |
| 240 | _ => { |
| 241 | error!("Unsupported algorithm in EC2 key: {:?}", alg); |
| 242 | return Err(RequestProcessingError::InvalidDiceChain); |
| 243 | } |
| 244 | }; |
| 245 | let digest = digester.digest(message)?; |
| 246 | Ok(public_key.ecdsa_verify(signature, &digest)?) |
| 247 | } |
| 248 | KeyType::Assigned(iana::KeyType::OKP) => { |
| 249 | let curve_type = |
| 250 | get_label_value(&self.0, Label::Int(iana::OkpKeyParameter::Crv.to_i64()))?; |
| 251 | if curve_type != &Value::from(iana::EllipticCurve::Ed25519.to_i64()) { |
| 252 | error!("Unsupported curve type in OKP COSE key: {:?}", curve_type); |
| 253 | return Err(RequestProcessingError::OperationUnimplemented); |
| 254 | } |
| 255 | let x = get_label_value_as_bytes( |
| 256 | &self.0, |
| 257 | Label::Int(iana::OkpKeyParameter::X.to_i64()), |
| 258 | )?; |
| 259 | let public_key = x.try_into().map_err(|_| { |
| 260 | error!("Invalid ED25519 public key size: {}", x.len()); |
| 261 | RequestProcessingError::InvalidDiceChain |
| 262 | })?; |
| 263 | let signature = signature.try_into().map_err(|_| { |
| 264 | error!("Invalid ED25519 signature size: {}", signature.len()); |
| 265 | RequestProcessingError::InvalidDiceChain |
| 266 | })?; |
| 267 | Ok(ed25519_verify(message, signature, public_key)?) |
| 268 | } |
| 269 | kty => { |
| 270 | error!("Unsupported key type in COSE key: {:?}", kty); |
| 271 | Err(RequestProcessingError::OperationUnimplemented) |
| 272 | } |
| 273 | } |
| 274 | } |
| 275 | } |
| 276 | |
Alice Wang | d3a9640 | 2023-11-24 15:37:39 +0000 | [diff] [blame] | 277 | /// Represents a partially decoded `DiceChainEntryPayload`. The whole payload is defined in: |
| 278 | /// |
| 279 | /// hardware/interfaces/security/rkp/aidl/android/hardware/security/keymint/ |
| 280 | /// generateCertificateRequestV2.cddl |
| 281 | #[derive(Debug, Clone)] |
| 282 | pub(crate) struct DiceChainEntryPayload { |
Alice Wang | 3397b36 | 2023-12-01 13:57:10 +0000 | [diff] [blame] | 283 | pub(crate) subject_public_key: PublicKey, |
Alice Wang | d3a9640 | 2023-11-24 15:37:39 +0000 | [diff] [blame] | 284 | mode: DiceMode, |
Alice Wang | 4e09310 | 2023-12-13 09:16:29 +0000 | [diff] [blame] | 285 | pub(crate) code_hash: [u8; HASH_SIZE], |
| 286 | pub(crate) authority_hash: [u8; HASH_SIZE], |
Alice Wang | 1cc1350 | 2023-12-05 11:05:34 +0000 | [diff] [blame] | 287 | config_descriptor: ConfigDescriptor, |
Alice Wang | d3a9640 | 2023-11-24 15:37:39 +0000 | [diff] [blame] | 288 | } |
| 289 | |
| 290 | impl DiceChainEntryPayload { |
| 291 | /// Validates the signature of the provided CBOR value with the provided public key and |
| 292 | /// extracts payload from the value. |
| 293 | fn validate_cose_signature_and_extract_payload( |
| 294 | value: Value, |
Alice Wang | 3397b36 | 2023-12-01 13:57:10 +0000 | [diff] [blame] | 295 | authority_public_key: &PublicKey, |
Alice Wang | d3a9640 | 2023-11-24 15:37:39 +0000 | [diff] [blame] | 296 | ) -> Result<Self> { |
| 297 | let cose_sign1 = CoseSign1::from_cbor_value(value)?; |
Alice Wang | 3397b36 | 2023-12-01 13:57:10 +0000 | [diff] [blame] | 298 | let aad = &[]; // AAD is not used in DICE chain entry. |
| 299 | cose_sign1.verify_signature(aad, |signature, message| { |
| 300 | authority_public_key.verify(signature, message) |
| 301 | })?; |
Alice Wang | d3a9640 | 2023-11-24 15:37:39 +0000 | [diff] [blame] | 302 | |
| 303 | let payload = cose_sign1.payload.ok_or_else(|| { |
| 304 | error!("No payload found in the DICE chain entry"); |
| 305 | RequestProcessingError::InvalidDiceChain |
| 306 | })?; |
Alice Wang | 4e09310 | 2023-12-13 09:16:29 +0000 | [diff] [blame] | 307 | Self::from_slice(&payload) |
Alice Wang | d3a9640 | 2023-11-24 15:37:39 +0000 | [diff] [blame] | 308 | } |
Ajinkya Chalke | bd51176 | 2023-12-12 11:57:03 +0000 | [diff] [blame] | 309 | |
Alice Wang | 4e09310 | 2023-12-13 09:16:29 +0000 | [diff] [blame] | 310 | pub(crate) fn from_slice(data: &[u8]) -> Result<Self> { |
| 311 | let entries = value_to_map(Value::from_slice(data)?, "DiceChainEntryPayload")?; |
| 312 | let mut builder = PayloadBuilder::default(); |
| 313 | for (key, value) in entries.into_iter() { |
| 314 | let key: i64 = value_to_num(key, "DiceChainEntryPayload key")?; |
| 315 | match key { |
| 316 | SUBJECT_PUBLIC_KEY => { |
| 317 | let subject_public_key = value_to_bytes(value, "subject_public_key")?; |
| 318 | let subject_public_key = |
| 319 | CoseKey::from_slice(&subject_public_key)?.try_into()?; |
| 320 | builder.subject_public_key(subject_public_key)?; |
| 321 | } |
| 322 | MODE => builder.mode(to_mode(value)?)?, |
| 323 | CODE_HASH => { |
| 324 | let code_hash = value_to_byte_array(value, "DiceChainEntryPayload code_hash")?; |
| 325 | builder.code_hash(code_hash)?; |
| 326 | } |
| 327 | AUTHORITY_HASH => { |
| 328 | let authority_hash = |
| 329 | value_to_byte_array(value, "DiceChainEntryPayload authority_hash")?; |
| 330 | builder.authority_hash(authority_hash)?; |
| 331 | } |
| 332 | CONFIG_DESC => { |
| 333 | let config_descriptor = value_to_bytes(value, "config_descriptor")?; |
| 334 | let config_descriptor = ConfigDescriptor::from_slice(&config_descriptor)?; |
| 335 | builder.config_descriptor(config_descriptor)?; |
| 336 | } |
| 337 | _ => {} |
Ajinkya Chalke | bd51176 | 2023-12-12 11:57:03 +0000 | [diff] [blame] | 338 | } |
Ajinkya Chalke | bd51176 | 2023-12-12 11:57:03 +0000 | [diff] [blame] | 339 | } |
Alice Wang | 4e09310 | 2023-12-13 09:16:29 +0000 | [diff] [blame] | 340 | builder.build() |
Ajinkya Chalke | bd51176 | 2023-12-12 11:57:03 +0000 | [diff] [blame] | 341 | } |
Ajinkya Chalke | bd51176 | 2023-12-12 11:57:03 +0000 | [diff] [blame] | 342 | } |
Alice Wang | 1cc1350 | 2023-12-05 11:05:34 +0000 | [diff] [blame] | 343 | /// Represents a partially decoded `ConfigurationDescriptor`. |
| 344 | /// |
| 345 | /// The whole `ConfigurationDescriptor` is defined in: |
| 346 | /// |
| 347 | /// hardware/interfaces/security/rkp/aidl/android/hardware/security/keymint/ |
| 348 | /// generateCertificateRequestV2.cddl |
| 349 | #[derive(Debug, Clone)] |
| 350 | pub(crate) struct ConfigDescriptor { |
| 351 | component_name: String, |
| 352 | sub_components: Option<Vec<SubComponent>>, |
| 353 | } |
| 354 | |
| 355 | impl ConfigDescriptor { |
| 356 | fn from_slice(data: &[u8]) -> Result<Self> { |
| 357 | let value = Value::from_slice(data)?; |
| 358 | let entries = value_to_map(value, "ConfigDescriptor")?; |
| 359 | let mut builder = ConfigDescriptorBuilder::default(); |
| 360 | for (key, value) in entries.into_iter() { |
| 361 | let key: i64 = value_to_num(key, "ConfigDescriptor key")?; |
| 362 | match key { |
| 363 | CONFIG_DESC_COMPONENT_NAME => { |
| 364 | let name = value_to_text(value, "ConfigDescriptor component_name")?; |
| 365 | builder.component_name(name)?; |
| 366 | } |
| 367 | CONFIG_DESC_SUB_COMPONENTS => { |
| 368 | let sub_components = value_to_array(value, "ConfigDescriptor sub_components")?; |
| 369 | let sub_components = sub_components |
| 370 | .into_iter() |
| 371 | .map(SubComponent::try_from) |
| 372 | .collect::<Result<Vec<_>>>()?; |
| 373 | builder.sub_components(sub_components)? |
| 374 | } |
| 375 | _ => {} |
| 376 | } |
| 377 | } |
| 378 | builder.build() |
| 379 | } |
| 380 | } |
| 381 | |
| 382 | #[derive(Debug, Clone, Default)] |
| 383 | struct ConfigDescriptorBuilder { |
| 384 | component_name: OnceCell<String>, |
| 385 | sub_components: OnceCell<Vec<SubComponent>>, |
| 386 | } |
| 387 | |
| 388 | impl ConfigDescriptorBuilder { |
| 389 | fn component_name(&mut self, component_name: String) -> Result<()> { |
| 390 | set_once(&self.component_name, component_name, "ConfigDescriptor component_name") |
| 391 | } |
| 392 | |
| 393 | fn sub_components(&mut self, sub_components: Vec<SubComponent>) -> Result<()> { |
| 394 | set_once(&self.sub_components, sub_components, "ConfigDescriptor sub_components") |
| 395 | } |
| 396 | |
| 397 | fn build(mut self) -> Result<ConfigDescriptor> { |
| 398 | let component_name = |
| 399 | take_value(&mut self.component_name, "ConfigDescriptor component_name")?; |
| 400 | let sub_components = self.sub_components.take(); |
| 401 | Ok(ConfigDescriptor { component_name, sub_components }) |
| 402 | } |
| 403 | } |
| 404 | |
| 405 | #[derive(Debug, Clone)] |
| 406 | pub(crate) struct SubComponent { |
| 407 | pub(crate) name: String, |
| 408 | pub(crate) version: u64, |
| 409 | pub(crate) code_hash: Vec<u8>, |
| 410 | pub(crate) authority_hash: Vec<u8>, |
| 411 | } |
| 412 | |
| 413 | impl TryFrom<Value> for SubComponent { |
| 414 | type Error = RequestProcessingError; |
| 415 | |
| 416 | fn try_from(value: Value) -> Result<Self> { |
| 417 | let entries = value_to_map(value, "SubComponent")?; |
| 418 | let mut builder = SubComponentBuilder::default(); |
| 419 | for (key, value) in entries.into_iter() { |
| 420 | let key: i64 = value_to_num(key, "SubComponent key")?; |
| 421 | match key { |
| 422 | SUB_COMPONENT_NAME => { |
| 423 | builder.name(value_to_text(value, "SubComponent component_name")?)? |
| 424 | } |
| 425 | SUB_COMPONENT_VERSION => { |
| 426 | builder.version(value_to_num(value, "SubComponent version")?)? |
| 427 | } |
| 428 | SUB_COMPONENT_CODE_HASH => { |
| 429 | builder.code_hash(value_to_bytes(value, "SubComponent code_hash")?)? |
| 430 | } |
| 431 | SUB_COMPONENT_AUTHORITY_HASH => { |
| 432 | builder.authority_hash(value_to_bytes(value, "SubComponent authority_hash")?)? |
| 433 | } |
| 434 | k => { |
| 435 | error!("Unknown key in SubComponent: {}", k); |
| 436 | return Err(RequestProcessingError::InvalidDiceChain); |
| 437 | } |
| 438 | } |
| 439 | } |
| 440 | builder.build() |
| 441 | } |
| 442 | } |
| 443 | |
| 444 | #[derive(Debug, Clone, Default)] |
| 445 | struct SubComponentBuilder { |
| 446 | name: OnceCell<String>, |
| 447 | version: OnceCell<u64>, |
| 448 | code_hash: OnceCell<Vec<u8>>, |
| 449 | authority_hash: OnceCell<Vec<u8>>, |
| 450 | } |
| 451 | |
| 452 | impl SubComponentBuilder { |
| 453 | fn name(&mut self, name: String) -> Result<()> { |
| 454 | set_once(&self.name, name, "SubComponent name") |
| 455 | } |
| 456 | |
| 457 | fn version(&mut self, version: u64) -> Result<()> { |
| 458 | set_once(&self.version, version, "SubComponent version") |
| 459 | } |
| 460 | |
| 461 | fn code_hash(&mut self, code_hash: Vec<u8>) -> Result<()> { |
| 462 | set_once(&self.code_hash, code_hash, "SubComponent code_hash") |
| 463 | } |
| 464 | |
| 465 | fn authority_hash(&mut self, authority_hash: Vec<u8>) -> Result<()> { |
| 466 | set_once(&self.authority_hash, authority_hash, "SubComponent authority_hash") |
| 467 | } |
| 468 | |
| 469 | fn build(mut self) -> Result<SubComponent> { |
| 470 | let name = take_value(&mut self.name, "SubComponent name")?; |
| 471 | let version = take_value(&mut self.version, "SubComponent version")?; |
| 472 | let code_hash = take_value(&mut self.code_hash, "SubComponent code_hash")?; |
| 473 | let authority_hash = take_value(&mut self.authority_hash, "SubComponent authority_hash")?; |
| 474 | Ok(SubComponent { name, version, code_hash, authority_hash }) |
| 475 | } |
| 476 | } |
| 477 | |
Alice Wang | d3a9640 | 2023-11-24 15:37:39 +0000 | [diff] [blame] | 478 | fn to_mode(value: Value) -> Result<DiceMode> { |
| 479 | let mode = match value { |
| 480 | // Mode is supposed to be encoded as a 1-byte bstr, but some implementations instead |
| 481 | // encode it as an integer. Accept either. See b/273552826. |
| 482 | // If Mode is omitted, it should be treated as if it was NotConfigured, according to |
| 483 | // the Open Profile for DICE spec. |
| 484 | Value::Bytes(bytes) => { |
| 485 | if bytes.len() != 1 { |
| 486 | error!("Bytes array with invalid length for mode: {:?}", bytes.len()); |
| 487 | return Err(RequestProcessingError::InvalidDiceChain); |
| 488 | } |
| 489 | bytes[0].into() |
| 490 | } |
| 491 | Value::Integer(i) => i, |
| 492 | v => return Err(CoseError::UnexpectedItem(cbor_value_type(&v), "bstr or int").into()), |
| 493 | }; |
| 494 | let mode = match mode { |
| 495 | x if x == (DiceMode::kDiceModeNormal as i64).into() => DiceMode::kDiceModeNormal, |
| 496 | x if x == (DiceMode::kDiceModeDebug as i64).into() => DiceMode::kDiceModeDebug, |
| 497 | x if x == (DiceMode::kDiceModeMaintenance as i64).into() => DiceMode::kDiceModeMaintenance, |
| 498 | // If Mode is invalid, it should be treated as if it was NotConfigured, according to |
| 499 | // the Open Profile for DICE spec. |
| 500 | _ => DiceMode::kDiceModeNotInitialized, |
| 501 | }; |
| 502 | Ok(mode) |
| 503 | } |
| 504 | |
| 505 | #[derive(Default, Debug, Clone)] |
| 506 | struct PayloadBuilder { |
| 507 | subject_public_key: OnceCell<PublicKey>, |
| 508 | mode: OnceCell<DiceMode>, |
| 509 | code_hash: OnceCell<[u8; HASH_SIZE]>, |
| 510 | authority_hash: OnceCell<[u8; HASH_SIZE]>, |
Alice Wang | 1cc1350 | 2023-12-05 11:05:34 +0000 | [diff] [blame] | 511 | config_descriptor: OnceCell<ConfigDescriptor>, |
Alice Wang | d3a9640 | 2023-11-24 15:37:39 +0000 | [diff] [blame] | 512 | } |
| 513 | |
| 514 | fn set_once<T>(field: &OnceCell<T>, value: T, field_name: &str) -> Result<()> { |
| 515 | field.set(value).map_err(|_| { |
| 516 | error!("Field '{field_name}' is duplicated in the Payload"); |
| 517 | RequestProcessingError::InvalidDiceChain |
| 518 | }) |
| 519 | } |
| 520 | |
| 521 | fn take_value<T>(field: &mut OnceCell<T>, field_name: &str) -> Result<T> { |
| 522 | field.take().ok_or_else(|| { |
| 523 | error!("Field '{field_name}' is missing in the Payload"); |
| 524 | RequestProcessingError::InvalidDiceChain |
| 525 | }) |
| 526 | } |
| 527 | |
| 528 | impl PayloadBuilder { |
| 529 | fn subject_public_key(&mut self, key: PublicKey) -> Result<()> { |
| 530 | set_once(&self.subject_public_key, key, "subject_public_key") |
| 531 | } |
| 532 | |
| 533 | fn mode(&mut self, mode: DiceMode) -> Result<()> { |
| 534 | set_once(&self.mode, mode, "mode") |
| 535 | } |
| 536 | |
| 537 | fn code_hash(&mut self, code_hash: [u8; HASH_SIZE]) -> Result<()> { |
| 538 | set_once(&self.code_hash, code_hash, "code_hash") |
| 539 | } |
| 540 | |
| 541 | fn authority_hash(&mut self, authority_hash: [u8; HASH_SIZE]) -> Result<()> { |
| 542 | set_once(&self.authority_hash, authority_hash, "authority_hash") |
| 543 | } |
| 544 | |
Alice Wang | 1cc1350 | 2023-12-05 11:05:34 +0000 | [diff] [blame] | 545 | fn config_descriptor(&mut self, config_descriptor: ConfigDescriptor) -> Result<()> { |
Alice Wang | d3a9640 | 2023-11-24 15:37:39 +0000 | [diff] [blame] | 546 | set_once(&self.config_descriptor, config_descriptor, "config_descriptor") |
| 547 | } |
| 548 | |
| 549 | fn build(mut self) -> Result<DiceChainEntryPayload> { |
| 550 | let subject_public_key = take_value(&mut self.subject_public_key, "subject_public_key")?; |
| 551 | // If Mode is omitted, it should be treated as if it was NotConfigured, according to |
| 552 | // the Open Profile for DICE spec. |
| 553 | let mode = self.mode.take().unwrap_or(DiceMode::kDiceModeNotInitialized); |
| 554 | let code_hash = take_value(&mut self.code_hash, "code_hash")?; |
| 555 | let authority_hash = take_value(&mut self.authority_hash, "authority_hash")?; |
| 556 | let config_descriptor = take_value(&mut self.config_descriptor, "config_descriptor")?; |
| 557 | Ok(DiceChainEntryPayload { |
| 558 | subject_public_key, |
| 559 | mode, |
| 560 | code_hash, |
| 561 | authority_hash, |
| 562 | config_descriptor, |
| 563 | }) |
| 564 | } |
| 565 | } |