Skip to main content

islet_hes/
lib.rs

1#![no_std]
2//! Islet HES library.
3
4extern crate alloc;
5
6// Submodule implementing the attestation functionality.
7mod attestation;
8// Submodule containing hardware data trait.
9mod hw;
10// Submodule implementing the measured boot functionality.
11mod measured_boot;
12// Common functionality.
13pub(crate) mod utils;
14
15use core::{fmt::Debug, str::from_utf8};
16
17use alloc::{string::ToString, vec::Vec};
18use ciborium::into_writer;
19use coset::CoseSign1;
20use key_derivation::generate_seed;
21use tinyvec::ArrayVec;
22
23pub use measured_boot::{
24    Measurement, MeasurementError, MeasurementMetaData, MeasurementMgr, MeasurementType, SWType,
25    SWVersion, SignerHash, MEASUREMENT_VALUE_MAX_SIZE, MEASUREMENT_VALUE_MIN_SIZE,
26    NUM_OF_MEASUREMENT_SLOTS, SIGNER_ID_MAX_SIZE, SIGNER_ID_MIN_SIZE, SW_TYPE_MAX_SIZE,
27    VERSION_MAX_SIZE,
28};
29
30pub use hw::{
31    BootMeasurement, BootMeasurementMetadata, HWAsymmetricKey, HWData, HWHash, HWSWType,
32    HWSWVersion, HWSymmetricKey,
33};
34
35pub use attestation::{
36    calculate_public_key_hash, AttestationError, AttestationMgr, ECCFamily, HWClaims, HashAlgo,
37    KeyBits, KeyMaterialData,
38};
39
40pub const MAX_HASH_VALUE_SIZE: usize = 64;
41pub type ValueHash = ArrayVec<[u8; MAX_HASH_VALUE_SIZE]>;
42
43/// Possible PSA lifecycle states (major):
44pub mod security_lifecycle {
45    pub const UNKNOWN: u32 = 0x1000;
46    pub const PSA_ROT_PROVISIONNING: u32 = 0x2000;
47    pub const SECURED: u32 = 0x3000;
48    pub const NON_PSA_ROT_DEBUG: u32 = 0x4000;
49    pub const RECOVERABLE_PSA_ROT_DEBUG: u32 = 0x5000;
50    pub const DECOMISSIONED: u32 = 0x6000;
51}
52
53pub struct IsletHES {
54    measured_boot_mgr: MeasurementMgr,
55    attestation_mgr: AttestationMgr,
56    lcs: u32,
57    huk: Vec<u8>,
58}
59
60#[derive(Debug)]
61pub enum IsletHESError {
62    /// Implementations may use this error code if none of the other is applicable.
63    GenericError,
64    /// The parameters passed to the function are invalid.
65    InvalidArgument,
66    /// Measurement signer_id doesn't match for extend operation
67    NotPermitted,
68    /// Slot_id of a Measurement is not populated
69    DoesNotExist,
70    /// Slot_id of a Measurement is lock for extend operation
71    BadState,
72    /// Requested key size of DAK is not supported
73    NotSupported,
74}
75
76impl From<MeasurementError> for IsletHESError {
77    fn from(value: MeasurementError) -> Self {
78        match value {
79            MeasurementError::BadState => Self::BadState,
80            MeasurementError::DoesNotExist => Self::DoesNotExist,
81            MeasurementError::InvalidArgument => Self::InvalidArgument,
82            MeasurementError::InvalidData(_) => Self::InvalidArgument,
83            MeasurementError::NotPermitted => Self::NotPermitted,
84        }
85    }
86}
87
88impl From<AttestationError> for IsletHESError {
89    fn from(value: AttestationError) -> Self {
90        match value {
91            AttestationError::InvalidArgument => Self::InvalidArgument,
92            AttestationError::GenericError => Self::GenericError,
93            AttestationError::NotSupported => Self::NotSupported,
94        }
95    }
96}
97
98impl IsletHES {
99    /// Initializes IsletHes with data for [`HWData`] interface
100    pub fn init<H: HWData>(hw_data: H) -> Result<Self, IsletHESError>
101    where
102        <H as HWData>::Error: Debug,
103    {
104        let measured_boot_mgr = MeasurementMgr::init(
105            hw_data
106                .boot_measurements()
107                .map_err(|_| IsletHESError::InvalidArgument)?,
108        )?;
109
110        let profile_definition = match hw_data
111            .profile_definition()
112            .map_err(|_| IsletHESError::InvalidArgument)?
113        {
114            Some(p) => Some(
115                from_utf8(&p)
116                    .map_err(|_| IsletHESError::InvalidArgument)?
117                    .to_string(),
118            ),
119            None => None,
120        };
121
122        let verification_service_url = match hw_data
123            .verification_service_url()
124            .map_err(|_| IsletHESError::InvalidArgument)?
125        {
126            Some(p) => Some(
127                from_utf8(&p)
128                    .map_err(|_| IsletHESError::InvalidArgument)?
129                    .to_string(),
130            ),
131            None => None,
132        };
133
134        let security_lifecycle = hw_data
135            .security_lifecycle()
136            .map_err(|_| IsletHESError::InvalidArgument)?;
137
138        let attestation_mgr = AttestationMgr::init(
139            KeyMaterialData {
140                hash: hw_data
141                    .bl_hash()
142                    .map_err(|_| IsletHESError::InvalidArgument)?,
143                guk: hw_data.guk().map_err(|_| IsletHESError::InvalidArgument)?,
144            },
145            HWClaims {
146                implementation_id: hw_data
147                    .implementation_id()
148                    .map_err(|_| IsletHESError::InvalidArgument)?,
149                security_lifecycle,
150                profile_definition,
151                verification_service_url,
152                platform_config: hw_data
153                    .platform_config()
154                    .map_err(|_| IsletHESError::InvalidArgument)?,
155            },
156        );
157
158        Ok(IsletHES {
159            measured_boot_mgr,
160            attestation_mgr,
161            lcs: security_lifecycle,
162            huk: hw_data
163                .huk()
164                .map_err(|_| IsletHESError::InvalidArgument)?
165                .to_vec(),
166        })
167    }
168
169    /// Resets the measurements database and unmarks DAK key as generated
170    pub fn reset<H: HWData>(&mut self, hw_data: H) -> Result<(), IsletHESError> {
171        self.measured_boot_mgr = MeasurementMgr::init(
172            hw_data
173                .boot_measurements()
174                .map_err(|_| IsletHESError::InvalidArgument)?,
175        )?;
176
177        self.attestation_mgr.reset();
178        Ok(())
179    }
180
181    /// Returns measurement metadata, value and locked attribute from given slot_id.
182    /// Returns [`IsletHESError::InvalidArgument`], when slot_id is out of bounds.
183    /// Returns [`IsletHESError::DoesNotExist`], when is not populated.
184    pub fn read_measurement(&self, slot_id: usize) -> Result<(&Measurement, bool), IsletHESError> {
185        Ok(self.measured_boot_mgr.read_measurement(slot_id)?)
186    }
187
188    /// Extends measurement with updated metadata, value and locked attribute in given slot_id.
189    /// Returns [`IsletHESError::InvalidArgument`], when slot_id is out of bounds.
190    /// Returns [`IsletHESError::BadState`], when measurement is locked.
191    /// Returns [`IsletHESError::NotPermitted`], when measurements signer id's
192    /// and algorithm do not match.
193    pub fn extend_measurement(
194        &mut self,
195        slot_id: usize,
196        measurement: Measurement,
197        lock: bool,
198    ) -> Result<(), IsletHESError> {
199        Ok(self
200            .measured_boot_mgr
201            .extend_measurement(slot_id, measurement, lock)?)
202    }
203
204    fn fetch_current_measurements(&self) -> Result<Vec<Measurement>, IsletHESError> {
205        let mut measurements = Vec::new();
206        for i in 0..measured_boot::NUM_OF_MEASUREMENT_SLOTS {
207            match self.measured_boot_mgr.read_measurement(i) {
208                Ok((measurement, _)) => measurements.push(measurement.clone()),
209                Err(MeasurementError::DoesNotExist) => continue,
210                Err(e) => return Err(e.into()),
211            }
212        }
213        Ok(measurements)
214    }
215
216    /// Generates DAK with [`ECCFamily`] and uses `measurements` ([`Measurement`])
217    /// as salt in the process.
218    /// Returns bytes of a scalar primitive, which can be used to recreate DAK Private Key.
219    /// [`HashAlgo`] is used for verification process, when `get_platform_token` is called.
220    /// Returns [`IsletHESError::GenericError`], when CBOR or crypto operation fails.
221    pub fn get_delegated_key(
222        &mut self,
223        ecc_family: ECCFamily,
224        key_bits: KeyBits,
225        hash_algo: HashAlgo,
226    ) -> Result<Vec<u8>, IsletHESError> {
227        let measurements = self.fetch_current_measurements()?;
228
229        Ok(self.attestation_mgr.get_delegated_key(
230            ecc_family,
231            key_bits,
232            hash_algo,
233            &measurements,
234        )?)
235    }
236
237    /// Creates a tagged [`CoseSign1`] of the platform token.
238    /// `dak_pub_hash` must be a valid hash of DAK Public Key using [`HashAlgo`] passed
239    /// in [`IsletHES::get_delegated_key`].
240    /// Returns [`IsletHESError::GenericError`], when CBOR or crypto operation fails.
241    /// Returns [`IsletHESError::InvalidArgument`], when DAK was not requsted before
242    /// this operation, or `dak_pub_hash` is not a valid hash of DAK Public Key.
243    pub fn get_platform_token(&mut self, dak_pub_hash: &[u8]) -> Result<CoseSign1, IsletHESError> {
244        let measurements = self.fetch_current_measurements()?;
245
246        Ok(self
247            .attestation_mgr
248            .get_platform_token(dak_pub_hash, &measurements)?)
249    }
250
251    /// Creates an authority based Virtual HUK (VHUK_A).
252    /// This key is bound to the authority data, the type of firmware components
253    /// and HUK. This makes it immune to firmware updates.
254    /// This function should not return an error.
255    pub fn get_authority_vhuk(&mut self) -> Result<Vec<u8>, IsletHESError> {
256        let measurements = self.fetch_current_measurements()?;
257
258        let mut authority_info = Vec::new();
259        for Measurement { metadata, .. } in measurements {
260            let MeasurementMetaData {
261                signer_id, sw_type, ..
262            } = metadata;
263            authority_info.extend_from_slice(signer_id.as_slice());
264            authority_info.extend_from_slice(sw_type.as_bytes());
265        }
266
267        let mut context = Vec::new();
268        context.extend(&authority_info);
269        context.extend(self.lcs.to_ne_bytes());
270
271        Ok(generate_seed(&context, &self.huk, b"VHUK_A"))
272    }
273
274    /// Creates a measurement based Virtual HUK (VHUK_M).
275    /// This key is bound to the boot measurements of firmware components and HUK.
276    /// It is bound to a specific version of CCA Platform firmware.
277    pub fn get_measurement_vhuk(&mut self) -> Result<Vec<u8>, IsletHESError> {
278        let measurements = self.fetch_current_measurements()?;
279        let encoded_measurements = utils::encode_measurements(&measurements);
280
281        let mut context = Vec::new();
282        into_writer(&encoded_measurements, &mut context)
283            .map_err(|_| IsletHESError::GenericError)?;
284        context.extend(self.lcs.to_ne_bytes());
285
286        Ok(generate_seed(&context, &self.huk, b"VHUK_M"))
287    }
288}