Skip to main content

islet_rmm/rmi/
metadata.rs

1use core::ffi::CStr;
2use p384::{
3    ecdsa::{signature::Verifier, Signature, VerifyingKey},
4    elliptic_curve::generic_array::GenericArray,
5    EncodedPoint,
6};
7
8use super::{HASH_ALGO_SHA256, HASH_ALGO_SHA512};
9use crate::granule::GRANULE_SIZE;
10use crate::measurement::{Measurement, MEASUREMENTS_SLOT_MAX_SIZE};
11use crate::rmi::error::Error;
12
13const FMT_VERSION: usize = 1;
14pub const REALM_ID_SIZE: usize = 128;
15pub const P384_PUBLIC_KEY_SIZE: usize = 96;
16const P384_SIGNATURE_SIZE: usize = P384_PUBLIC_KEY_SIZE;
17
18#[allow(dead_code)]
19const P385_SIGNATURE_POINT_SIZE: usize = P384_SIGNATURE_SIZE / 2;
20#[allow(dead_code)]
21const SHA_384_HASH_SIZE: usize = 48;
22
23const METADATA_HASH_SHA_256: usize = 0x01;
24const METADATA_HASH_SHA_512: usize = 0x02;
25
26const REALM_METADATA_HEADER_SIZE: usize = 0x150;
27#[allow(dead_code)]
28const REALM_METADATA_SIGNED_SIZE: usize = 0x1B0;
29const REALM_METADATA_UNUSED_SIZE: usize = 0xE50;
30
31#[derive(Debug, Clone, Copy)]
32#[repr(C)]
33pub struct IsletRealmMetadata {
34    fmt_version: usize,
35    realm_id: [u8; REALM_ID_SIZE],
36    rim: [u8; MEASUREMENTS_SLOT_MAX_SIZE],
37    hash_algo: usize,
38    svn: usize,
39    version_major: usize,
40    version_minor: usize,
41    version_patch: usize,
42    public_key: [u8; P384_PUBLIC_KEY_SIZE],
43    signature: [u8; P384_SIGNATURE_SIZE],
44    _unused: [u8; REALM_METADATA_UNUSED_SIZE],
45}
46
47const _: () = assert!(core::mem::size_of::<IsletRealmMetadata>() == GRANULE_SIZE);
48const _: () = assert!(core::mem::size_of::<IsletRealmMetadata>() >= REALM_METADATA_SIGNED_SIZE);
49
50const _: () = assert!(core::mem::offset_of!(IsletRealmMetadata, fmt_version) == 0x00);
51const _: () = assert!(core::mem::offset_of!(IsletRealmMetadata, realm_id) == 0x08);
52const _: () = assert!(core::mem::offset_of!(IsletRealmMetadata, rim) == 0x88);
53const _: () = assert!(core::mem::offset_of!(IsletRealmMetadata, hash_algo) == 0xc8);
54const _: () = assert!(core::mem::offset_of!(IsletRealmMetadata, svn) == 0xd0);
55const _: () = assert!(core::mem::offset_of!(IsletRealmMetadata, version_major) == 0xd8);
56const _: () = assert!(core::mem::offset_of!(IsletRealmMetadata, version_minor) == 0xe0);
57const _: () = assert!(core::mem::offset_of!(IsletRealmMetadata, version_patch) == 0xe8);
58const _: () = assert!(core::mem::offset_of!(IsletRealmMetadata, public_key) == 0xf0);
59
60impl IsletRealmMetadata {
61    fn realm_id_as_str(&self) -> Option<&str> {
62        let Ok(cstr) = CStr::from_bytes_until_nul(&self.realm_id) else {
63            return None;
64        };
65        let Ok(s) = cstr.to_str() else {
66            return None;
67        };
68        Some(s)
69    }
70
71    pub fn dump(&self) {
72        debug!("fmt_version: {:#010x}", self.fmt_version);
73        debug!(
74            "realm_id: {}",
75            self.realm_id_as_str().unwrap_or("INVALID REALM ID")
76        );
77        debug!("rim: {}", hex::encode(self.rim));
78        debug!("hash_algo: {:#010x}", self.hash_algo);
79        debug!("svn: {:#010x}", self.svn);
80        debug!("version_major: {:#010x}", self.version_major);
81        debug!("version_minor: {:#010x}", self.version_minor);
82        debug!("version_patch: {:#010x}", self.version_patch);
83        debug!("public_key: {}", hex::encode(self.public_key));
84        debug!("signature: {}", hex::encode(self.signature));
85    }
86
87    fn verifying_key(&self) -> core::result::Result<VerifyingKey, Error> {
88        let point = EncodedPoint::from_untagged_bytes(GenericArray::from_slice(&self.public_key));
89        VerifyingKey::from_encoded_point(&point).or(Err(Error::RmiErrorInput))
90    }
91
92    fn signature(&self) -> core::result::Result<Signature, Error> {
93        Signature::from_slice(&self.signature).or(Err(Error::RmiErrorInput))
94    }
95
96    fn header_as_u8_slice(&self) -> &[u8] {
97        let slice = unsafe {
98            core::slice::from_raw_parts(
99                (self as *const Self) as *const u8,
100                core::mem::size_of::<Self>(),
101            )
102        };
103        &slice[..REALM_METADATA_HEADER_SIZE]
104    }
105
106    pub fn verify_signature(&self) -> core::result::Result<(), Error> {
107        let verifying_key = self.verifying_key()?;
108        let signature = self.signature()?;
109        let data = self.header_as_u8_slice();
110
111        verifying_key
112            .verify(data, &signature)
113            .or(Err(Error::RmiErrorInput))
114    }
115
116    pub fn validate(&self) -> core::result::Result<(), Error> {
117        if self.fmt_version != FMT_VERSION {
118            error!(
119                "Metadata format version {} is not supported!",
120                self.fmt_version
121            );
122            Err(Error::RmiErrorInput)?
123        }
124
125        if self.svn == 0 {
126            error!("SVN number should be greater than zero");
127            Err(Error::RmiErrorInput)?
128        }
129
130        if ![METADATA_HASH_SHA_256, METADATA_HASH_SHA_512].contains(&self.hash_algo) {
131            error!("Hash algorithm is invalid {}", self.hash_algo);
132            Err(Error::RmiErrorInput)?
133        }
134
135        let is_printable_ascii = |&c| c >= b' ' && c <= b'~';
136
137        if !self
138            .realm_id
139            .iter()
140            .take_while(|&c| *c != b'\0')
141            .all(is_printable_ascii)
142        {
143            error!("Realm id is invalid");
144            Err(Error::RmiErrorInput)?
145        }
146
147        Ok(())
148    }
149
150    pub fn equal_rd_rim(&self, rim: &Measurement) -> bool {
151        rim.as_slice() == self.rim
152    }
153
154    pub fn equal_rd_hash_algo(&self, hash_algo: u8) -> bool {
155        let converted_algo = match hash_algo {
156            HASH_ALGO_SHA256 => METADATA_HASH_SHA_256,
157            HASH_ALGO_SHA512 => METADATA_HASH_SHA_512,
158            _ => unreachable!(),
159        };
160
161        converted_algo == self.hash_algo
162    }
163
164    // for sealing key derivation
165
166    pub fn svn(&self) -> usize {
167        self.svn
168    }
169
170    pub fn public_key(&self) -> &[u8; P384_PUBLIC_KEY_SIZE] {
171        &self.public_key
172    }
173
174    pub fn realm_id(&self) -> &[u8; REALM_ID_SIZE] {
175        &self.realm_id
176    }
177}
178
179// This should work but I don't like this traits
180impl vmsa::guard::Content for IsletRealmMetadata {}
181impl safe_abstraction::raw_ptr::RawPtr for IsletRealmMetadata {}
182impl safe_abstraction::raw_ptr::SafetyChecked for IsletRealmMetadata {}
183impl safe_abstraction::raw_ptr::SafetyAssured for IsletRealmMetadata {
184    fn is_initialized(&self) -> bool {
185        true
186    }
187
188    fn verify_ownership(&self) -> bool {
189        true
190    }
191}