strat9_kernel/crypto.rs
1//! Cryptographic verification for CMOD modules.
2//!
3//! Provides Ed25519 signature verification to ensure module integrity.
4//! The kernel maintains a key store of trusted public keys; modules must
5//! be signed by one of these keys to be loaded.
6//!
7//! # Security Design
8//!
9//! The signed payload is **code + data only** (everything after the header).
10//! The header (containing key_id and signature) is NOT part of the signed
11//! payload. This prevents an attacker from using a known key_id to trick
12//! the kernel into looking up a legitimate key while verifying a malicious
13//! signature.
14//!
15//! Verification flow:
16//! 1. Extract key_id (8 bytes) and signature (64 bytes) from header
17//! 2. Look up the public key by key_id
18//! 3. Verify signature over: module_data[HEADER_SIZE..]
19//! (code + data, excluding the header)
20
21/* TODO: re-enable once LLVM backend crash is fixed
22use ed25519_dalek::{Signature, Verifier, VerifyingKey};
23*/
24use spin::Mutex;
25
26/// Size of an Ed25519 public key in bytes.
27pub const ED25519_PUBLIC_KEY_SIZE: usize = 32;
28/// Size of an Ed25519 signature in bytes.
29pub const ED25519_SIGNATURE_SIZE: usize = 64;
30/// Size of a key ID.
31pub const KEY_ID_SIZE: usize = 8;
32
33/// Result of a signature verification attempt.
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35pub enum VerifyResult {
36 /// Signature is valid.
37 Valid,
38 /// Signature is invalid (tampered module or wrong key).
39 InvalidSignature,
40 /// No key found matching the key_id in the module header.
41 KeyNotFound,
42 /// The signature field is all zeros (unsigned module).
43 Unsigned,
44}
45
46impl core::fmt::Display for VerifyResult {
47 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
48 match self {
49 VerifyResult::Valid => write!(f, "valid"),
50 VerifyResult::InvalidSignature => write!(f, "invalid signature"),
51 VerifyResult::KeyNotFound => write!(f, "key not found"),
52 VerifyResult::Unsigned => write!(f, "unsigned module"),
53 }
54 }
55}
56
57// ============================================================================
58// Trusted Key Store
59// ============================================================================
60
61/// Global trusted key store protected by a spinlock.
62static TRUSTED_KEYS: Mutex<alloc::vec::Vec<TrustedKey>> = Mutex::new(alloc::vec::Vec::new());
63
64/// A trusted signing key with its identifier.
65#[derive(Debug, Clone)]
66pub struct TrustedKey {
67 /// 8-byte key identifier (matches `key_id` in CMOD header).
68 pub id: [u8; KEY_ID_SIZE],
69 /// Ed25519 public key (32 bytes).
70 pub public_key: [u8; ED25519_PUBLIC_KEY_SIZE],
71 /// Human-readable label for this key.
72 pub label: &'static str,
73}
74
75/// Register a trusted signing key.
76///
77/// Called during boot to populate the key store. In production, these
78/// keys would be provisioned via a secure channel or hardware root of trust.
79pub fn register_trusted_key(
80 id: [u8; KEY_ID_SIZE],
81 public_key: [u8; ED25519_PUBLIC_KEY_SIZE],
82 label: &'static str,
83) {
84 let mut keys = TRUSTED_KEYS.lock();
85 if keys.iter().any(|k| k.id == id) {
86 log::warn!("[crypto] duplicate key id {:02x?}, skipping", id);
87 return;
88 }
89 keys.push(TrustedKey {
90 id,
91 public_key,
92 label,
93 });
94 log::info!(
95 "[crypto] registered trusted key: {} (id={:02x?})",
96 label,
97 id
98 );
99}
100
101/// Remove a trusted signing key by ID.
102pub fn remove_trusted_key(id: [u8; KEY_ID_SIZE]) -> bool {
103 let mut keys = TRUSTED_KEYS.lock();
104 let len_before = keys.len();
105 keys.retain(|k| k.id != id);
106 keys.len() < len_before
107}
108
109/// Number of registered trusted keys.
110pub fn trusted_key_count() -> usize {
111 TRUSTED_KEYS.lock().len()
112}
113
114/// Check if a key ID is registered as trusted.
115pub fn is_key_trusted(id: &[u8; KEY_ID_SIZE]) -> bool {
116 TRUSTED_KEYS.lock().iter().any(|k| k.id == *id)
117}
118
119/// Lookup a trusted key by its ID.
120fn find_trusted_key(id: &[u8; KEY_ID_SIZE]) -> Option<TrustedKey> {
121 TRUSTED_KEYS.lock().iter().find(|k| k.id == *id).cloned()
122}
123
124// ============================================================================
125// Ed25519 Verification
126// ============================================================================
127
128/// Verify an Ed25519 signature over data.
129///
130/// # Arguments
131/// * `key_id` - 8-byte identifier for the signing key
132/// * `signature` - 64-byte Ed25519 signature
133/// * `data` - The data that was signed (code + data sections)
134///
135/// # Returns
136/// * `VerifyResult::Valid` if signature is valid
137/// * `VerifyResult::KeyNotFound` if no trusted key matches `key_id`
138/// * `VerifyResult::InvalidSignature` if verification fails
139/// * `VerifyResult::Unsigned` if signature is all zeros
140pub fn verify_signature(
141 key_id: &[u8; KEY_ID_SIZE],
142 signature: &[u8; ED25519_SIGNATURE_SIZE],
143 data: &[u8],
144) -> VerifyResult {
145 if signature.iter().all(|&b| b == 0) {
146 return VerifyResult::Unsigned;
147 }
148
149 let key = match find_trusted_key(key_id) {
150 Some(k) => k,
151 None => return VerifyResult::KeyNotFound,
152 };
153
154 /* TODO: re-enable ed25519-dalek verification once LLVM backend crash is fixed.
155 let verifying_key = match VerifyingKey::from_bytes(&key.public_key) {
156 Ok(vk) => vk,
157 Err(_) => {
158 log::error!("[crypto] corrupt public key for id {:02x?}", key_id);
159 return VerifyResult::InvalidSignature;
160 }
161 };
162
163 let sig = Signature::from_bytes(signature);
164
165 let ok = verifying_key.verify(data, &sig).is_ok();
166 if ok {
167 log::debug!(
168 "[crypto] signature OK for key {} (id={:02x?})",
169 key.label,
170 key_id
171 );
172 VerifyResult::Valid
173 } else {
174 log::warn!("[crypto] signature FAILED for key id {:02x?}", key_id);
175 VerifyResult::InvalidSignature
176 }
177 */
178
179 // Stub: accept all signatures from trusted keys
180 let _ = data;
181 log::warn!(
182 "[crypto] ed25519 verification DISABLED (LLVM backend bug) : accepting key {} (id={:02x?})",
183 key.label,
184 key_id
185 );
186 VerifyResult::Valid
187}
188
189/// Verify a CMOD module's signature.
190///
191/// Extracts key_id and signature from the raw header, then verifies
192/// the signature over `payload` (which must be code+data, NOT the header).
193///
194/// # Arguments
195/// * `header_bytes` - The raw CMOD header
196/// * `key_id_offset` - Byte offset of key_id in header
197/// * `sig_offset` - Byte offset of signature in header
198/// * `payload` - The data that was signed (code + data sections)
199pub fn verify_cmod_signature(
200 header_bytes: &[u8],
201 key_id_offset: usize,
202 sig_offset: usize,
203 payload: &[u8],
204) -> VerifyResult {
205 // Bounds: key_id must fit before signature, signature must fit in header.
206 if key_id_offset + KEY_ID_SIZE > sig_offset {
207 return VerifyResult::InvalidSignature;
208 }
209 if sig_offset + ED25519_SIGNATURE_SIZE > header_bytes.len() {
210 return VerifyResult::InvalidSignature;
211 }
212
213 let mut key_id = [0u8; KEY_ID_SIZE];
214 key_id.copy_from_slice(&header_bytes[key_id_offset..key_id_offset + KEY_ID_SIZE]);
215
216 let mut signature = [0u8; ED25519_SIGNATURE_SIZE];
217 signature.copy_from_slice(&header_bytes[sig_offset..sig_offset + ED25519_SIGNATURE_SIZE]);
218
219 verify_signature(&key_id, &signature, payload)
220}
221
222// ============================================================================
223// Key Provisioning (Boot-time initialization)
224// ============================================================================
225
226/// Initialize the crypto subsystem.
227///
228/// Called during kernel boot. In production, keys are provisioned via
229/// secure boot chain or hardware root of trust. No default keys are
230/// embedded : unsigned or unverifiable modules are rejected.
231pub fn init() {
232 log::info!("[init] Crypto subsystem...");
233 let count = trusted_key_count();
234 if count == 0 {
235 log::warn!("[init] No trusted keys : module signing verification disabled");
236 } else {
237 log::info!("[init] Crypto subsystem ready ({} trusted key(s))", count);
238 }
239}
240
241// ============================================================================
242// Unit tests
243// ============================================================================
244
245#[cfg(test)]
246mod tests {
247 use super::*;
248
249 #[test]
250 fn test_unsigned_module_detection() {
251 let sig = [0u8; ED25519_SIGNATURE_SIZE];
252 let key_id = [0u8; KEY_ID_SIZE];
253 assert_eq!(verify_signature(&key_id, &sig, &[]), VerifyResult::Unsigned);
254 }
255
256 #[test]
257 fn test_key_not_found() {
258 let sig = [1u8; ED25519_SIGNATURE_SIZE];
259 let key_id = [0xff; KEY_ID_SIZE];
260 assert_eq!(
261 verify_signature(&key_id, &sig, &[]),
262 VerifyResult::KeyNotFound
263 );
264 }
265}