Skip to main content

islet_hes/measured_boot/
measurement.rs

1use core::str::from_utf8;
2
3use alloc::string::{String, ToString};
4use tinyvec::ArrayVec;
5
6use super::MeasurementError;
7use crate::BootMeasurement;
8use crate::ValueHash;
9
10/// Minimal size based on the shortest hash algorithm - sha256
11pub const MEASUREMENT_VALUE_MIN_SIZE: usize = 32;
12/// Maximal size based on the longest hash algorithm - sha512
13pub const MEASUREMENT_VALUE_MAX_SIZE: usize = 64;
14/// Minimal size based on the shortest hash algorithm - sha256
15pub const SIGNER_ID_MIN_SIZE: usize = MEASUREMENT_VALUE_MIN_SIZE;
16/// Maximal size based on the longest hash algorithm - sha512
17pub const SIGNER_ID_MAX_SIZE: usize = MEASUREMENT_VALUE_MAX_SIZE;
18/// Set based on RSS imlementation.
19pub const VERSION_MAX_SIZE: usize = 14;
20/// Set based on RSS imlementation.
21pub const SW_TYPE_MAX_SIZE: usize = 32;
22
23/// Error message for MessageError::InvalidData(), when converted signer_id has wrong size.
24const SIGNER_ID_SIZE_ERROR_MSG: &'static str = "SignerIdSize";
25/// Error message for MessageError::InvalidData(), when converted sw_version has wrong size.
26const SW_VERSION_SIZE_ERROR_MSG: &'static str = "SWVersionSize";
27/// Error message for MessageError::InvalidData(), when converted sw_version is not a valid utf-8.
28const SW_VERSION_VALUE_ERROR_MSG: &'static str = "SWVersionNotUtf8";
29/// Error message for MessageError::InvalidData(), when converted sw_type has wrong size.
30const SW_TYPE_SIZE_ERROR_MSG: &'static str = "SWTypeSize";
31/// Error message for MessageError::InvalidData(), when converted sw_type is not a valid utf-8.
32const SW_TYPE_VALUE_ERROR_MSG: &'static str = "SWTypeNotUtf8";
33/// Error message for MessageError::InvalidData(), when converted measurement_value has wrong size.
34const MEASUREMENT_VALUE_SIZE_ERROR_MSG: &'static str = "MeasurementValueSize";
35
36/// Error message for MessageError::InvalidData(), when converted measurement_type has wrong value.
37const MEASUREMENT_TYPE_ERROR_MSG: &'static str = "MeasurementType";
38
39/// Represents hash algorithm used for calculating measurement value.
40#[derive(Debug, Copy, Clone, PartialEq)]
41pub enum MeasurementType {
42    Sha256,
43    Sha384,
44    Sha512,
45}
46
47impl Default for MeasurementType {
48    /// Arbitrary default value - won't be used, because the root
49    /// MeasurementSlot will be set as not populated by default.
50    fn default() -> Self {
51        Self::Sha256
52    }
53}
54
55impl TryFrom<u16> for MeasurementType {
56    type Error = MeasurementError;
57    /// Converts the `MeasurementType` from given [`u16`].
58    /// Returns [`MeasurementError::InvalidData`] when `value` is out of scope
59    /// of `MeasurementType`.
60    fn try_from(value: u16) -> Result<Self, Self::Error> {
61        match value {
62            0 => Ok(Self::Sha256),
63            1 => Ok(Self::Sha384),
64            2 => Ok(Self::Sha512),
65            _ => Err(MeasurementError::InvalidData(MEASUREMENT_TYPE_ERROR_MSG)),
66        }
67    }
68}
69
70impl MeasurementType {
71    pub fn hash_len(&self) -> usize {
72        match self {
73            MeasurementType::Sha256 => 32,
74            MeasurementType::Sha384 => 48,
75            MeasurementType::Sha512 => 64,
76        }
77    }
78}
79
80pub type SignerHash = ArrayVec<[u8; SIGNER_ID_MAX_SIZE]>;
81pub type SWVersion = String;
82pub type SWType = String;
83
84/// Keeps measurement slot metadata.
85#[derive(Debug, Default, Clone, PartialEq)]
86pub struct MeasurementMetaData {
87    /// Represents the hash o signing authority public key.
88    pub signer_id: SignerHash,
89    /// Represents the issued software version.
90    pub sw_version: SWVersion,
91    /// Represents the way in which the measurement value of
92    /// the software component is computed.
93    pub algorithm: MeasurementType,
94    /// Represents the role of the software component.
95    pub sw_type: SWType,
96}
97
98/// Keeps measurement metadata and value.
99#[derive(Debug, Default, Clone, PartialEq)]
100pub struct Measurement {
101    /// Keeps measurement metadata.
102    pub metadata: MeasurementMetaData,
103    /// Represent the hash value of the measurement.
104    pub value: ValueHash,
105}
106
107impl TryFrom<BootMeasurement> for Measurement {
108    type Error = MeasurementError;
109    /// Tries to convert the given [`BootMeasurement`] to `Measurement`.
110    /// Returns [`MeasurementError::InvalidData`], when values cannot be properly converted.
111    fn try_from(value: BootMeasurement) -> Result<Self, Self::Error> {
112        if value.metadata.signer_id.len() < SIGNER_ID_MIN_SIZE
113            || value.metadata.signer_id.len() > SIGNER_ID_MAX_SIZE
114        {
115            return Err(MeasurementError::InvalidData(SIGNER_ID_SIZE_ERROR_MSG));
116        }
117
118        if value.metadata.sw_version.len() > VERSION_MAX_SIZE {
119            return Err(MeasurementError::InvalidData(SW_VERSION_SIZE_ERROR_MSG));
120        }
121
122        if value.metadata.sw_type.len() > SW_TYPE_MAX_SIZE {
123            return Err(MeasurementError::InvalidData(SW_TYPE_SIZE_ERROR_MSG));
124        }
125
126        if value.measurement_value.len() < MEASUREMENT_VALUE_MIN_SIZE
127            || value.measurement_value.len() > MEASUREMENT_VALUE_MAX_SIZE
128        {
129            return Err(MeasurementError::InvalidData(
130                MEASUREMENT_VALUE_SIZE_ERROR_MSG,
131            ));
132        }
133
134        let sw_version = match from_utf8(&value.metadata.sw_version) {
135            Ok(version_str) => version_str,
136            Err(_) => return Err(MeasurementError::InvalidData(SW_VERSION_VALUE_ERROR_MSG)),
137        }
138        .to_string();
139
140        let sw_type = match from_utf8(&value.metadata.sw_type) {
141            Ok(type_str) => type_str,
142            Err(_) => return Err(MeasurementError::InvalidData(SW_TYPE_VALUE_ERROR_MSG)),
143        }
144        .to_string();
145
146        Ok(Self {
147            metadata: MeasurementMetaData {
148                signer_id: value.metadata.signer_id.iter().cloned().collect(),
149                sw_version,
150                algorithm: value.metadata.measurement_type.try_into()?,
151                sw_type,
152            },
153            value: value.measurement_value,
154        })
155    }
156}
157
158#[cfg(test)]
159mod tests {
160    use super::*;
161    use crate::{
162        hw::{
163            BootMeasurementMetadata, HWHash, HWSWType, HWSWVersion, MAX_HW_HASH_VALUE_SIZE,
164            MAX_HW_SW_TYPE_SIZE, MAX_HW_SW_VERSION_SIZE,
165        },
166        BootMeasurement,
167    };
168    use alloc::{vec, vec::Vec};
169
170    fn boot_measurement_value(len: usize) -> HWHash {
171        let mut value: HWHash = HWHash::from([
172            0x8a, 0x66, 0x01, 0xf6, 0x70, 0x74, 0x8b, 0xe2, 0x33, 0xff, 0x5d, 0x75, 0xd7, 0xea,
173            0x89, 0xa8, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x01, 0x05, 0x01, 0xEF,
174            0x68, 0x07, 0x88, 0xCC, 0x83, 0x09, 0x22, 0xCD, 0x09, 0x61, 0xB6, 0xFF, 0xbb, 0xbb,
175            0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x56, 0x46, 0x58, 0x49, 0x99, 0x31, 0xcf, 0x59,
176            0x7d, 0xbc, 0x3a, 0x4e, 0x68, 0x79, 0x8a, 0x1c,
177        ]);
178        value.truncate(len);
179        value
180    }
181
182    fn boot_signer_id(len: usize) -> HWHash {
183        let mut signer_id: HWHash = HWHash::from([
184            0x01, 0x05, 0x01, 0xEF, 0x68, 0x07, 0x88, 0xCC, 0x33, 0x06, 0x54, 0xAB, 0x09, 0x01,
185            0x74, 0x77, 0x49, 0x08, 0x93, 0xA8, 0x01, 0x07, 0xEF, 0x01, 0x83, 0x09, 0x22, 0xCD,
186            0x09, 0x61, 0xB6, 0xFF, 0x01, 0x05, 0x01, 0xEF, 0x68, 0x07, 0x88, 0xCC, 0x33, 0x06,
187            0x54, 0xAB, 0x09, 0x01, 0x74, 0x77, 0x49, 0x08, 0x93, 0xA8, 0x01, 0x07, 0xEF, 0x01,
188            0x83, 0x09, 0x22, 0xCD, 0x09, 0x61, 0xB6, 0xFF,
189        ]);
190        signer_id.truncate(len);
191        signer_id
192    }
193
194    fn boot_sw_type(len: usize) -> HWSWType {
195        let mut sw_type: HWSWType =
196            HWSWType::from([0x4D, 0x45, 0x41, 0x53, 0x55, 0x52, 0x45, 0x44, 0x5F, 0x42]);
197        sw_type.truncate(len);
198        sw_type
199    }
200
201    fn boot_sw_version(len: usize) -> HWSWVersion {
202        let mut sw_version: HWSWVersion = HWSWVersion::from([
203            0x32, 0x35, 0x35, 0x2E, 0x32, 0x35, 0x35, 0x2E, 0x36, 0x35, 0x35, 0x33, 0x35, 0x0,
204        ]);
205        sw_version.truncate(len);
206        sw_version
207    }
208
209    #[test]
210    fn measurement_type_conversion_ok() {
211        let measurement_types_u16: Vec<u16> = vec![0, 1, 2];
212        let measurement_types: Vec<MeasurementType> = vec![
213            MeasurementType::Sha256,
214            MeasurementType::Sha384,
215            MeasurementType::Sha512,
216        ];
217
218        for (measurement_type_u16, measurement_type) in measurement_types_u16
219            .into_iter()
220            .zip(measurement_types.into_iter())
221        {
222            assert_eq!(
223                <u16 as TryInto<MeasurementType>>::try_into(measurement_type_u16).unwrap(),
224                measurement_type
225            );
226        }
227    }
228
229    #[test]
230    fn measurement_conversion_ok() {
231        let boot_measurement = BootMeasurement {
232            measurement_value: boot_measurement_value(MEASUREMENT_VALUE_MIN_SIZE),
233            metadata: BootMeasurementMetadata {
234                measurement_type: 0,
235                signer_id: boot_signer_id(MAX_HW_HASH_VALUE_SIZE),
236                sw_type: boot_sw_type(MAX_HW_SW_TYPE_SIZE),
237                sw_version: boot_sw_version(MAX_HW_SW_VERSION_SIZE),
238            },
239        };
240
241        let measurement: Measurement = boot_measurement.clone().try_into().unwrap();
242        assert_eq!(boot_measurement.measurement_value, measurement.value);
243
244        let boot_metadata = &boot_measurement.metadata;
245        let metadata = &measurement.metadata;
246
247        assert_eq!(boot_metadata.signer_id, metadata.signer_id);
248        assert_eq!(
249            <u16 as TryInto<MeasurementType>>::try_into(boot_metadata.measurement_type).unwrap(),
250            metadata.algorithm
251        );
252        assert_eq!(
253            &boot_metadata.sw_type[..],
254            &metadata.sw_type[..MAX_HW_SW_TYPE_SIZE].as_bytes()[..]
255        );
256        assert_eq!(boot_metadata.sw_version, metadata.sw_version.as_bytes());
257    }
258
259    #[test]
260    fn measurement_conversion_too_short_value() {
261        let boot_measurement = BootMeasurement {
262            // too short
263            measurement_value: boot_measurement_value(MEASUREMENT_VALUE_MIN_SIZE - 1),
264            metadata: BootMeasurementMetadata {
265                measurement_type: 0,
266                signer_id: boot_signer_id(MAX_HW_HASH_VALUE_SIZE),
267                sw_type: boot_sw_type(MAX_HW_SW_TYPE_SIZE),
268                sw_version: boot_sw_version(MAX_HW_SW_VERSION_SIZE),
269            },
270        };
271
272        assert_eq!(
273            <BootMeasurement as TryInto<Measurement>>::try_into(boot_measurement).unwrap_err(),
274            MeasurementError::InvalidData(MEASUREMENT_VALUE_SIZE_ERROR_MSG)
275        );
276    }
277
278    #[test]
279    fn measurement_conversion_too_short_signer_id() {
280        let boot_measurement = BootMeasurement {
281            measurement_value: boot_measurement_value(MAX_HW_HASH_VALUE_SIZE),
282            metadata: BootMeasurementMetadata {
283                measurement_type: 0,
284                // too short
285                signer_id: boot_signer_id(SIGNER_ID_MIN_SIZE - 1),
286                sw_type: boot_sw_type(MAX_HW_SW_TYPE_SIZE),
287                sw_version: boot_sw_version(MAX_HW_SW_VERSION_SIZE),
288            },
289        };
290
291        assert_eq!(
292            <BootMeasurement as TryInto<Measurement>>::try_into(boot_measurement).unwrap_err(),
293            MeasurementError::InvalidData(SIGNER_ID_SIZE_ERROR_MSG)
294        );
295    }
296
297    #[test]
298    fn measurement_conversion_bad_measurement_type() {
299        let boot_measurement = BootMeasurement {
300            measurement_value: boot_measurement_value(MAX_HW_HASH_VALUE_SIZE),
301            metadata: BootMeasurementMetadata {
302                // doesn't map to any proper value
303                measurement_type: 10,
304                signer_id: boot_signer_id(MAX_HW_HASH_VALUE_SIZE),
305                sw_type: boot_sw_type(MAX_HW_SW_TYPE_SIZE),
306                sw_version: boot_sw_version(MAX_HW_SW_VERSION_SIZE),
307            },
308        };
309
310        assert_eq!(
311            <BootMeasurement as TryInto<Measurement>>::try_into(boot_measurement).unwrap_err(),
312            MeasurementError::InvalidData(MEASUREMENT_TYPE_ERROR_MSG)
313        );
314    }
315}