• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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