Skip to main content

islet_rmm/rsi/
sealing.rs

1use core::ffi::CStr;
2
3use crate::granule::GranuleState;
4use crate::measurement::{Measurement, MEASUREMENTS_SLOT_MAX_SIZE, MEASUREMENTS_SLOT_RIM};
5use crate::realm::rd::{Rd, RPV_SIZE};
6use crate::rmi::error::Error;
7use crate::rmi::metadata::{IsletRealmMetadata, P384_PUBLIC_KEY_SIZE, REALM_ID_SIZE};
8use crate::rmm_el3::{vhuk_a, vhuk_m};
9use crate::{get_granule, get_granule_if};
10
11const RSI_ISLET_USE_VHUK_M: usize = 0x1 << 0;
12const RSI_ISLET_SLK_RIM: usize = 0x1 << 1;
13const RSI_ISLET_SLK_REALM_ID: usize = 0x1 << 2;
14const RSI_ISLET_SLK_SVN: usize = 0x1 << 3;
15
16pub const SEALING_KEY_SIZE: usize = 32;
17
18const SALT: [u8; 32] = [
19    0xd5, 0x77, 0x5f, 0x52, 0x4a, 0xce, 0x32, 0x21, 0xce, 0x77, 0x1e, 0xd2, 0x74, 0xbb, 0x74, 0xa4,
20    0x60, 0xce, 0x3f, 0xb9, 0x74, 0x9c, 0xe3, 0x7d, 0x0a, 0xe6, 0xd2, 0xe9, 0x07, 0xf8, 0xb5, 0x4b,
21];
22
23#[repr(C, packed)]
24struct KdfInfo {
25    public_key: [u8; P384_PUBLIC_KEY_SIZE],
26    realm_id: [u8; REALM_ID_SIZE],
27    rpv: [u8; RPV_SIZE],
28    flags: usize,
29    rim: [u8; MEASUREMENTS_SLOT_MAX_SIZE],
30    hash_algo: u8,
31    svn: usize,
32}
33
34impl KdfInfo {
35    fn new() -> Self {
36        Self {
37            public_key: [0; P384_PUBLIC_KEY_SIZE],
38            realm_id: [0; REALM_ID_SIZE],
39            rpv: [0; RPV_SIZE],
40            flags: 0,
41            rim: [0; MEASUREMENTS_SLOT_MAX_SIZE],
42            hash_algo: 0,
43            svn: 0,
44        }
45    }
46
47    // TODO: use zeroize?
48    fn zeroize(&mut self) {
49        let addr = self as *mut Self;
50        unsafe {
51            core::ptr::write_bytes(addr as *mut u8, 0x0, core::mem::size_of::<Self>());
52        }
53    }
54
55    fn realm_id_as_str(&self) -> Option<&str> {
56        let Ok(cstr) = CStr::from_bytes_until_nul(&self.realm_id) else {
57            return None;
58        };
59        let Ok(s) = cstr.to_str() else {
60            return None;
61        };
62        Some(s)
63    }
64
65    fn dump(&self) {
66        debug!("KDF info");
67        debug!("public_key: {}", hex::encode(self.public_key));
68        debug!(
69            "realm_id: {}",
70            self.realm_id_as_str().unwrap_or("INVALID REALM ID")
71        );
72        debug!("rpv: {}", hex::encode(self.rpv));
73        let flags = self.flags; // not aligned
74        debug!("flags: {:#010x}", flags);
75        debug!("rim: {}", hex::encode(self.rim));
76        debug!("hash_algo: {:#04x}", self.hash_algo);
77        let svn = self.svn; // not aligned
78        debug!("svn: {:#010x}", svn);
79    }
80
81    fn as_u8_slice(&self) -> &[u8] {
82        unsafe {
83            core::slice::from_raw_parts(
84                (self as *const Self) as *const u8,
85                core::mem::size_of::<Self>(),
86            )
87        }
88    }
89
90    fn derive_sealing_key(
91        &self,
92        use_vhuk_m: bool,
93        okm: &mut [u8; SEALING_KEY_SIZE],
94    ) -> core::result::Result<(), Error> {
95        let ikm = if use_vhuk_m { vhuk_m() } else { vhuk_a() };
96        let info = self.as_u8_slice();
97
98        let hkdf = hkdf::Hkdf::<sha2::Sha256>::new(Some(&SALT), &ikm);
99        hkdf.expand(info, okm).or(Err(Error::RmiErrorInput))?;
100
101        Ok(())
102    }
103}
104
105pub fn realm_sealing_key(
106    rd: &Rd,
107    flags: usize,
108    svn: usize,
109    buf: &mut [u8; SEALING_KEY_SIZE],
110) -> core::result::Result<(), Error> {
111    debug!("flags: {:#010x}, svn: {:#010x}", flags, svn);
112
113    let mut info = KdfInfo::new();
114
115    info.rpv.copy_from_slice(rd.personalization_value());
116    info.flags = flags;
117
118    if let Some(meta_addr) = rd.metadata() {
119        let metadata_granule = get_granule_if!(meta_addr, GranuleState::Metadata)?;
120        let metadata_obj = metadata_granule.content::<IsletRealmMetadata>()?;
121
122        if flags & RSI_ISLET_SLK_SVN != 0 && metadata_obj.svn() < svn {
123            warn!("The SVN parameter is invalid!");
124            Err(Error::RmiErrorInput)?
125        }
126
127        info.public_key = *metadata_obj.public_key();
128
129        if flags & RSI_ISLET_SLK_REALM_ID != 0 {
130            info.realm_id = *metadata_obj.realm_id();
131        }
132
133        if flags & RSI_ISLET_SLK_SVN != 0 {
134            info.svn = svn;
135        }
136    }
137
138    // We allow realms not having a metadata block assigned
139    // to derive sealing keys. In that case, the RIM of the realm
140    // is always used as the key material.
141    if flags & RSI_ISLET_SLK_RIM != 0 || rd.metadata().is_none() {
142        let mut rim = Measurement::empty();
143        crate::rsi::measurement::read(rd, MEASUREMENTS_SLOT_RIM, &mut rim)?;
144        info.rim.copy_from_slice(rim.as_slice());
145    }
146
147    info.dump();
148    debug!(
149        "ikm type: {}",
150        if flags & RSI_ISLET_USE_VHUK_M != 0 {
151            "VHUK_M"
152        } else {
153            "VHUK_A"
154        }
155    );
156    info.derive_sealing_key(flags & RSI_ISLET_USE_VHUK_M != 0, buf)?;
157
158    // Clear the input key material
159    info.zeroize();
160
161    Ok(())
162}