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(Clone, Eq, PartialEq)] 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 writeln!(f, "}}")?; 106 Ok(()) 107 } 108 } 109 110 impl fmt::Debug for Payload { fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error>111 fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { 112 let mut debug = f.debug_struct("Payload"); 113 debug.field("Issuer", &self.issuer); 114 debug.field("Subject", &self.subject); 115 debug.field("Mode", &self.mode); 116 if let Some(code_desc) = &self.code_desc { 117 debug.field("Code Desc", &hex::encode(code_desc)); 118 } 119 debug.field("Code Hash", &hex::encode(&self.code_hash)); 120 if let Some(config_hash) = &self.config_hash { 121 debug.field("Config Hash", &hex::encode(config_hash)); 122 } 123 if let Some(authority_desc) = &self.authority_desc { 124 debug.field("Authority Desc", &hex::encode(authority_desc)); 125 } 126 debug.field("Authority Hash", &hex::encode(&self.authority_hash)); 127 debug.field("Config Desc", &self.config_desc); 128 debug.finish() 129 } 130 } 131 132 #[derive(Error, Debug, PartialEq, Eq)] 133 pub(crate) enum PayloadBuilderError { 134 #[error("issuer empty")] 135 IssuerEmpty, 136 #[error("subject empty")] 137 SubjectEmpty, 138 #[error("bad code hash size, actual: {0}, expected: 32, 48, or 64")] 139 CodeHashSize(usize), 140 #[error("bad config hash size, actual: {0}, expected: {1}")] 141 ConfigHashSize(usize, usize), 142 #[error("bad authority hash size, actual: {0}, expected: {1}")] 143 AuthorityHashSize(usize, usize), 144 } 145 146 pub(crate) struct PayloadBuilder(Payload); 147 148 impl PayloadBuilder { 149 /// Constructs a new builder with the given subject public key. with_subject_public_key(subject_public_key: PublicKey) -> Self150 pub fn with_subject_public_key(subject_public_key: PublicKey) -> Self { 151 Self(Payload { 152 issuer: Default::default(), 153 subject: Default::default(), 154 subject_public_key, 155 mode: Default::default(), 156 code_desc: Default::default(), 157 code_hash: Default::default(), 158 config_desc: Default::default(), 159 config_hash: Default::default(), 160 authority_desc: Default::default(), 161 authority_hash: Default::default(), 162 }) 163 } 164 165 /// Builds the [`Payload`] after validating the fields. build(self) -> Result<Payload, PayloadBuilderError>166 pub fn build(self) -> Result<Payload, PayloadBuilderError> { 167 if self.0.issuer.is_empty() { 168 return Err(PayloadBuilderError::IssuerEmpty); 169 } 170 if self.0.subject.is_empty() { 171 return Err(PayloadBuilderError::SubjectEmpty); 172 } 173 let used_hash_size = self.0.code_hash.len(); 174 if ![32, 48, 64].contains(&used_hash_size) { 175 return Err(PayloadBuilderError::CodeHashSize(used_hash_size)); 176 } 177 if let Some(ref config_hash) = self.0.config_hash { 178 if config_hash.len() != used_hash_size { 179 return Err(PayloadBuilderError::ConfigHashSize(config_hash.len(), used_hash_size)); 180 } 181 } 182 if self.0.authority_hash.len() != used_hash_size { 183 return Err(PayloadBuilderError::AuthorityHashSize( 184 self.0.authority_hash.len(), 185 used_hash_size, 186 )); 187 } 188 Ok(self.0) 189 } 190 191 /// Sets the issuer of the payload. 192 #[must_use] issuer<S: Into<String>>(mut self, issuer: S) -> Self193 pub fn issuer<S: Into<String>>(mut self, issuer: S) -> Self { 194 self.0.issuer = issuer.into(); 195 self 196 } 197 198 /// Sets the subject of the payload. 199 #[must_use] subject<S: Into<String>>(mut self, subject: S) -> Self200 pub fn subject<S: Into<String>>(mut self, subject: S) -> Self { 201 self.0.subject = subject.into(); 202 self 203 } 204 205 /// Sets the mode of the payload. 206 #[must_use] mode(mut self, mode: DiceMode) -> Self207 pub fn mode(mut self, mode: DiceMode) -> Self { 208 self.0.mode = mode; 209 self 210 } 211 212 /// Sets the code descriptor of the payload. 213 #[must_use] code_desc(mut self, code_desc: Option<Vec<u8>>) -> Self214 pub fn code_desc(mut self, code_desc: Option<Vec<u8>>) -> Self { 215 self.0.code_desc = code_desc; 216 self 217 } 218 219 /// Sets the code hash of the payload. 220 #[must_use] code_hash(mut self, code_hash: Vec<u8>) -> Self221 pub fn code_hash(mut self, code_hash: Vec<u8>) -> Self { 222 self.0.code_hash = code_hash; 223 self 224 } 225 226 /// Sets the configuration descriptor of the payload. 227 #[must_use] config_desc(mut self, config_desc: ConfigDesc) -> Self228 pub fn config_desc(mut self, config_desc: ConfigDesc) -> Self { 229 self.0.config_desc = config_desc; 230 self 231 } 232 233 /// Sets the configuration hash of the payload. 234 #[must_use] config_hash(mut self, config_hash: Option<Vec<u8>>) -> Self235 pub fn config_hash(mut self, config_hash: Option<Vec<u8>>) -> Self { 236 self.0.config_hash = config_hash; 237 self 238 } 239 240 /// Sets the authority descriptor of the payload. 241 #[must_use] authority_desc(mut self, authority_desc: Option<Vec<u8>>) -> Self242 pub fn authority_desc(mut self, authority_desc: Option<Vec<u8>>) -> Self { 243 self.0.authority_desc = authority_desc; 244 self 245 } 246 247 /// Sets the authority hash of the payload. 248 #[must_use] authority_hash(mut self, authority_hash: Vec<u8>) -> Self249 pub fn authority_hash(mut self, authority_hash: Vec<u8>) -> Self { 250 self.0.authority_hash = authority_hash; 251 self 252 } 253 } 254 255 /// Version of the component from the configuration descriptor. 256 #[derive(Debug, Clone, PartialEq, Eq)] 257 pub enum ComponentVersion { 258 /// An integer component version number. 259 Integer(i64), 260 /// A free-form string component version. 261 String(String), 262 } 263 264 impl Display for ComponentVersion { fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error>265 fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { 266 match self { 267 ComponentVersion::Integer(n) => write!(f, "{n}")?, 268 ComponentVersion::String(s) => write!(f, "{s}")?, 269 } 270 Ok(()) 271 } 272 } 273 274 /// Fields from the configuration descriptor. 275 #[derive(Debug, Default, Clone, PartialEq, Eq)] 276 pub struct ConfigDesc { 277 component_name: Option<String>, 278 component_version: Option<ComponentVersion>, 279 resettable: bool, 280 security_version: Option<u64>, 281 rkp_vm_marker: bool, 282 extensions: Vec<(String, String)>, 283 } 284 285 impl ConfigDesc { 286 /// Gets the component name. component_name(&self) -> Option<&str>287 pub fn component_name(&self) -> Option<&str> { 288 self.component_name.as_deref() 289 } 290 291 /// Gets the component version. component_version(&self) -> Option<&ComponentVersion>292 pub fn component_version(&self) -> Option<&ComponentVersion> { 293 self.component_version.as_ref() 294 } 295 296 /// Returns whether the component is factory resettable. resettable(&self) -> bool297 pub fn resettable(&self) -> bool { 298 self.resettable 299 } 300 301 /// Gets the security version. security_version(&self) -> Option<u64>302 pub fn security_version(&self) -> Option<u64> { 303 self.security_version 304 } 305 306 /// Returns whether the component may be part of an RPK VM. rkp_vm_marker(&self) -> bool307 pub fn rkp_vm_marker(&self) -> bool { 308 self.rkp_vm_marker 309 } 310 311 /// Return any extensions present in the descriptor. extensions(&self) -> &[(String, String)]312 pub fn extensions(&self) -> &[(String, String)] { 313 &self.extensions 314 } 315 } 316 317 impl Display for ConfigDesc { fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error>318 fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { 319 if let Some(component_name) = &self.component_name { 320 writeln!(f, "Component Name: {}", component_name)?; 321 } 322 if let Some(component_version) = &self.component_version { 323 writeln!(f, "Component Version: {}", component_version)?; 324 } 325 if self.resettable { 326 writeln!(f, "Resettable")?; 327 } 328 if let Some(security_version) = &self.security_version { 329 writeln!(f, "Security Version: {}", security_version)?; 330 } 331 if self.rkp_vm_marker { 332 writeln!(f, "RKP VM Marker")?; 333 } 334 for (key, value) in &self.extensions { 335 writeln!(f, "{key}: {value}")?; 336 } 337 Ok(()) 338 } 339 } 340 341 pub(crate) struct ConfigDescBuilder(ConfigDesc); 342 343 impl ConfigDescBuilder { 344 /// Constructs a new builder with default values. new() -> Self345 pub fn new() -> Self { 346 Self(ConfigDesc::default()) 347 } 348 349 /// Builds the [`ConfigDesc`]. build(self) -> ConfigDesc350 pub fn build(self) -> ConfigDesc { 351 self.0 352 } 353 354 /// Sets the component name. 355 #[must_use] component_name(mut self, name: Option<String>) -> Self356 pub fn component_name(mut self, name: Option<String>) -> Self { 357 self.0.component_name = name; 358 self 359 } 360 361 /// Sets the component version. 362 #[must_use] component_version(mut self, version: Option<ComponentVersion>) -> Self363 pub fn component_version(mut self, version: Option<ComponentVersion>) -> Self { 364 self.0.component_version = version; 365 self 366 } 367 368 /// Sets whether the component is factory resettable. 369 #[must_use] resettable(mut self, resettable: bool) -> Self370 pub fn resettable(mut self, resettable: bool) -> Self { 371 self.0.resettable = resettable; 372 self 373 } 374 375 /// Sets the security version. 376 #[must_use] security_version(mut self, version: Option<u64>) -> Self377 pub fn security_version(mut self, version: Option<u64>) -> Self { 378 self.0.security_version = version; 379 self 380 } 381 382 /// Sets whether the component may be part of an RKP VM. 383 #[must_use] rkp_vm_marker(mut self, rkp_vm_marker: bool) -> Self384 pub fn rkp_vm_marker(mut self, rkp_vm_marker: bool) -> Self { 385 self.0.rkp_vm_marker = rkp_vm_marker; 386 self 387 } 388 389 /// Sets the extension key/value pairs. 390 #[must_use] extensions(mut self, extensions: Vec<(String, String)>) -> Self391 pub fn extensions(mut self, extensions: Vec<(String, String)>) -> Self { 392 self.0.extensions = extensions; 393 self 394 } 395 } 396 397 #[cfg(test)] 398 mod tests { 399 use super::*; 400 use crate::publickey::testkeys::{PrivateKey, P256_KEY_PEM}; 401 402 #[test] payload_builder_valid()403 fn payload_builder_valid() { 404 valid_payload().build().unwrap(); 405 } 406 407 #[test] payload_builder_valid_512_bit_hashes()408 fn payload_builder_valid_512_bit_hashes() { 409 valid_payload() 410 .code_hash(vec![1; 64]) 411 .authority_hash(vec![2; 64]) 412 .config_hash(Some(vec![3; 64])) 413 .build() 414 .unwrap(); 415 } 416 417 #[test] payload_builder_valid_384_bit_hashes()418 fn payload_builder_valid_384_bit_hashes() { 419 valid_payload() 420 .code_hash(vec![1; 48]) 421 .authority_hash(vec![2; 48]) 422 .config_hash(Some(vec![3; 48])) 423 .build() 424 .unwrap(); 425 } 426 427 #[test] payload_builder_valid_256_bit_hashes()428 fn payload_builder_valid_256_bit_hashes() { 429 valid_payload() 430 .code_hash(vec![1; 32]) 431 .authority_hash(vec![2; 32]) 432 .config_hash(Some(vec![3; 32])) 433 .build() 434 .unwrap(); 435 } 436 437 #[test] payload_builder_empty_issuer()438 fn payload_builder_empty_issuer() { 439 let err = valid_payload().issuer("").build().unwrap_err(); 440 assert_eq!(err, PayloadBuilderError::IssuerEmpty); 441 } 442 443 #[test] payload_builder_empty_subject()444 fn payload_builder_empty_subject() { 445 let err = valid_payload().subject("").build().unwrap_err(); 446 assert_eq!(err, PayloadBuilderError::SubjectEmpty); 447 } 448 449 #[test] payload_builder_bad_code_hash_size()450 fn payload_builder_bad_code_hash_size() { 451 let err = valid_payload().code_hash(vec![1; 16]).build().unwrap_err(); 452 assert_eq!(err, PayloadBuilderError::CodeHashSize(16)); 453 } 454 455 #[test] payload_builder_bad_authority_hash_size()456 fn payload_builder_bad_authority_hash_size() { 457 let err = valid_payload().authority_hash(vec![1; 16]).build().unwrap_err(); 458 assert_eq!(err, PayloadBuilderError::AuthorityHashSize(16, 64)); 459 } 460 461 #[test] payload_builder_inconsistent_authority_hash_size()462 fn payload_builder_inconsistent_authority_hash_size() { 463 let err = 464 valid_payload().code_hash(vec![1; 32]).authority_hash(vec![1; 64]).build().unwrap_err(); 465 assert_eq!(err, PayloadBuilderError::AuthorityHashSize(64, 32)); 466 } 467 468 #[test] payload_builder_bad_config_hash_size()469 fn payload_builder_bad_config_hash_size() { 470 let err = valid_payload().config_hash(Some(vec![1; 16])).build().unwrap_err(); 471 assert_eq!(err, PayloadBuilderError::ConfigHashSize(16, 64)); 472 } 473 474 #[test] payload_builder_inconsistent_config_hash_size()475 fn payload_builder_inconsistent_config_hash_size() { 476 let err = valid_payload() 477 .code_hash(vec![1; 64]) 478 .config_hash(Some(vec![1; 32])) 479 .build() 480 .unwrap_err(); 481 assert_eq!(err, PayloadBuilderError::ConfigHashSize(32, 64)); 482 } 483 valid_payload() -> PayloadBuilder484 fn valid_payload() -> PayloadBuilder { 485 let key = PrivateKey::from_pem(P256_KEY_PEM[0]).public_key(); 486 PayloadBuilder::with_subject_public_key(key) 487 .issuer("issuer") 488 .subject("subject") 489 .code_hash(vec![1; 64]) 490 .authority_hash(vec![2; 64]) 491 } 492 } 493