1 use crate::publickey::PublicKey; 2 use std::fmt::{self, Display, Formatter}; 3 use thiserror::Error; 4 5 /// Enumeration of modes used in the DICE chain payloads. 6 #[derive(Debug, Default, Copy, Clone, PartialEq, Eq)] 7 pub enum DiceMode { 8 /// This mode also acts as a catch-all for configurations which do not fit the other modes and 9 /// invalid modes. 10 #[default] 11 NotConfigured, 12 /// The device is operating normally under secure configuration. 13 Normal, 14 /// At least one criteria for [`Normal`] is not met and the device is not in a secure state. 15 Debug, 16 /// A recovery or maintenance mode of some kind. 17 Recovery, 18 } 19 20 /// The payload of a DICE chain entry. 21 #[derive(Debug)] 22 pub struct Payload { 23 issuer: String, 24 subject: String, 25 subject_public_key: PublicKey, 26 mode: DiceMode, 27 code_desc: Option<Vec<u8>>, 28 code_hash: Vec<u8>, 29 config_desc: ConfigDesc, 30 config_hash: Option<Vec<u8>>, 31 authority_desc: Option<Vec<u8>>, 32 authority_hash: Vec<u8>, 33 } 34 35 impl Payload { 36 /// Gets the issuer of the payload. issuer(&self) -> &str37 pub fn issuer(&self) -> &str { 38 &self.issuer 39 } 40 41 /// Gets the subject of the payload. subject(&self) -> &str42 pub fn subject(&self) -> &str { 43 &self.subject 44 } 45 46 /// Gets the subject public key of the payload. subject_public_key(&self) -> &PublicKey47 pub fn subject_public_key(&self) -> &PublicKey { 48 &self.subject_public_key 49 } 50 51 /// Gets the mode of the payload. mode(&self) -> DiceMode52 pub fn mode(&self) -> DiceMode { 53 self.mode 54 } 55 56 /// Gets the code descriptor of the payload. code_desc(&self) -> Option<&[u8]>57 pub fn code_desc(&self) -> Option<&[u8]> { 58 self.code_desc.as_deref() 59 } 60 61 /// Gets the code hash of the payload. code_hash(&self) -> &[u8]62 pub fn code_hash(&self) -> &[u8] { 63 &self.code_hash 64 } 65 66 /// Gets the configuration descriptor of the payload. config_desc(&self) -> &ConfigDesc67 pub fn config_desc(&self) -> &ConfigDesc { 68 &self.config_desc 69 } 70 71 /// Gets the configuration hash of the payload. config_hash(&self) -> Option<&[u8]>72 pub fn config_hash(&self) -> Option<&[u8]> { 73 self.config_hash.as_deref() 74 } 75 76 /// Gets the authority descriptor of the payload. authority_desc(&self) -> Option<&[u8]>77 pub fn authority_desc(&self) -> Option<&[u8]> { 78 self.authority_desc.as_deref() 79 } 80 81 /// Gets the authority hash of the payload. authority_hash(&self) -> &[u8]82 pub fn authority_hash(&self) -> &[u8] { 83 &self.authority_hash 84 } 85 } 86 87 impl Display for Payload { fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error>88 fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { 89 writeln!(f, "Issuer: {}", self.issuer)?; 90 writeln!(f, "Subject: {}", self.subject)?; 91 writeln!(f, "Mode: {:?}", self.mode)?; 92 if let Some(code_desc) = &self.code_desc { 93 writeln!(f, "Code Desc: {}", hex::encode(code_desc))?; 94 } 95 writeln!(f, "Code Hash: {}", hex::encode(&self.code_hash))?; 96 if let Some(config_hash) = &self.config_hash { 97 writeln!(f, "Config Hash: {}", hex::encode(config_hash))?; 98 } 99 if let Some(authority_desc) = &self.authority_desc { 100 writeln!(f, "Authority Desc: {}", hex::encode(authority_desc))?; 101 } 102 writeln!(f, "Authority Hash: {}", hex::encode(&self.authority_hash))?; 103 writeln!(f, "Config Desc:")?; 104 write!(f, "{}", &self.config_desc)?; 105 Ok(()) 106 } 107 } 108 109 #[derive(Error, Debug, PartialEq, Eq)] 110 pub(crate) enum PayloadBuilderError { 111 #[error("issuer empty")] 112 IssuerEmpty, 113 #[error("subject empty")] 114 SubjectEmpty, 115 #[error("bad code hash size")] 116 CodeHashSize, 117 #[error("bad config hash size")] 118 ConfigHashSize, 119 #[error("bad authority hash size")] 120 AuthorityHashSize, 121 } 122 123 pub(crate) struct PayloadBuilder(Payload); 124 125 impl PayloadBuilder { 126 /// Constructs a new builder with the given subject public key. with_subject_public_key(subject_public_key: PublicKey) -> Self127 pub fn with_subject_public_key(subject_public_key: PublicKey) -> Self { 128 Self(Payload { 129 issuer: Default::default(), 130 subject: Default::default(), 131 subject_public_key, 132 mode: Default::default(), 133 code_desc: Default::default(), 134 code_hash: Default::default(), 135 config_desc: Default::default(), 136 config_hash: Default::default(), 137 authority_desc: Default::default(), 138 authority_hash: Default::default(), 139 }) 140 } 141 142 /// Builds the [`Payload`] after validating the fields. build(self) -> Result<Payload, PayloadBuilderError>143 pub fn build(self) -> Result<Payload, PayloadBuilderError> { 144 if self.0.issuer.is_empty() { 145 return Err(PayloadBuilderError::IssuerEmpty); 146 } 147 if self.0.subject.is_empty() { 148 return Err(PayloadBuilderError::SubjectEmpty); 149 } 150 let used_hash_size = self.0.code_hash.len(); 151 if ![32, 48, 64].contains(&used_hash_size) { 152 return Err(PayloadBuilderError::CodeHashSize); 153 } 154 if let Some(ref config_hash) = self.0.config_hash { 155 if config_hash.len() != used_hash_size { 156 return Err(PayloadBuilderError::ConfigHashSize); 157 } 158 } 159 if self.0.authority_hash.len() != used_hash_size { 160 return Err(PayloadBuilderError::AuthorityHashSize); 161 } 162 Ok(self.0) 163 } 164 165 /// Sets the issuer of the payload. 166 #[must_use] issuer<S: Into<String>>(mut self, issuer: S) -> Self167 pub fn issuer<S: Into<String>>(mut self, issuer: S) -> Self { 168 self.0.issuer = issuer.into(); 169 self 170 } 171 172 /// Sets the subject of the payload. 173 #[must_use] subject<S: Into<String>>(mut self, subject: S) -> Self174 pub fn subject<S: Into<String>>(mut self, subject: S) -> Self { 175 self.0.subject = subject.into(); 176 self 177 } 178 179 /// Sets the mode of the payload. 180 #[must_use] mode(mut self, mode: DiceMode) -> Self181 pub fn mode(mut self, mode: DiceMode) -> Self { 182 self.0.mode = mode; 183 self 184 } 185 186 /// Sets the code descriptor of the payload. 187 #[must_use] code_desc(mut self, code_desc: Option<Vec<u8>>) -> Self188 pub fn code_desc(mut self, code_desc: Option<Vec<u8>>) -> Self { 189 self.0.code_desc = code_desc; 190 self 191 } 192 193 /// Sets the code hash of the payload. 194 #[must_use] code_hash(mut self, code_hash: Vec<u8>) -> Self195 pub fn code_hash(mut self, code_hash: Vec<u8>) -> Self { 196 self.0.code_hash = code_hash; 197 self 198 } 199 200 /// Sets the configuration descriptor of the payload. 201 #[must_use] config_desc(mut self, config_desc: ConfigDesc) -> Self202 pub fn config_desc(mut self, config_desc: ConfigDesc) -> Self { 203 self.0.config_desc = config_desc; 204 self 205 } 206 207 /// Sets the configuration hash of the payload. 208 #[must_use] config_hash(mut self, config_hash: Option<Vec<u8>>) -> Self209 pub fn config_hash(mut self, config_hash: Option<Vec<u8>>) -> Self { 210 self.0.config_hash = config_hash; 211 self 212 } 213 214 /// Sets the authority descriptor of the payload. 215 #[must_use] authority_desc(mut self, authority_desc: Option<Vec<u8>>) -> Self216 pub fn authority_desc(mut self, authority_desc: Option<Vec<u8>>) -> Self { 217 self.0.authority_desc = authority_desc; 218 self 219 } 220 221 /// Sets the authority hash of the payload. 222 #[must_use] authority_hash(mut self, authority_hash: Vec<u8>) -> Self223 pub fn authority_hash(mut self, authority_hash: Vec<u8>) -> Self { 224 self.0.authority_hash = authority_hash; 225 self 226 } 227 } 228 229 /// Version of the component from the configuration descriptor. 230 #[derive(Debug, Clone, PartialEq, Eq)] 231 pub enum ComponentVersion { 232 /// An integer component version number. 233 Integer(i64), 234 /// A free-form string component version. 235 String(String), 236 } 237 238 impl Display for ComponentVersion { fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error>239 fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { 240 match self { 241 ComponentVersion::Integer(n) => write!(f, "{n}")?, 242 ComponentVersion::String(s) => write!(f, "{s}")?, 243 } 244 Ok(()) 245 } 246 } 247 248 /// Fields from the configuration descriptor. 249 #[derive(Debug, Default, Clone, PartialEq, Eq)] 250 pub struct ConfigDesc { 251 component_name: Option<String>, 252 component_version: Option<ComponentVersion>, 253 resettable: bool, 254 } 255 256 impl ConfigDesc { 257 /// Gets the component name. component_name(&self) -> Option<&str>258 pub fn component_name(&self) -> Option<&str> { 259 self.component_name.as_deref() 260 } 261 262 /// Gets the component version. component_version(&self) -> Option<&ComponentVersion>263 pub fn component_version(&self) -> Option<&ComponentVersion> { 264 self.component_version.as_ref() 265 } 266 267 /// Returns whether the component is factory resettable. resettable(&self) -> bool268 pub fn resettable(&self) -> bool { 269 self.resettable 270 } 271 } 272 273 impl Display for ConfigDesc { fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error>274 fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { 275 if let Some(component_name) = &self.component_name { 276 writeln!(f, "Component Name: {}", component_name)?; 277 } 278 if let Some(component_version) = &self.component_version { 279 writeln!(f, "Component Version: {}", component_version)?; 280 } 281 if self.resettable { 282 writeln!(f, "Resettable")?; 283 } 284 Ok(()) 285 } 286 } 287 288 pub(crate) struct ConfigDescBuilder(ConfigDesc); 289 290 impl ConfigDescBuilder { 291 /// Constructs a new builder with default values. new() -> Self292 pub fn new() -> Self { 293 Self(ConfigDesc::default()) 294 } 295 296 /// Builds the [`ConfigDesc`]. build(self) -> ConfigDesc297 pub fn build(self) -> ConfigDesc { 298 self.0 299 } 300 301 /// Sets the component name. 302 #[must_use] component_name(mut self, name: Option<String>) -> Self303 pub fn component_name(mut self, name: Option<String>) -> Self { 304 self.0.component_name = name; 305 self 306 } 307 308 /// Sets the component version. 309 #[must_use] component_version(mut self, version: Option<ComponentVersion>) -> Self310 pub fn component_version(mut self, version: Option<ComponentVersion>) -> Self { 311 self.0.component_version = version; 312 self 313 } 314 315 /// Sets whether the component is factory resettable. 316 #[must_use] resettable(mut self, resettable: bool) -> Self317 pub fn resettable(mut self, resettable: bool) -> Self { 318 self.0.resettable = resettable; 319 self 320 } 321 } 322 323 #[cfg(test)] 324 mod tests { 325 use super::*; 326 use crate::publickey::testkeys::{PrivateKey, P256_KEY_PEM}; 327 328 #[test] payload_builder_valid()329 fn payload_builder_valid() { 330 valid_payload().build().unwrap(); 331 } 332 333 #[test] payload_builder_valid_512_bit_hashes()334 fn payload_builder_valid_512_bit_hashes() { 335 valid_payload() 336 .code_hash(vec![1; 64]) 337 .authority_hash(vec![2; 64]) 338 .config_hash(Some(vec![3; 64])) 339 .build() 340 .unwrap(); 341 } 342 343 #[test] payload_builder_valid_384_bit_hashes()344 fn payload_builder_valid_384_bit_hashes() { 345 valid_payload() 346 .code_hash(vec![1; 48]) 347 .authority_hash(vec![2; 48]) 348 .config_hash(Some(vec![3; 48])) 349 .build() 350 .unwrap(); 351 } 352 353 #[test] payload_builder_valid_256_bit_hashes()354 fn payload_builder_valid_256_bit_hashes() { 355 valid_payload() 356 .code_hash(vec![1; 32]) 357 .authority_hash(vec![2; 32]) 358 .config_hash(Some(vec![3; 32])) 359 .build() 360 .unwrap(); 361 } 362 363 #[test] payload_builder_empty_issuer()364 fn payload_builder_empty_issuer() { 365 let err = valid_payload().issuer("").build().unwrap_err(); 366 assert_eq!(err, PayloadBuilderError::IssuerEmpty); 367 } 368 369 #[test] payload_builder_empty_subject()370 fn payload_builder_empty_subject() { 371 let err = valid_payload().subject("").build().unwrap_err(); 372 assert_eq!(err, PayloadBuilderError::SubjectEmpty); 373 } 374 375 #[test] payload_builder_bad_code_hash_size()376 fn payload_builder_bad_code_hash_size() { 377 let err = valid_payload().code_hash(vec![1; 16]).build().unwrap_err(); 378 assert_eq!(err, PayloadBuilderError::CodeHashSize); 379 } 380 381 #[test] payload_builder_bad_authority_hash_size()382 fn payload_builder_bad_authority_hash_size() { 383 let err = valid_payload().authority_hash(vec![1; 16]).build().unwrap_err(); 384 assert_eq!(err, PayloadBuilderError::AuthorityHashSize); 385 } 386 387 #[test] payload_builder_inconsistent_authority_hash_size()388 fn payload_builder_inconsistent_authority_hash_size() { 389 let err = 390 valid_payload().code_hash(vec![1; 32]).authority_hash(vec![1; 64]).build().unwrap_err(); 391 assert_eq!(err, PayloadBuilderError::AuthorityHashSize); 392 } 393 394 #[test] payload_builder_bad_config_hash_size()395 fn payload_builder_bad_config_hash_size() { 396 let err = valid_payload().config_hash(Some(vec![1; 16])).build().unwrap_err(); 397 assert_eq!(err, PayloadBuilderError::ConfigHashSize); 398 } 399 400 #[test] payload_builder_inconsistent_config_hash_size()401 fn payload_builder_inconsistent_config_hash_size() { 402 let err = valid_payload() 403 .code_hash(vec![1; 64]) 404 .config_hash(Some(vec![1; 32])) 405 .build() 406 .unwrap_err(); 407 assert_eq!(err, PayloadBuilderError::ConfigHashSize); 408 } 409 valid_payload() -> PayloadBuilder410 fn valid_payload() -> PayloadBuilder { 411 let key = PrivateKey::from_pem(P256_KEY_PEM[0]).public_key(); 412 PayloadBuilder::with_subject_public_key(key) 413 .issuer("issuer") 414 .subject("subject") 415 .code_hash(vec![1; 64]) 416 .authority_hash(vec![2; 64]) 417 } 418 } 419