1use crate::authenticatorservice::{
6 AuthenticatorService, CtapVersion, RegisterArgsCtap2, SignArgsCtap2,
7};
8use crate::ctap2::attestation::AttestationStatement;
9use crate::ctap2::commands::get_assertion::{Assertion, AssertionObject, GetAssertionOptions};
10use crate::ctap2::commands::make_credentials::MakeCredentialsOptions;
11use crate::ctap2::commands::CommandError;
12use crate::ctap2::server::{
13 PublicKeyCredentialDescriptor, PublicKeyCredentialParameters, RelyingParty, User,
14};
15use crate::errors::{AuthenticatorError, U2FTokenError};
16use crate::statecallback::StateCallback;
17use crate::{AttestationObject, CollectedClientData, Pin, StatusUpdate};
18use crate::{RegisterResult, SignResult};
19use libc::size_t;
20use rand::{thread_rng, Rng};
21use serde_cbor;
22use std::convert::TryFrom;
23use std::ffi::{CStr, CString};
24use std::os::raw::c_char;
25use std::sync::mpsc::channel;
26use std::thread;
27use std::{ptr, slice};
28
29type Ctap2RegisterResult = Result<(AttestationObject, CString), AuthenticatorError>;
30type Ctap2PubKeyCredDescriptors = Vec<PublicKeyCredentialDescriptor>;
31type Ctap2RegisterCallback = extern "C" fn(u64, *mut Ctap2RegisterResult);
32type Ctap2SignResult = Result<(AssertionObject, CString), AuthenticatorError>;
33type Ctap2SignCallback = extern "C" fn(u64, *mut Ctap2SignResult);
34type Ctap2StatusUpdateCallback = extern "C" fn(*mut StatusUpdate);
35
36const SIGN_RESULT_PUBKEY_CRED_ID: u8 = 1;
37const SIGN_RESULT_AUTH_DATA: u8 = 2;
38const SIGN_RESULT_SIGNATURE: u8 = 3;
39const SIGN_RESULT_USER_ID: u8 = 4;
40const SIGN_RESULT_USER_NAME: u8 = 5;
41
42const REGISTER_RESULT_ATTESTATION_STATEMENT: u8 = 1;
43const REGISTER_RESULT_AUTH_DATA: u8 = 2;
44
45const ATTESTATION_FORMAT_NONE: u8 = 0;
46const ATTESTATION_FORMAT_U2F: u8 = 1;
47const ATTESTATION_FORMAT_PACKED: u8 = 2;
48const ATTESTATION_FORMAT_UNPARSED: u8 = 3; #[repr(C)]
51pub struct AuthenticatorArgsUser {
52 id_ptr: *const u8,
53 id_len: usize,
54 name: *const c_char,
55}
56
57#[repr(C)]
58pub struct AuthenticatorArgsChallenge {
59 ptr: *const u8,
60 len: usize,
61}
62
63#[repr(C)]
64pub struct AuthenticatorArgsPubCred {
65 ptr: *const i32,
66 len: usize,
67}
68
69#[repr(C)]
70pub struct AuthenticatorArgsOptions {
71 resident_key: bool,
72 user_verification: bool,
73 user_presence: bool,
74}
75
76fn new_tid() -> u64 {
78 thread_rng().gen::<u64>()
79}
80
81unsafe fn from_raw(ptr: *const u8, len: usize) -> Vec<u8> {
82 slice::from_raw_parts(ptr, len).to_vec()
83}
84
85#[no_mangle]
90pub unsafe extern "C" fn rust_ctap2_mgr_free(mgr: *mut AuthenticatorService) {
91 if !mgr.is_null() {
92 Box::from_raw(mgr);
93 }
94}
95
96#[no_mangle]
100pub unsafe extern "C" fn rust_ctap2_pkcd_new() -> *mut Ctap2PubKeyCredDescriptors {
101 Box::into_raw(Box::new(vec![]))
102}
103
104#[no_mangle]
108pub unsafe extern "C" fn rust_ctap2_pkcd_add(
109 pkcd: *mut Ctap2PubKeyCredDescriptors,
110 id_ptr: *const u8,
111 id_len: usize,
112 transports: u8,
113) {
114 (*pkcd).push(PublicKeyCredentialDescriptor {
115 id: from_raw(id_ptr, id_len),
116 transports: crate::AuthenticatorTransports::from_bits_truncate(transports).into(),
117 });
118}
119
120#[no_mangle]
125pub unsafe extern "C" fn rust_ctap2_pkcd_free(khs: *mut Ctap2PubKeyCredDescriptors) {
126 if !khs.is_null() {
127 Box::from_raw(khs);
128 }
129}
130
131#[no_mangle]
138pub extern "C" fn rust_ctap2_mgr_new() -> *mut AuthenticatorService {
139 if let Ok(mut mgr) = AuthenticatorService::new(CtapVersion::CTAP2) {
140 mgr.add_detected_transports();
141 Box::into_raw(Box::new(mgr))
142 } else {
143 ptr::null_mut()
144 }
145}
146
147fn rewrap_client_data(client_data: CollectedClientData) -> Result<CString, AuthenticatorError> {
148 let client_data_str = serde_json::to_string(&client_data).map_err(|e| CommandError::Json(e))?;
149 let s = CString::new(client_data_str).map_err(|_| {
150 AuthenticatorError::Custom("Failed to transform client_data to C String".to_string())
151 })?;
152 Ok(s)
153}
154
155fn rewrap_register_result(
156 attestation_object: AttestationObject,
157 client_data: CollectedClientData,
158) -> Ctap2RegisterResult {
159 let s = rewrap_client_data(client_data)?;
160 Ok((attestation_object, s))
161}
162
163fn rewrap_sign_result(
164 assertion_object: AssertionObject,
165 client_data: CollectedClientData,
166) -> Ctap2SignResult {
167 let s = rewrap_client_data(client_data)?;
168 Ok((assertion_object, s))
169}
170
171#[no_mangle]
181pub unsafe extern "C" fn rust_ctap2_mgr_register(
182 mgr: *mut AuthenticatorService,
183 timeout: u64,
184 callback: Ctap2RegisterCallback,
185 status_callback: Ctap2StatusUpdateCallback,
186 challenge: AuthenticatorArgsChallenge,
187 relying_party_id: *const c_char,
188 origin_ptr: *const c_char,
189 user: AuthenticatorArgsUser,
190 pub_cred_params: AuthenticatorArgsPubCred,
191 exclude_list: *const Ctap2PubKeyCredDescriptors,
192 options: AuthenticatorArgsOptions,
193 pin_ptr: *const c_char,
194) -> u64 {
195 if mgr.is_null() {
196 return 0;
197 }
198
199 if challenge.ptr.is_null()
201 || origin_ptr.is_null()
202 || relying_party_id.is_null()
203 || user.id_ptr.is_null()
204 || user.name.is_null()
205 || exclude_list.is_null()
206 {
207 return 0;
208 }
209
210 let pub_cred_params = match slice::from_raw_parts(pub_cred_params.ptr, pub_cred_params.len)
211 .iter()
212 .map(|x| PublicKeyCredentialParameters::try_from(*x))
213 .collect()
214 {
215 Ok(x) => x,
216 Err(_) => {
217 return 0;
218 }
219 };
220 let pin = if pin_ptr.is_null() {
221 None
222 } else {
223 Some(Pin::new(&CStr::from_ptr(pin_ptr).to_string_lossy()))
224 };
225 let user = User {
226 id: from_raw(user.id_ptr, user.id_len),
227 name: Some(CStr::from_ptr(user.name).to_string_lossy().to_string()), display_name: None,
229 icon: None,
230 };
231 let rp = RelyingParty {
232 id: CStr::from_ptr(relying_party_id)
233 .to_string_lossy()
234 .to_string(),
235 name: None,
236 icon: None,
237 };
238 let origin = CStr::from_ptr(origin_ptr).to_string_lossy().to_string();
239 let challenge = from_raw(challenge.ptr, challenge.len);
240 let exclude_list = (*exclude_list).clone();
241
242 let (status_tx, status_rx) = channel::<crate::StatusUpdate>();
243 thread::spawn(move || loop {
244 match status_rx.recv() {
245 Ok(r) => {
246 let rb = Box::new(r);
247 status_callback(Box::into_raw(rb));
248 }
249 Err(e) => {
250 status_callback(ptr::null_mut());
251 error!("Error when receiving status update: {:?}", e);
252 return;
253 }
254 }
255 });
256
257 let tid = new_tid();
258
259 let state_callback = StateCallback::<crate::Result<RegisterResult>>::new(Box::new(move |rv| {
260 let res = match rv {
261 Ok(RegisterResult::CTAP1(..)) => Err(AuthenticatorError::VersionMismatch(
262 "rust_ctap2_mgr_register",
263 2,
264 )),
265 Ok(RegisterResult::CTAP2(attestation_object, client_data)) => {
266 rewrap_register_result(attestation_object, client_data)
267 }
268 Err(e) => Err(e),
269 };
270
271 callback(tid, Box::into_raw(Box::new(res)));
272 }));
273
274 let ctap_args = RegisterArgsCtap2 {
275 challenge,
276 relying_party: rp,
277 origin,
278 user,
279 pub_cred_params,
280 exclude_list,
281 options: MakeCredentialsOptions {
282 resident_key: options.resident_key.then(|| true),
283 user_verification: options.user_verification.then(|| true),
284 },
285 extensions: Default::default(),
286 pin,
287 };
288
289 let res = (*mgr).register(timeout, ctap_args.into(), status_tx, state_callback);
290
291 if res.is_ok() {
292 tid
293 } else {
294 0
295 }
296}
297
298#[no_mangle]
307pub unsafe extern "C" fn rust_ctap2_mgr_sign(
308 mgr: *mut AuthenticatorService,
309 timeout: u64,
310 callback: Ctap2SignCallback,
311 status_callback: Ctap2StatusUpdateCallback,
312 challenge: AuthenticatorArgsChallenge,
313 relying_party_id: *const c_char,
314 origin_ptr: *const c_char,
315 allow_list: *const Ctap2PubKeyCredDescriptors,
316 options: AuthenticatorArgsOptions,
317 pin_ptr: *const c_char,
318) -> u64 {
319 if mgr.is_null() {
320 return 0;
321 }
322
323 if challenge.ptr.is_null()
325 || origin_ptr.is_null()
326 || relying_party_id.is_null()
327 || allow_list.is_null()
328 {
329 return 0;
330 }
331
332 let pin = if pin_ptr.is_null() {
333 None
334 } else {
335 Some(Pin::new(&CStr::from_ptr(pin_ptr).to_string_lossy()))
336 };
337 let rpid = CStr::from_ptr(relying_party_id)
338 .to_string_lossy()
339 .to_string();
340 let origin = CStr::from_ptr(origin_ptr).to_string_lossy().to_string();
341 let challenge = from_raw(challenge.ptr, challenge.len);
342 let allow_list: Vec<_> = (*allow_list).clone();
343
344 let (status_tx, status_rx) = channel::<crate::StatusUpdate>();
345 thread::spawn(move || loop {
346 match status_rx.recv() {
347 Ok(r) => {
348 let rb = Box::new(r);
349 status_callback(Box::into_raw(rb));
350 }
351 Err(e) => {
352 status_callback(ptr::null_mut());
353 error!("Error when receiving status update: {:?}", e);
354 return;
355 }
356 }
357 });
358
359 let single_key_handle = if allow_list.len() == 1 {
360 Some(allow_list.first().unwrap().clone())
361 } else {
362 None
363 };
364
365 let tid = new_tid();
366 let state_callback = StateCallback::<crate::Result<SignResult>>::new(Box::new(move |rv| {
367 let res = match rv {
368 Ok(SignResult::CTAP1(..)) => Err(AuthenticatorError::VersionMismatch(
369 "rust_ctap2_mgr_register",
370 2,
371 )),
372 Ok(SignResult::CTAP2(mut assertion_object, client_data)) => {
373 assertion_object.0.iter_mut().for_each(|x| {
376 x.credentials = x.credentials.clone().or(single_key_handle.clone());
377 });
378 rewrap_sign_result(assertion_object, client_data)
379 }
380 Err(e) => Err(e),
381 };
382
383 callback(tid, Box::into_raw(Box::new(res)));
384 }));
385 let ctap_args = SignArgsCtap2 {
386 challenge,
387 origin,
388 relying_party_id: rpid,
389 allow_list,
390 options: GetAssertionOptions {
391 user_presence: options.user_presence.then(|| true),
392 user_verification: options.user_verification.then(|| true),
393 },
394 extensions: Default::default(),
395 pin,
396 };
397
398 let res = (*mgr).sign(timeout, ctap_args.into(), status_tx, state_callback);
399
400 if res.is_ok() {
401 tid
402 } else {
403 0
404 }
405}
406
407#[no_mangle]
411pub unsafe extern "C" fn rust_ctap2_register_result_error(res: *const Ctap2RegisterResult) -> u8 {
412 if res.is_null() {
413 return U2FTokenError::Unknown as u8;
414 }
415
416 match &*res {
417 Ok(..) => 0, Err(e) => e.as_u2f_errorcode(),
419 }
420}
421
422#[no_mangle]
423pub unsafe extern "C" fn rust_ctap2_sign_result_error(res: *const Ctap2SignResult) -> u8 {
424 if res.is_null() {
425 return U2FTokenError::Unknown as u8;
426 }
427
428 match &*res {
429 Ok(..) => 0, Err(e) => e.as_u2f_errorcode(),
431 }
432}
433
434#[no_mangle]
439pub unsafe extern "C" fn rust_ctap2_register_res_free(res: *mut Ctap2RegisterResult) {
440 if !res.is_null() {
441 let _ = Box::from_raw(res);
442 }
443}
444
445#[no_mangle]
450pub unsafe extern "C" fn rust_ctap2_sign_res_free(res: *mut Ctap2SignResult) {
451 if !res.is_null() {
452 let _ = Box::from_raw(res);
453 }
454}
455
456#[no_mangle]
461pub unsafe extern "C" fn rust_ctap2_mgr_cancel(mgr: *mut AuthenticatorService) {
462 if !mgr.is_null() {
463 let _ = (*mgr).cancel();
465 }
466}
467
468unsafe fn client_data_len<T>(
469 res: *const Result<(T, CString), AuthenticatorError>,
470 len: *mut size_t,
471) -> bool {
472 if res.is_null() || len.is_null() {
473 return false;
474 }
475
476 match &*res {
477 Ok((_, client_data)) => {
478 *len = client_data.as_bytes().len();
479 true
480 }
481 Err(_) => false,
482 }
483}
484
485#[no_mangle]
488pub unsafe extern "C" fn rust_ctap2_register_result_client_data_len(
489 res: *const Ctap2RegisterResult,
490 len: *mut size_t,
491) -> bool {
492 client_data_len(res, len)
493}
494
495#[no_mangle]
498pub unsafe extern "C" fn rust_ctap2_sign_result_client_data_len(
499 res: *const Ctap2SignResult,
500 len: *mut size_t,
501) -> bool {
502 client_data_len(res, len)
503}
504
505unsafe fn client_data_copy<T>(
506 res: *const Result<(T, CString), AuthenticatorError>,
507 dst: *mut c_char,
508) -> bool {
509 if dst.is_null() || res.is_null() {
510 return false;
511 }
512
513 match &*res {
514 Ok((_, client_data)) => {
515 ptr::copy_nonoverlapping(client_data.as_ptr(), dst, client_data.as_bytes().len());
516 return true;
517 }
518 Err(_) => false,
519 }
520}
521
522#[no_mangle]
527pub unsafe extern "C" fn rust_ctap2_register_result_client_data_copy(
528 res: *const Ctap2RegisterResult,
529 dst: *mut c_char,
530) -> bool {
531 client_data_copy(res, dst)
532}
533
534#[no_mangle]
539pub unsafe extern "C" fn rust_ctap2_sign_result_client_data_copy(
540 res: *const Ctap2SignResult,
541 dst: *mut c_char,
542) -> bool {
543 client_data_copy(res, dst)
544}
545
546fn register_result_item_len(attestation: &AttestationObject, item_idx: u8) -> Option<usize> {
547 match item_idx {
548 REGISTER_RESULT_ATTESTATION_STATEMENT => match &attestation.att_statement {
549 AttestationStatement::None => None,
550 AttestationStatement::Packed(x) => serde_cbor::to_vec(&x).ok().map(|x| x.len()),
551 AttestationStatement::FidoU2F(x) => serde_cbor::to_vec(&x).ok().map(|x| x.len()),
552 AttestationStatement::Unparsed(x) => Some(x.len()),
553 },
554 REGISTER_RESULT_AUTH_DATA => attestation.auth_data.to_vec().ok().map(|x| x.len()),
555 _ => None,
556 }
557}
558
559#[no_mangle]
563pub unsafe extern "C" fn rust_ctap2_register_result_item_len(
564 res: *const Ctap2RegisterResult,
565 item_idx: u8,
566 len: *mut size_t,
567) -> bool {
568 if res.is_null() || len.is_null() {
569 return false;
570 }
571
572 match &*res {
573 Ok((attestation, _)) => {
574 if let Some(item_len) = register_result_item_len(&attestation, item_idx) {
575 *len = item_len;
576 true
577 } else {
578 false
579 }
580 }
581 Err(_) => false,
582 }
583}
584
585unsafe fn register_result_item_copy(
586 attestation: &AttestationObject,
587 item_idx: u8,
588 dst: *mut u8,
589) -> bool {
590 if dst.is_null() {
591 return false;
592 }
593
594 let tmp_val;
595 let item = match item_idx {
596 REGISTER_RESULT_ATTESTATION_STATEMENT => match &attestation.att_statement {
597 AttestationStatement::None => None,
598 AttestationStatement::Packed(x) => {
599 tmp_val = serde_cbor::to_vec(&x).ok();
600 tmp_val.as_ref().map(|x| x.as_ref())
601 }
602 AttestationStatement::FidoU2F(x) => {
603 tmp_val = serde_cbor::to_vec(&x).ok();
604 tmp_val.as_ref().map(|x| x.as_ref())
605 }
606 AttestationStatement::Unparsed(x) => Some(x.as_slice()),
607 },
608 REGISTER_RESULT_AUTH_DATA => {
609 tmp_val = attestation.auth_data.to_vec().ok();
610 tmp_val.as_ref().map(|x| x.as_ref())
611 }
612 _ => None,
613 };
614
615 if let Some(item) = item {
616 ptr::copy_nonoverlapping(item.as_ptr(), dst, item.len());
617 true
618 } else {
619 false
620 }
621}
622
623#[no_mangle]
628pub unsafe extern "C" fn rust_ctap2_register_result_item_copy(
629 res: *const Ctap2RegisterResult,
630 item_idx: u8,
631 dst: *mut u8,
632) -> bool {
633 if res.is_null() || dst.is_null() {
634 return false;
635 }
636
637 match &*res {
638 Ok((attestation, _)) => register_result_item_copy(&attestation, item_idx, dst),
639 Err(_) => false,
640 }
641}
642
643#[no_mangle]
644pub unsafe extern "C" fn rust_ctap2_register_result_attestation_format(
645 res: *const Ctap2RegisterResult,
646 fmt: *mut u8,
647) -> bool {
648 if res.is_null() || fmt.is_null() {
649 return false;
650 }
651
652 match &*res {
653 Ok((attestation_object, _client_data)) => {
654 *fmt = match &attestation_object.att_statement {
655 AttestationStatement::None => ATTESTATION_FORMAT_NONE,
656 AttestationStatement::FidoU2F(..) => ATTESTATION_FORMAT_U2F,
657 AttestationStatement::Packed(..) => ATTESTATION_FORMAT_PACKED,
658 AttestationStatement::Unparsed(..) => ATTESTATION_FORMAT_UNPARSED,
659 };
660 true
661 }
662 Err(_) => false,
663 }
664}
665
666#[no_mangle]
670pub unsafe extern "C" fn rust_ctap2_sign_result_assertions_len(
671 res: *const Ctap2SignResult,
672 len: *mut size_t,
673) -> bool {
674 if res.is_null() || len.is_null() {
675 return false;
676 }
677
678 match &*res {
679 Ok((assertions, _)) => {
680 *len = assertions.0.len();
681 return true;
682 }
683 Err(_) => false,
684 }
685}
686
687fn sign_result_item_len(assertion: &Assertion, item_idx: u8) -> Option<usize> {
688 match item_idx {
689 SIGN_RESULT_PUBKEY_CRED_ID => assertion.credentials.as_ref().map(|x| x.id.len()),
690 SIGN_RESULT_AUTH_DATA => assertion.auth_data.to_vec().ok().map(|x| x.len()),
692 SIGN_RESULT_SIGNATURE => Some(assertion.signature.len()),
693 SIGN_RESULT_USER_ID => assertion.user.as_ref().map(|u| u.id.len()),
694 SIGN_RESULT_USER_NAME => assertion
695 .user
696 .as_ref()
697 .map(|u| {
698 u.display_name
699 .as_ref()
700 .or(u.name.as_ref())
701 .map(|n| n.as_bytes().len())
702 })
703 .flatten(),
704 _ => None,
705 }
706}
707
708#[no_mangle]
709pub unsafe extern "C" fn rust_ctap2_sign_result_item_contains(
710 res: *const Ctap2SignResult,
711 assertion_idx: usize,
712 item_idx: u8,
713) -> bool {
714 if res.is_null() {
715 return false;
716 }
717
718 match &*res {
719 Ok((assertions, _)) => {
720 if assertion_idx >= assertions.0.len() {
721 return false;
722 }
723 if item_idx == SIGN_RESULT_AUTH_DATA {
724 return true;
726 }
727 sign_result_item_len(&assertions.0[assertion_idx], item_idx).is_some()
728 }
729 Err(_) => false,
730 }
731}
732
733#[no_mangle]
737pub unsafe extern "C" fn rust_ctap2_sign_result_item_len(
738 res: *const Ctap2SignResult,
739 assertion_idx: usize,
740 item_idx: u8,
741 len: *mut size_t,
742) -> bool {
743 if res.is_null() || len.is_null() {
744 return false;
745 }
746
747 match &*res {
748 Ok((assertions, _)) => {
749 if assertion_idx >= assertions.0.len() {
750 return false;
751 }
752
753 if let Some(item_len) = sign_result_item_len(&assertions.0[assertion_idx], item_idx) {
754 *len = item_len;
755 true
756 } else {
757 false
758 }
759 }
760 Err(_) => false,
761 }
762}
763
764unsafe fn sign_result_item_copy(assertion: &Assertion, item_idx: u8, dst: *mut u8) -> bool {
765 if dst.is_null() {
766 return false;
767 }
768
769 let tmp_val;
770 let item = match item_idx {
771 SIGN_RESULT_PUBKEY_CRED_ID => assertion.credentials.as_ref().map(|x| x.id.as_ref()),
772 SIGN_RESULT_AUTH_DATA => {
774 tmp_val = assertion.auth_data.to_vec().ok();
775 tmp_val.as_ref().map(|x| x.as_ref())
776 }
777 SIGN_RESULT_SIGNATURE => Some(assertion.signature.as_ref()),
778 SIGN_RESULT_USER_ID => assertion.user.as_ref().map(|u| u.id.as_ref()),
779 SIGN_RESULT_USER_NAME => assertion
780 .user
781 .as_ref()
782 .map(|u| {
783 u.display_name
784 .as_ref()
785 .or(u.name.as_ref())
786 .map(|n| n.as_bytes().as_ref())
787 })
788 .flatten(),
789 _ => None,
790 };
791
792 if let Some(item) = item {
793 ptr::copy_nonoverlapping(item.as_ptr(), dst, item.len());
794 true
795 } else {
796 false
797 }
798}
799
800#[no_mangle]
805pub unsafe extern "C" fn rust_ctap2_sign_result_item_copy(
806 res: *const Ctap2SignResult,
807 assertion_idx: usize,
808 item_idx: u8,
809 dst: *mut u8,
810) -> bool {
811 if res.is_null() || dst.is_null() {
812 return false;
813 }
814
815 match &*res {
816 Ok((assertions, _)) => {
817 if assertion_idx >= assertions.0.len() {
818 return false;
819 }
820
821 sign_result_item_copy(&assertions.0[assertion_idx], item_idx, dst)
822 }
823 Err(_) => false,
824 }
825}
826
827#[no_mangle]
828pub unsafe extern "C" fn rust_ctap2_sign_result_contains_username(
829 res: *const Ctap2SignResult,
830 assertion_idx: usize,
831) -> bool {
832 if res.is_null() {
833 return false;
834 }
835
836 match &*res {
837 Ok((assertions, _)) => {
838 if assertion_idx >= assertions.0.len() {
839 return false;
840 }
841 assertions.0[assertion_idx]
842 .user
843 .as_ref()
844 .map(|u| u.display_name.as_ref().or(u.name.as_ref()))
845 .is_some()
846 }
847 Err(_) => false,
848 }
849}
850
851#[no_mangle]
855pub unsafe extern "C" fn rust_ctap2_sign_result_username_len(
856 res: *const Ctap2SignResult,
857 assertion_idx: usize,
858 len: *mut size_t,
859) -> bool {
860 if res.is_null() || len.is_null() {
861 return false;
862 }
863
864 match &*res {
865 Ok((assertions, _)) => {
866 if assertion_idx >= assertions.0.len() {
867 return false;
868 }
869
870 if let Some(name_len) = assertions.0[assertion_idx]
871 .user
872 .as_ref()
873 .map(|u| u.display_name.as_ref().or(u.name.as_ref()))
874 .flatten()
875 .map(|x| x.as_bytes().len())
876 {
877 *len = name_len;
878 true
879 } else {
880 false
881 }
882 }
883 Err(_) => false,
884 }
885}
886
887#[no_mangle]
892pub unsafe extern "C" fn rust_ctap2_sign_result_username_copy(
893 res: *const Ctap2SignResult,
894 assertion_idx: usize,
895 dst: *mut c_char,
896) -> bool {
897 if res.is_null() || dst.is_null() {
898 return false;
899 }
900
901 match &*res {
902 Ok((assertions, _)) => {
903 if assertion_idx >= assertions.0.len() {
904 return false;
905 }
906
907 if let Some(name) = assertions.0[assertion_idx]
908 .user
909 .as_ref()
910 .map(|u| u.display_name.as_ref().or(u.name.as_ref()))
911 .flatten()
912 .map(|u| CString::new(u.clone()).ok())
913 .flatten()
914 {
915 ptr::copy_nonoverlapping(name.as_ptr(), dst, name.as_bytes().len());
916 true
917 } else {
918 false
919 }
920 }
921
922 Err(_) => false,
923 }
924}
925
926#[no_mangle]
930pub unsafe extern "C" fn rust_ctap2_status_update_len(
931 res: *const StatusUpdate,
932 len: *mut size_t,
933) -> bool {
934 if res.is_null() || len.is_null() {
935 return false;
936 }
937
938 match serde_json::to_string(&*res) {
939 Ok(s) => {
940 *len = s.len();
941 true
942 }
943 Err(e) => {
944 error!("Failed to parse {:?} into json: {:?}", &*res, e);
945 false
946 }
947 }
948}
949
950#[no_mangle]
955pub unsafe extern "C" fn rust_ctap2_status_update_copy_json(
956 res: *const StatusUpdate,
957 dst: *mut c_char,
958) -> bool {
959 if res.is_null() || dst.is_null() {
960 return false;
961 }
962
963 match serde_json::to_string(&*res) {
964 Ok(s) => {
965 if let Ok(cs) = CString::new(s) {
966 ptr::copy_nonoverlapping(cs.as_ptr(), dst, cs.as_bytes().len());
967 true
968 } else {
969 error!("Failed to convert String to CString");
970 false
971 }
972 }
973 Err(e) => {
974 error!("Failed to parse {:?} into json: {:?}", &*res, e);
975 false
976 }
977 }
978}
979
980#[no_mangle]
984pub unsafe extern "C" fn rust_ctap2_status_update_send_pin(
985 res: *const StatusUpdate,
986 c_pin: *mut c_char,
987) -> bool {
988 if res.is_null() || c_pin.is_null() {
989 return false;
990 }
991
992 match &*res {
993 StatusUpdate::PinError(_, sender) => {
994 if let Ok(pin) = CStr::from_ptr(c_pin).to_str() {
995 sender
996 .send(Pin::new(pin))
997 .map_err(|e| {
998 error!("Failed to send PIN to device-thread");
999 e
1000 })
1001 .is_ok()
1002 } else {
1003 error!("Failed to convert PIN from c_char to String");
1004 false
1005 }
1006 }
1007 _ => {
1008 error!("Wrong state!");
1009 false
1010 }
1011 }
1012}
1013
1014#[no_mangle]
1018pub unsafe extern "C" fn rust_ctap2_destroy_status_update_res(res: *mut StatusUpdate) -> bool {
1019 if res.is_null() {
1020 return false;
1021 }
1022 let _ = Box::from_raw(res);
1024 true
1025}