1use std::collections::HashMap;
15use std::ffi::CStr;
16use std::ffi::c_void;
17use std::os::raw::c_char;
18
19use snafu::{ResultExt, ensure};
20
21use crate::backend::Options;
22use crate::error::{InvalidUTF8Snafu, NullPointerSnafu, Result};
23use crate::keystore::Keystore;
24
25pub enum FFIKeystore {}
28
29pub enum FFIKeyIterator {}
31
32pub type FFIKey = c_void;
35
36fn require<T>(ptr: *const T, param: &'static str) -> Result<()> {
41 if ptr.is_null() {
42 Err(NullPointerSnafu { param }.build())
43 } else {
44 Ok(())
45 }
46}
47
48fn cstring(cstr: *const c_char, param: &'static str) -> Result<String> {
51 require(cstr, param)?;
52 unsafe {
53 CStr::from_ptr(cstr)
54 .to_str()
55 .context(InvalidUTF8Snafu {})
56 .map(str::to_string)
57 }
58}
59
60fn options_from_ptr(_opts: *const c_void) -> Options {
65 HashMap::new()
71}
72
73fn cfm_keystore_create_(
76 out: *mut *mut FFIKeystore,
77 backend_name: *const c_char,
78 opts: *const c_void,
79) -> Result<()> {
80 require(out, "out")?;
81 require(backend_name, "backend_name")?;
82 let name = cstring(backend_name, "backend_name")?;
83 let opts = options_from_ptr(opts);
84 let ks = Keystore::new(&name, &opts)?;
85 unsafe {
88 *out = Box::into_raw(Box::new(ks)) as *mut FFIKeystore;
89 }
90 Ok(())
91}
92
93#[unsafe(no_mangle)]
98pub extern "C" fn cfm_keystore_create(
99 out: *mut *mut FFIKeystore,
100 backend_name: *const c_char,
101 opts: *const c_void,
102) -> u32 {
103 cfm_keystore_create_(out, backend_name, opts).map_or_else(|e| e.code(), |_| 0)
104}
105
106#[unsafe(no_mangle)]
108pub extern "C" fn cfm_keystore_destroy(ks: *mut FFIKeystore) {
109 if !ks.is_null() {
110 unsafe {
113 drop(Box::from_raw(ks as *mut Keystore));
114 }
115 }
116}
117
118fn keystore_mut(ks: *mut FFIKeystore) -> Result<&'static mut Keystore> {
121 require(ks, "ks")?;
122 Ok(unsafe { &mut *(ks as *mut Keystore) })
127}
128
129fn keystore_ref(ks: *mut FFIKeystore) -> Result<&'static Keystore> {
130 require(ks, "ks")?;
131 Ok(unsafe { &*(ks as *mut Keystore) })
132}
133
134#[unsafe(no_mangle)]
136pub extern "C" fn cfm_keystore_put_secret(
137 ks: *mut FFIKeystore,
138 module_id: *const c_char,
139 app_id: *const c_char,
140 key_id: *const c_char,
141 secret_key: *mut FFIKey,
142) -> u32 {
143 let inner = || -> Result<()> {
144 let ks = keystore_mut(ks)?;
145 let module = cstring(module_id, "module_id")?;
146 let app = cstring(app_id, "app_id")?;
147 let key_id = cstring(key_id, "key_id")?;
148 require(secret_key, "secret_key")?;
149 ks.put_secret(&module, &app, &key_id, secret_key)
150 };
151 inner().map_or_else(|e| e.code(), |_| 0)
152}
153
154#[unsafe(no_mangle)]
158pub extern "C" fn cfm_keystore_get_secret(
159 ks: *mut FFIKeystore,
160 module_id: *const c_char,
161 app_id: *const c_char,
162 key_id: *const c_char,
163 out: *mut *mut FFIKey,
164) -> u32 {
165 let inner = || -> Result<()> {
166 let ks = keystore_ref(ks)?;
167 let module = cstring(module_id, "module_id")?;
168 let app = cstring(app_id, "app_id")?;
169 let key_id = cstring(key_id, "key_id")?;
170 require(out, "out")?;
171 let key = ks.get_secret(&module, &app, &key_id)?;
172 unsafe {
173 *out = key as *mut FFIKey;
174 }
175 Ok(())
176 };
177 inner().map_or_else(|e| e.code(), |_| 0)
178}
179
180#[unsafe(no_mangle)]
185pub extern "C" fn cfm_keystore_put_public(
186 ks: *mut FFIKeystore,
187 module_id: *const c_char,
188 app_id: *const c_char,
189 identity: *const c_char,
190 public_key: *mut FFIKey,
191 signature: *const u8,
192 sig_len: u32,
193) -> u32 {
194 let inner = || -> Result<()> {
195 let ks = keystore_mut(ks)?;
196 let module = cstring(module_id, "module_id")?;
197 let app = cstring(app_id, "app_id")?;
198 let identity = cstring(identity, "identity")?;
199 require(public_key, "public_key")?;
200 let sig: &[u8] = if signature.is_null() {
203 ensure!(sig_len == 0, NullPointerSnafu { param: "signature" });
204 &[]
205 } else {
206 unsafe { std::slice::from_raw_parts(signature, sig_len as usize) }
209 };
210 ks.put_public(&module, &app, &identity, public_key, sig)
211 };
212 inner().map_or_else(|e| e.code(), |_| 0)
213}
214
215#[unsafe(no_mangle)]
219pub extern "C" fn cfm_keystore_get_public(
220 ks: *mut FFIKeystore,
221 module_id: *const c_char,
222 app_id: *const c_char,
223 identity: *const c_char,
224 out: *mut *mut FFIKey,
225) -> u32 {
226 let inner = || -> Result<()> {
227 let ks = keystore_ref(ks)?;
228 let module = cstring(module_id, "module_id")?;
229 let app = cstring(app_id, "app_id")?;
230 let identity = cstring(identity, "identity")?;
231 require(out, "out")?;
232 let (key, _sig) = ks.get_public(&module, &app, &identity)?;
233 unsafe {
234 *out = key as *mut FFIKey;
235 }
236 Ok(())
237 };
238 inner().map_or_else(|e| e.code(), |_| 0)
239}
240
241#[allow(clippy::missing_safety_doc)]
248#[unsafe(no_mangle)]
249pub unsafe extern "C" fn cfm_keystore_sign(
250 ks: *mut FFIKeystore,
251 module_id: *const c_char,
252 app_id: *const c_char,
253 key_id: *const c_char,
254 algorithm: *const c_char,
255 message: *const u8,
256 message_len: u32,
257 out: *mut *mut u8,
258 out_len: *mut u32,
259) -> u32 {
260 let inner = || -> Result<()> {
261 let ks = keystore_ref(ks)?;
262 let module = cstring(module_id, "module_id")?;
263 let app = cstring(app_id, "app_id")?;
264 let key_id = cstring(key_id, "key_id")?;
265 let algorithm = cstring(algorithm, "algorithm")?;
266 require(message, "message")?;
267 require(out, "out")?;
268 require(out_len, "out_len")?;
269 ensure!(
270 message_len > 0,
271 NullPointerSnafu {
272 param: "message_len"
273 }
274 );
275 let bytes = unsafe { std::slice::from_raw_parts(message, message_len as usize) };
278 let sig = ks.sign(&module, &app, &key_id, &algorithm, bytes)?;
279 let len = u32::try_from(sig.len()).map_err(|_| crate::error::Error::Wrapped {
280 message: "signature exceeds u32 length".to_string(),
281 })?;
282 unsafe {
285 *out = Box::into_raw(sig.into_boxed_slice()) as *mut u8;
286 *out_len = len;
287 }
288 Ok(())
289 };
290 inner().map_or_else(|e| e.code(), |_| 0)
291}
292
293struct IterEntry {
297 key: *mut c_void,
298 #[allow(dead_code)]
303 index: String,
304}
305
306pub struct KeyIterator {
311 entries: std::vec::IntoIter<IterEntry>,
312}
313
314fn cfm_keystore_enumerate_(
315 ks: *mut FFIKeystore,
316 module_id: *const c_char,
317 app_id: *const c_char,
318 compartment: u32,
319 out: *mut *mut FFIKeyIterator,
320) -> Result<()> {
321 let ks = keystore_ref(ks)?;
322 let module = cstring(module_id, "module_id")?;
323 let app = cstring(app_id, "app_id")?;
324 require(out, "out")?;
325 let comp = crate::backend::Compartment::from_wire(compartment)?;
326 let raw = ks.enumerate(&module, &app, comp)?;
327 let entries: Vec<IterEntry> = raw
328 .into_iter()
329 .map(|(key, index)| IterEntry { key, index })
330 .collect();
331 let it = KeyIterator {
332 entries: entries.into_iter(),
333 };
334 unsafe {
335 *out = Box::into_raw(Box::new(it)) as *mut FFIKeyIterator;
336 }
337 Ok(())
338}
339
340#[unsafe(no_mangle)]
345pub extern "C" fn cfm_keystore_enumerate(
346 ks: *mut FFIKeystore,
347 module_id: *const c_char,
348 app_id: *const c_char,
349 compartment: u32,
350 out: *mut *mut FFIKeyIterator,
351) -> u32 {
352 cfm_keystore_enumerate_(ks, module_id, app_id, compartment, out)
353 .map_or_else(|e| e.code(), |_| 0)
354}
355
356#[unsafe(no_mangle)]
360pub extern "C" fn cfm_keystore_iterator_next(
361 it: *mut FFIKeyIterator,
362 out: *mut *mut FFIKey,
363) -> u32 {
364 let inner = || -> Result<()> {
365 require(it, "it")?;
366 require(out, "out")?;
367 let it = unsafe { &mut *(it as *mut KeyIterator) };
369 match it.entries.next() {
370 Some(entry) => {
371 unsafe {
372 *out = entry.key as *mut FFIKey;
373 }
374 Ok(())
375 }
376 None => Err(crate::error::ValueNotFoundSnafu.build()),
377 }
378 };
379 inner().map_or_else(|e| e.code(), |_| 0)
380}
381
382#[unsafe(no_mangle)]
384pub extern "C" fn cfm_keystore_iterator_destroy(it: *mut FFIKeyIterator) {
385 if !it.is_null() {
386 unsafe {
388 drop(Box::from_raw(it as *mut KeyIterator));
389 }
390 }
391}
392
393#[cfg(test)]
394mod tests {
395 use super::*;
396 use crate::error::Error;
397 use std::ffi::CString;
398 use std::ptr;
399
400 fn sentinel(n: usize) -> *mut FFIKey {
401 n as *mut FFIKey
402 }
403
404 #[test]
409 fn create_and_destroy_memory_keystore() {
410 let mut ks: *mut FFIKeystore = ptr::null_mut();
411 let name = CString::new("memory").unwrap().into_raw();
412 let rc = cfm_keystore_create(&mut ks, name, ptr::null());
413 assert_eq!(rc, 0, "create should succeed");
414 assert!(!ks.is_null());
415 cfm_keystore_destroy(ks);
416 }
417
418 #[test]
419 fn create_unknown_backend_returns_error() {
420 let mut ks: *mut FFIKeystore = ptr::null_mut();
421 let name = CString::new("nope").unwrap().into_raw();
422 let rc = cfm_keystore_create(&mut ks, name, ptr::null());
423 assert_eq!(
424 rc,
425 Error::UnknownBackend {
426 name: String::new()
427 }
428 .code()
429 );
430 assert!(ks.is_null());
431 }
432
433 #[test]
434 fn put_get_secret_round_trip() {
435 let mut ks: *mut FFIKeystore = ptr::null_mut();
436 let name = CString::new("memory").unwrap().into_raw();
437 cfm_keystore_create(&mut ks, name, ptr::null());
438 let key = sentinel(0x1000);
439
440 let m = CString::new("mod").unwrap().into_raw();
441 let a = CString::new("app").unwrap().into_raw();
442 let k = CString::new("key-1").unwrap().into_raw();
443 let rc = cfm_keystore_put_secret(ks, m, a, k, key);
444 assert_eq!(rc, 0, "put_secret");
445
446 let m = CString::new("mod").unwrap().into_raw();
447 let a = CString::new("app").unwrap().into_raw();
448 let k = CString::new("key-1").unwrap().into_raw();
449 let mut out: *mut FFIKey = ptr::null_mut();
450 let rc = cfm_keystore_get_secret(ks, m, a, k, &mut out);
451 assert_eq!(rc, 0, "get_secret");
452 assert_eq!(out, key);
453
454 cfm_keystore_destroy(ks);
455 }
456
457 #[test]
458 fn get_secret_missing_returns_value_not_found() {
459 let mut ks: *mut FFIKeystore = ptr::null_mut();
460 let name = CString::new("memory").unwrap().into_raw();
461 cfm_keystore_create(&mut ks, name, ptr::null());
462
463 let m = CString::new("mod").unwrap().into_raw();
464 let a = CString::new("app").unwrap().into_raw();
465 let k = CString::new("missing").unwrap().into_raw();
466 let mut out: *mut FFIKey = ptr::null_mut();
467 let rc = cfm_keystore_get_secret(ks, m, a, k, &mut out);
468 assert_eq!(rc, Error::ValueNotFound.code());
469 }
470
471 #[test]
472 fn put_get_public_round_trip() {
473 let mut ks: *mut FFIKeystore = ptr::null_mut();
474 let name = CString::new("memory").unwrap().into_raw();
475 cfm_keystore_create(&mut ks, name, ptr::null());
476
477 let key = sentinel(0x2000);
478 let sig = [0xDEu8, 0xAD, 0xBE, 0xEF];
479 let m = CString::new("mod").unwrap().into_raw();
480 let a = CString::new("app").unwrap().into_raw();
481 let id = CString::new("email:alice@example.com").unwrap().into_raw();
482 let rc = cfm_keystore_put_public(ks, m, a, id, key, sig.as_ptr(), sig.len() as u32);
483 assert_eq!(rc, 0, "put_public");
484
485 let m = CString::new("mod").unwrap().into_raw();
486 let a = CString::new("app").unwrap().into_raw();
487 let id = CString::new("email:alice@example.com").unwrap().into_raw();
488 let mut out: *mut FFIKey = ptr::null_mut();
489 let rc = cfm_keystore_get_public(ks, m, a, id, &mut out);
490 assert_eq!(rc, 0, "get_public");
491 assert_eq!(out, key);
492
493 cfm_keystore_destroy(ks);
494 }
495
496 #[test]
497 fn enumerate_private_then_exhaust() {
498 let mut ks: *mut FFIKeystore = ptr::null_mut();
499 let name = CString::new("memory").unwrap().into_raw();
500 cfm_keystore_create(&mut ks, name, ptr::null());
501
502 for (kid, key) in [("a", sentinel(1)), ("b", sentinel(2))] {
503 let m = CString::new("mod").unwrap().into_raw();
504 let a = CString::new("app").unwrap().into_raw();
505 let k = CString::new(kid).unwrap().into_raw();
506 let rc = cfm_keystore_put_secret(ks, m, a, k, key);
507 assert_eq!(rc, 0);
508 }
509
510 let m = CString::new("mod").unwrap().into_raw();
511 let a = CString::new("app").unwrap().into_raw();
512 let mut it: *mut FFIKeyIterator = ptr::null_mut();
513 let rc = cfm_keystore_enumerate(ks, m, a, 1, &mut it); assert_eq!(rc, 0);
515
516 let mut seen = Vec::new();
517 loop {
518 let mut out: *mut FFIKey = ptr::null_mut();
519 let rc = cfm_keystore_iterator_next(it, &mut out);
520 if rc == Error::ValueNotFound.code() {
521 break;
522 }
523 assert_eq!(rc, 0);
524 seen.push(out as usize);
525 }
526 assert_eq!(seen.len(), 2);
527 cfm_keystore_iterator_destroy(it);
528 cfm_keystore_destroy(ks);
529 }
530
531 #[test]
532 fn enumerate_invalid_compartment_returns_error() {
533 let mut ks: *mut FFIKeystore = ptr::null_mut();
534 let name = CString::new("memory").unwrap().into_raw();
535 cfm_keystore_create(&mut ks, name, ptr::null());
536
537 let m = CString::new("mod").unwrap().into_raw();
538 let a = CString::new("app").unwrap().into_raw();
539 let mut it: *mut FFIKeyIterator = ptr::null_mut();
540 let rc = cfm_keystore_enumerate(ks, m, a, 99, &mut it);
541 assert_eq!(rc, crate::error::ErrorCode::INVALID_COMPARTMENT as u32);
542 }
543
544 #[test]
545 fn filesystem_open_succeeds() {
546 let mut ks: *mut FFIKeystore = ptr::null_mut();
551 let name = CString::new("filesystem").unwrap().into_raw();
552 let rc = cfm_keystore_create(&mut ks, name, ptr::null());
553 assert_eq!(rc, 0, "filesystem create should succeed");
554 assert!(!ks.is_null());
555 cfm_keystore_destroy(ks);
556 }
557
558 #[test]
559 fn null_keystore_pointer_returns_null_pointer_code() {
560 let m = CString::new("mod").unwrap().into_raw();
561 let a = CString::new("app").unwrap().into_raw();
562 let k = CString::new("key-1").unwrap().into_raw();
563 let rc = cfm_keystore_put_secret(ptr::null_mut(), m, a, k, sentinel(1));
564 assert_eq!(rc, crate::error::ErrorCode::NULL_POINTER as u32);
565 }
566
567 #[test]
568 fn destroy_null_is_safe() {
569 cfm_keystore_destroy(ptr::null_mut());
571 cfm_keystore_iterator_destroy(ptr::null_mut());
572 }
573}