extern crate base64;
use data_encoding::HEXUPPER;
use libc::{c_int, c_void, size_t};
use crate::ffi::icc::IccChannel;
use crate::ffi::log::platform_log;
use crate::util::raw_string::{FromRawStr, StrFind};
const PLATFORM_SUPPORT_DIRECT_AKA: bool = true;
const LOG_TAG: &str = "aka";
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
extern "C" {
fn platform_perform_aka(
subscription_id: c_int,
in_data: *const c_void,
in_size: size_t,
out_size: *mut size_t,
) -> *mut c_void;
}
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
fn perform_aka(challenge_data: &[u8], subscription_id: i32) -> Result<Vec<u8>, ErrorKind> {
let mut out_size: size_t = 0;
let out_data;
unsafe {
out_data = platform_perform_aka(
subscription_id,
challenge_data.as_ptr() as *const c_void,
challenge_data.len(),
&mut out_size,
);
if out_size <= 0 || out_data.is_null() {
return Err(ErrorKind::FFI);
}
let mut data: Vec<u8> = Vec::with_capacity(out_size);
std::ptr::copy_nonoverlapping(out_data as *const u8, data.as_mut_ptr(), out_size);
libc::free(out_data);
data.set_len(out_size);
return Ok(data);
}
}
#[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))]
fn perform_aka(challenge_data: &[u8], subscription_id: i32) -> Result<Vec<u8>, ErrorKind> {
Err(ErrorKind::FFI)
}
pub struct AkaAlgorithm<'a> {
pub version: i16,
pub algorithm: &'a [u8],
}
pub trait AsAkaAlgorithm<'a> {
type Target;
type Err;
fn as_aka_algorithm(&'a self) -> Result<Self::Target, Self::Err>;
}
impl<'a> AsAkaAlgorithm<'a> for [u8] {
type Target = AkaAlgorithm<'a>;
type Err = ();
fn as_aka_algorithm(&'a self) -> Result<AkaAlgorithm<'a>, ()> {
let mut iter = self.into_iter();
if let Some(5) = iter.position(|c| *c == b'-') {
if self.start_with(b"AKAv1") {
return Ok(AkaAlgorithm {
version: 1,
algorithm: &self[6..],
});
}
}
Err(())
}
}
pub struct AkaChallenge {
pub rand: [u8; 16],
pub autn: [u8; 16],
}
impl FromRawStr for AkaChallenge {
type Err = ();
fn from_raw_str(s: &[u8]) -> Result<AkaChallenge, ()> {
if let Ok(s) = base64::decode(s) {
if s.len() >= 32 {
let mut aka_challenge = AkaChallenge {
rand: [0; 16],
autn: [0; 16],
};
unsafe {
std::ptr::copy_nonoverlapping(
s[..16].as_ptr(),
aka_challenge.rand.as_mut_ptr(),
16,
);
std::ptr::copy_nonoverlapping(
s[16..32].as_ptr(),
aka_challenge.autn.as_mut_ptr(),
16,
);
}
return Ok(aka_challenge);
}
}
Err(())
}
}
pub enum AkaResponse {
Successful(Vec<u8>, Option<(Vec<u8>, Vec<u8>)>),
SyncFailure(Vec<u8>),
}
fn aka_decode_response(data: Vec<u8>) -> Result<AkaResponse, ErrorKind> {
if data.len() >= 2 {
let tag = data[0];
if tag == 0xDB {
let res_length = data[1] as usize;
platform_log(LOG_TAG, format!("res_length:{}", res_length));
if data.len() >= 2 + res_length {
let mut res = Vec::with_capacity(res_length);
res.extend_from_slice(&data[2..2 + res_length]);
platform_log(LOG_TAG, format!("res:{}", &HEXUPPER.encode(&res)));
if data.len() > 2 + res_length {
let ck_length = data[2 + res_length] as usize;
platform_log(LOG_TAG, format!("ck_length:{}", res_length));
if data.len() >= 2 + res_length + 1 + ck_length {
let mut ck = Vec::with_capacity(ck_length);
ck.extend_from_slice(
&data[2 + res_length + 1..2 + res_length + 1 + ck_length],
);
platform_log(LOG_TAG, format!("ck:{}", &HEXUPPER.encode(&ck)));
if data.len() > 2 + res_length + 1 + ck_length {
let ik_length = data[2 + res_length + 1 + ck_length] as usize;
platform_log(LOG_TAG, format!("ik_length:{}", ik_length));
if data.len() >= 2 + res_length + 1 + ck_length + 1 + ik_length {
let mut ik = Vec::with_capacity(ik_length);
ik.extend_from_slice(
&data[2 + res_length + 1 + ck_length + 1
..2 + res_length + 1 + ck_length + 1 + ik_length],
);
platform_log(LOG_TAG, format!("ik:{}", &HEXUPPER.encode(&ik)));
return Ok(AkaResponse::Successful(res, Some((ck, ik))));
}
}
}
}
return Ok(AkaResponse::Successful(res, None));
}
} else if tag == 0xDC {
let auts_length = data[1] as usize;
platform_log(LOG_TAG, format!("auts_length:{}", auts_length));
if data.len() >= 2 + auts_length {
let mut auts = Vec::with_capacity(auts_length);
auts.extend_from_slice(&data[2..2 + auts_length]);
platform_log(LOG_TAG, format!("auts:{}", &HEXUPPER.encode(&auts)));
return Ok(AkaResponse::SyncFailure(auts));
}
}
}
Err(ErrorKind::BadFormat)
}
pub fn aka_do_challenge(
challenge: &AkaChallenge,
subscription_id: i32,
) -> Result<AkaResponse, ErrorKind> {
let mut challenge_data: [u8; 34] = [0; 34];
challenge_data[0] = 16;
challenge_data[17] = 16;
unsafe {
std::ptr::copy_nonoverlapping(
challenge.rand.as_ptr(),
challenge_data[1..17].as_mut_ptr(),
16,
);
std::ptr::copy_nonoverlapping(
challenge.autn.as_ptr(),
challenge_data[18..].as_mut_ptr(),
16,
);
}
if PLATFORM_SUPPORT_DIRECT_AKA {
if let Ok(data) = perform_aka(&challenge_data, subscription_id) {
return aka_decode_response(data);
}
} else {
let aid_bytes: [u8; 7] = [0xA0, 0x00, 0x00, 0x00, 0x87, 0x10, 0x02];
if let Some(channel) = IccChannel::new(&aid_bytes) {
let cla = 0x00;
let ins = 0x88;
let p1 = 0x00;
let p2 = 0x81;
let lc = 0x22;
let _le = 0x00;
if let Ok(data) = channel.icc_exchange_apdu(cla, ins, p1, p2, lc, &challenge_data) {
return aka_decode_response(data);
}
}
}
Err(ErrorKind::FFI)
}
pub enum ErrorKind {
BadFormat,
FFI,
UnknownParameter,
}