jpki/
lib.rs

1#![feature(vec_into_raw_parts)]
2#![allow(clippy::missing_safety_doc)]
3
4use jpki::ap::crypto::CertType;
5use jpki::ap::CryptoAp;
6use jpki::nfc::{HandleError, HandlerInCtx, Result as NfcResult};
7use jpki::Card;
8use std::ffi::{c_char, CStr, CString};
9use std::ptr::null_mut;
10use std::rc::Rc;
11
12static mut LAST_ERROR: Option<String> = None;
13
14fn unwrap_or<T, E>(result: Result<T, E>, default: T) -> T
15where
16    E: ToString,
17{
18    unsafe {
19        // If result is an error, sets the message to LAST_ERROR.
20        // Clears the last error otherwise.
21        LAST_ERROR = result.as_ref().err().map(|e| e.to_string());
22    }
23
24    match result {
25        Ok(value) => value,
26        Err(_) => default,
27    }
28}
29
30fn unwrap<T, E>(result: Result<T, E>) -> T
31where
32    T: Default,
33    E: ToString,
34{
35    unwrap_or(result, T::default())
36}
37
38/// A struct represents a byte array.
39/// Dependents can read it from ptr to ptr+len, and should ignore about cap.
40/// ptr can be null pointer, so dependents must check the ptr is not null.
41#[repr(C)]
42#[derive(Copy, Clone)]
43pub struct ByteArray {
44    ptr: *mut u8,
45    len: usize,
46    cap: usize,
47}
48
49impl Default for ByteArray {
50    fn default() -> Self {
51        Self {
52            ptr: null_mut(),
53            len: 0,
54            cap: 0,
55        }
56    }
57}
58
59impl From<Vec<u8>> for ByteArray {
60    fn from(bytes: Vec<u8>) -> Self {
61        let bytes = Box::new(bytes);
62        let (ptr, len, cap) = bytes.into_raw_parts();
63        Self { ptr, len, cap }
64    }
65}
66
67impl From<ByteArray> for Vec<u8> {
68    fn from(ByteArray { ptr, len, cap }: ByteArray) -> Self {
69        unsafe { Self::from_raw_parts(ptr, len, cap) }
70    }
71}
72
73impl ByteArray {
74    fn drain(self) {
75        let Self { ptr, len, cap } = self;
76        let _ = unsafe { Vec::from_raw_parts(ptr, len, cap) };
77    }
78}
79
80pub struct NfcCard {
81    delegate: extern "C" fn(ByteArray) -> ByteArray,
82}
83
84impl HandlerInCtx for NfcCard {
85    fn handle_in_ctx(&self, _: (), command: &[u8], response: &mut [u8]) -> NfcResult {
86        let command = Vec::from(command).into();
87        let buf = Vec::from((self.delegate)(command));
88        let len = buf.len();
89        if response.len() < len {
90            return Err(HandleError::NotEnoughBuffer(len));
91        }
92
93        response[..len].copy_from_slice(&buf);
94        command.drain();
95        Ok(len)
96    }
97}
98
99/// Initiates the libjpki library.
100/// Currently this occur no side effects, but it will be added in the future.
101/// So dependents should call this before using other functions.
102#[no_mangle]
103pub extern "C" fn jpki_init() {}
104
105/// Returns the latest error occurred before calling this function.
106/// If no error occurred before or failed to get the error, returns null pointer.
107#[no_mangle]
108pub extern "C" fn jpki_last_error() -> *mut c_char {
109    match unsafe { LAST_ERROR.clone() }.and_then(|e| CString::new(e).ok()) {
110        Some(str) => str.into_raw(),
111        None => null_mut(),
112    }
113}
114
115/// Creates a new NFC card delegate from the function pointer.
116/// This provided function will be called on transmitting APDU commands into the card.
117#[no_mangle]
118pub extern "C" fn jpki_new_nfc_card(
119    delegate: extern "C" fn(ByteArray) -> ByteArray,
120) -> *mut NfcCard {
121    let card = NfcCard { delegate };
122
123    Box::into_raw(Box::new(card))
124}
125
126/// Closes the NFC card.
127#[no_mangle]
128pub unsafe extern "C" fn jpki_nfc_card_close(card: &mut NfcCard) {
129    let _ = Box::from_raw(card);
130}
131
132/// Creates a new card from the NFC card.
133/// This is an abstraction layer to support other protocols rather than NFC in the future.
134#[no_mangle]
135pub unsafe extern "C" fn jpki_new_card(nfc_card: *mut NfcCard) -> *mut Card<NfcCard, ()> {
136    let nfc_card = Box::from_raw(nfc_card);
137    let card = Card::new(nfc_card);
138
139    Box::into_raw(Box::new(card))
140}
141
142/// Closes the card.
143#[no_mangle]
144pub unsafe extern "C" fn jpki_card_close(card: &mut Card<NfcCard, ()>) {
145    // HACK: To avoid NfcCard to be deallocated recursively, using Rc instead of Box here.
146    let _ = Rc::from_raw(card);
147}
148
149/// Opens JPKI application on the card.
150#[no_mangle]
151pub unsafe extern "C" fn jpki_new_crypto_ap(
152    card: *mut Card<NfcCard, ()>,
153) -> *mut CryptoAp<NfcCard, ()> {
154    let card = Rc::from_raw(card);
155
156    unwrap_or(
157        CryptoAp::open((), card).map(|ap| Box::into_raw(Box::new(ap))),
158        null_mut(),
159    )
160}
161
162/// Closes the opened JPKI application.
163#[no_mangle]
164pub unsafe extern "C" fn jpki_crypto_ap_close(crypto_ap: *mut CryptoAp<NfcCard, ()>) {
165    let _ = Box::from_raw(crypto_ap);
166}
167
168/// Reads a certificate for signing.
169/// PIN is required.
170/// If ca is true, reads a CA certificate instead.
171#[no_mangle]
172pub unsafe extern "C" fn jpki_crypto_ap_read_certificate_sign(
173    crypto_ap: *mut CryptoAp<NfcCard, ()>,
174    pin: *const c_char,
175    ca: bool,
176) -> ByteArray {
177    let pin = CStr::from_ptr(pin).to_bytes().to_vec();
178    let ty = match ca {
179        true => CertType::SignCA,
180        _ => CertType::Sign,
181    };
182
183    unwrap(
184        crypto_ap
185            .as_ref()
186            .unwrap()
187            .read_certificate((), ty, pin)
188            .map(|v| v.into()),
189    )
190}
191
192/// Reads a certificate for user authentication.
193/// If ca is true, reads a CA certificate instead.
194#[no_mangle]
195pub unsafe extern "C" fn jpki_crypto_ap_read_certificate_auth(
196    crypto_ap: *mut CryptoAp<NfcCard, ()>,
197    ca: bool,
198) -> ByteArray {
199    let ty = match ca {
200        true => CertType::AuthCA,
201        _ => CertType::Auth,
202    };
203
204    unwrap(
205        crypto_ap
206            .as_ref()
207            .unwrap()
208            .read_certificate((), ty, vec![])
209            .map(|v| v.into()),
210    )
211}
212
213/// Sign the computed digest using the key-pair for user authentication.
214#[no_mangle]
215pub unsafe extern "C" fn jpki_crypto_ap_auth(
216    crypto_ap: *mut CryptoAp<NfcCard, ()>,
217    pin: *const c_char,
218    digest: ByteArray,
219) -> ByteArray {
220    let pin = CStr::from_ptr(pin).to_bytes().to_vec();
221
222    unwrap(
223        crypto_ap
224            .as_ref()
225            .unwrap()
226            .auth((), pin, digest.into())
227            .map(|v| v.into()),
228    )
229}
230
231/// Sign the computed digest using the key-pair for signing.
232#[no_mangle]
233pub unsafe extern "C" fn jpki_crypto_ap_sign(
234    crypto_ap: *mut CryptoAp<NfcCard, ()>,
235    pin: *const c_char,
236    digest: ByteArray,
237) -> ByteArray {
238    let pin = CStr::from_ptr(pin).to_bytes().to_vec();
239
240    unwrap(
241        crypto_ap
242            .as_ref()
243            .unwrap()
244            .sign((), pin, digest.into())
245            .map(|v| v.into()),
246    )
247}