use crate::authenticatorservice::{
AuthenticatorService, CtapVersion, RegisterArgsCtap1, SignArgsCtap1,
};
use crate::errors;
use crate::statecallback::StateCallback;
use crate::{RegisterResult, SignResult};
use libc::size_t;
use rand::{thread_rng, Rng};
use std::collections::HashMap;
use std::sync::mpsc::channel;
use std::thread;
use std::{ptr, slice};
type U2FAppIds = Vec<crate::AppId>;
type U2FKeyHandles = Vec<crate::KeyHandle>;
type U2FCallback = extern "C" fn(u64, *mut U2FResult);
pub enum U2FResult {
Success(HashMap<u8, Vec<u8>>),
Error(errors::AuthenticatorError),
}
const RESBUF_ID_REGISTRATION: u8 = 0;
const RESBUF_ID_KEYHANDLE: u8 = 1;
const RESBUF_ID_SIGNATURE: u8 = 2;
const RESBUF_ID_APPID: u8 = 3;
const RESBUF_ID_VENDOR_NAME: u8 = 4;
const RESBUF_ID_DEVICE_NAME: u8 = 5;
const RESBUF_ID_FIRMWARE_MAJOR: u8 = 6;
const RESBUF_ID_FIRMWARE_MINOR: u8 = 7;
const RESBUF_ID_FIRMWARE_BUILD: u8 = 8;
fn new_tid() -> u64 {
thread_rng().gen::<u64>()
}
unsafe fn from_raw(ptr: *const u8, len: usize) -> Vec<u8> {
slice::from_raw_parts(ptr, len).to_vec()
}
#[no_mangle]
pub extern "C" fn rust_u2f_mgr_new() -> *mut AuthenticatorService {
if let Ok(mut mgr) = AuthenticatorService::new(CtapVersion::CTAP1) {
mgr.add_detected_transports();
Box::into_raw(Box::new(mgr))
} else {
ptr::null_mut()
}
}
#[no_mangle]
pub unsafe extern "C" fn rust_u2f_mgr_free(mgr: *mut AuthenticatorService) {
if !mgr.is_null() {
Box::from_raw(mgr);
}
}
#[no_mangle]
pub unsafe extern "C" fn rust_u2f_app_ids_new() -> *mut U2FAppIds {
Box::into_raw(Box::new(vec![]))
}
#[no_mangle]
pub unsafe extern "C" fn rust_u2f_app_ids_add(
ids: *mut U2FAppIds,
id_ptr: *const u8,
id_len: usize,
) {
(*ids).push(from_raw(id_ptr, id_len));
}
#[no_mangle]
pub unsafe extern "C" fn rust_u2f_app_ids_free(ids: *mut U2FAppIds) {
if !ids.is_null() {
Box::from_raw(ids);
}
}
#[no_mangle]
pub unsafe extern "C" fn rust_u2f_khs_new() -> *mut U2FKeyHandles {
Box::into_raw(Box::new(vec![]))
}
#[no_mangle]
pub unsafe extern "C" fn rust_u2f_khs_add(
khs: *mut U2FKeyHandles,
key_handle_ptr: *const u8,
key_handle_len: usize,
transports: u8,
) {
(*khs).push(crate::KeyHandle {
credential: from_raw(key_handle_ptr, key_handle_len),
transports: crate::AuthenticatorTransports::from_bits_truncate(transports),
});
}
#[no_mangle]
pub unsafe extern "C" fn rust_u2f_khs_free(khs: *mut U2FKeyHandles) {
if !khs.is_null() {
Box::from_raw(khs);
}
}
#[no_mangle]
pub unsafe extern "C" fn rust_u2f_result_error(res: *const U2FResult) -> u8 {
if res.is_null() {
return errors::U2FTokenError::Unknown as u8;
}
if let U2FResult::Error(ref err) = *res {
return err.as_u2f_errorcode();
}
0
}
#[no_mangle]
pub unsafe extern "C" fn rust_u2f_resbuf_contains(res: *const U2FResult, bid: u8) -> bool {
if res.is_null() {
return false;
}
if let U2FResult::Success(ref bufs) = *res {
return bufs.contains_key(&bid);
}
false
}
#[no_mangle]
pub unsafe extern "C" fn rust_u2f_resbuf_length(
res: *const U2FResult,
bid: u8,
len: *mut size_t,
) -> bool {
if res.is_null() {
return false;
}
if let U2FResult::Success(ref bufs) = *res {
if let Some(buf) = bufs.get(&bid) {
*len = buf.len();
return true;
}
}
false
}
#[no_mangle]
pub unsafe extern "C" fn rust_u2f_resbuf_copy(
res: *const U2FResult,
bid: u8,
dst: *mut u8,
) -> bool {
if res.is_null() {
return false;
}
if let U2FResult::Success(ref bufs) = *res {
if let Some(buf) = bufs.get(&bid) {
ptr::copy_nonoverlapping(buf.as_ptr(), dst, buf.len());
return true;
}
}
false
}
#[no_mangle]
pub unsafe extern "C" fn rust_u2f_res_free(res: *mut U2FResult) {
if !res.is_null() {
Box::from_raw(res);
}
}
#[no_mangle]
pub unsafe extern "C" fn rust_u2f_mgr_register(
mgr: *mut AuthenticatorService,
flags: u64,
timeout: u64,
callback: U2FCallback,
challenge_ptr: *const u8,
challenge_len: usize,
application_ptr: *const u8,
application_len: usize,
khs: *const U2FKeyHandles,
) -> u64 {
if mgr.is_null() {
return 0;
}
if challenge_ptr.is_null() || application_ptr.is_null() {
return 0;
}
let flags = crate::RegisterFlags::from_bits_truncate(flags);
let challenge = from_raw(challenge_ptr, challenge_len);
let application = from_raw(application_ptr, application_len);
let key_handles = (*khs).clone();
let (status_tx, status_rx) = channel::<crate::StatusUpdate>();
thread::spawn(move || loop {
match status_rx.recv() {
Ok(_) => {}
Err(_recv_error) => return,
}
});
let tid = new_tid();
let state_callback = StateCallback::<crate::Result<RegisterResult>>::new(Box::new(move |rv| {
let result = match rv {
Ok(RegisterResult::CTAP1(registration, dev_info)) => {
let mut bufs = HashMap::new();
bufs.insert(RESBUF_ID_REGISTRATION, registration);
bufs.insert(RESBUF_ID_VENDOR_NAME, dev_info.vendor_name);
bufs.insert(RESBUF_ID_DEVICE_NAME, dev_info.device_name);
bufs.insert(RESBUF_ID_FIRMWARE_MAJOR, vec![dev_info.version_major]);
bufs.insert(RESBUF_ID_FIRMWARE_MINOR, vec![dev_info.version_minor]);
bufs.insert(RESBUF_ID_FIRMWARE_BUILD, vec![dev_info.version_build]);
U2FResult::Success(bufs)
}
Ok(RegisterResult::CTAP2(..)) => U2FResult::Error(
errors::AuthenticatorError::VersionMismatch("rust_u2f_mgr_register", 1),
),
Err(e) => U2FResult::Error(e),
};
callback(tid, Box::into_raw(Box::new(result)));
}));
let ctap_args = RegisterArgsCtap1 {
flags,
challenge,
application,
key_handles,
};
let res = (*mgr).register(timeout, ctap_args.into(), status_tx, state_callback);
if res.is_ok() {
tid
} else {
0
}
}
#[no_mangle]
pub unsafe extern "C" fn rust_u2f_mgr_sign(
mgr: *mut AuthenticatorService,
flags: u64,
timeout: u64,
callback: U2FCallback,
challenge_ptr: *const u8,
challenge_len: usize,
app_ids: *const U2FAppIds,
khs: *const U2FKeyHandles,
) -> u64 {
if mgr.is_null() || khs.is_null() {
return 0;
}
if challenge_ptr.is_null() {
return 0;
}
if (*app_ids).is_empty() {
return 0;
}
let flags = crate::SignFlags::from_bits_truncate(flags);
let challenge = from_raw(challenge_ptr, challenge_len);
let app_ids = (*app_ids).clone();
let key_handles = (*khs).clone();
let (status_tx, status_rx) = channel::<crate::StatusUpdate>();
thread::spawn(move || loop {
match status_rx.recv() {
Ok(_) => {}
Err(_recv_error) => return,
}
});
let tid = new_tid();
let state_callback = StateCallback::<crate::Result<SignResult>>::new(Box::new(move |rv| {
let result = match rv {
Ok(SignResult::CTAP1(app_id, key_handle, signature, dev_info)) => {
let mut bufs = HashMap::new();
bufs.insert(RESBUF_ID_KEYHANDLE, key_handle);
bufs.insert(RESBUF_ID_SIGNATURE, signature);
bufs.insert(RESBUF_ID_APPID, app_id);
bufs.insert(RESBUF_ID_VENDOR_NAME, dev_info.vendor_name);
bufs.insert(RESBUF_ID_DEVICE_NAME, dev_info.device_name);
bufs.insert(RESBUF_ID_FIRMWARE_MAJOR, vec![dev_info.version_major]);
bufs.insert(RESBUF_ID_FIRMWARE_MINOR, vec![dev_info.version_minor]);
bufs.insert(RESBUF_ID_FIRMWARE_BUILD, vec![dev_info.version_build]);
U2FResult::Success(bufs)
}
Ok(SignResult::CTAP2(..)) => U2FResult::Error(
errors::AuthenticatorError::VersionMismatch("rust_u2f_mgr_sign", 1),
),
Err(e) => U2FResult::Error(e),
};
callback(tid, Box::into_raw(Box::new(result)));
}));
let res = (*mgr).sign(
timeout,
SignArgsCtap1 {
flags,
challenge,
app_ids,
key_handles,
}
.into(),
status_tx,
state_callback,
);
if res.is_ok() {
tid
} else {
0
}
}
#[no_mangle]
pub unsafe extern "C" fn rust_u2f_mgr_cancel(mgr: *mut AuthenticatorService) {
if !mgr.is_null() {
let _ = (*mgr).cancel();
}
}