|  | extern crate base64; | 
|  | extern crate hex; | 
|  |  | 
|  | #[cfg(test)] | 
|  | mod tests { | 
|  | #[test] | 
|  | fn it_works() { | 
|  | assert_eq!(2 + 2, 4); | 
|  | } | 
|  |  | 
|  | #[test] | 
|  | fn test_key() { | 
|  | let key = "wDp3/3NPEi+R0peokVv010GkDk1mRTp3tUB/lCEVRAA="; | 
|  | let key_buffer = base64::decode_config(key, base64::STANDARD).unwrap(); | 
|  | let s = hex::encode(key_buffer.as_slice()); | 
|  | let key_string = hex::decode(format!("{}{:04x}{:02x}", s, 5, 10)).unwrap(); | 
|  | println!( | 
|  | "key : {}", | 
|  | base64::encode_config(key_string, base64::URL_SAFE) | 
|  | ); | 
|  | } | 
|  | } | 
|  |  | 
|  | // use std::ffi::{CStr, CString}; | 
|  | // use std::os::raw::{c_char, c_uchar, c_ulong}; | 
|  |  | 
|  | // #[no_mangle] | 
|  | // pub extern "C" fn dlsmk_decode( | 
|  | //     key: *const c_uchar, | 
|  | //     qrcode: *const c_uchar, | 
|  | //     qrlen: c_ulong, | 
|  | // ) -> *mut c_char { | 
|  | //     CString::new("Hello ".to_owned()).unwrap().into_raw() | 
|  | // } | 
|  |  | 
|  | #[cfg(any(target_family = "unix", target_os = "android"))] | 
|  | #[allow(non_snake_case)] | 
|  | pub mod android { | 
|  | extern crate base64; | 
|  | extern crate jni; | 
|  |  | 
|  | use self::jni::objects::{JClass, JMap, JObject, JString}; | 
|  | use self::jni::sys::{jboolean, jint, jlong, jstring, JNI_FALSE, JNI_TRUE}; | 
|  | use self::jni::JNIEnv; | 
|  | use std::ffi::CStr; | 
|  |  | 
|  | use log::debug; | 
|  |  | 
|  | use dlqrcode::DaliQrCode; | 
|  |  | 
|  | fn put_data(env: &JNIEnv, map: &JMap, key: &str, value: &str) { | 
|  | map.put( | 
|  | *env.new_string(key).unwrap(), | 
|  | *env.new_string(value).unwrap(), | 
|  | ) | 
|  | .unwrap(); | 
|  | } | 
|  |  | 
|  | #[no_mangle] | 
|  | pub unsafe extern "C" fn Java_com_supwisdom_dlsmk_DLSMKQrCode_decode( | 
|  | env: JNIEnv, | 
|  | _: JClass, | 
|  | key_hex: JString, | 
|  | qrcode: JString, | 
|  | offset: jlong, | 
|  | result: JObject, | 
|  | ) -> jboolean { | 
|  | let key = { | 
|  | let s = env | 
|  | .get_string(key_hex) | 
|  | .expect("invalid key string") | 
|  | .as_ptr(); | 
|  | String::from(CStr::from_ptr(s).to_string_lossy()) | 
|  | }; | 
|  |  | 
|  | let qrcode = { | 
|  | let s = env | 
|  | .get_string(qrcode) | 
|  | .expect("invalid qrcode string") | 
|  | .as_ptr(); | 
|  | String::from(CStr::from_ptr(s).to_string_lossy()) | 
|  | }; | 
|  |  | 
|  | let qrdata = env.get_map(result).expect("invalid qrdata map"); | 
|  |  | 
|  | let (key, step, skew) = { | 
|  | let mut k = [0u8; 32]; | 
|  | if let Ok(v) = base64::decode_config(key, base64::URL_SAFE) { | 
|  | if v.len() != 35 { | 
|  | put_data(&env, &qrdata, "error", "key format error"); | 
|  | return JNI_FALSE; | 
|  | } | 
|  | k.clone_from_slice(&v.as_slice()[..32]); | 
|  | let step = ((v[32] as u64) << 8) | (v[33] as u64); | 
|  | let skew = v[34] as u8; | 
|  | (k, step, skew) | 
|  | } else { | 
|  | put_data( | 
|  | &env, | 
|  | &qrdata, | 
|  | "error", | 
|  | "key must be hex format and 64 characters", | 
|  | ); | 
|  | return JNI_FALSE; | 
|  | } | 
|  | }; | 
|  |  | 
|  | debug!("TOTP step <{}> , skew <{}>", step, skew); | 
|  | let decoder = match DaliQrCode::new(key, None, Some(step), Some(skew), None) { | 
|  | Ok(d) => d, | 
|  | Err(e) => { | 
|  | let s = format!("invalid input parameter {:?}", e); | 
|  | put_data(&env, &qrdata, "error", &s); | 
|  | return JNI_FALSE; | 
|  | } | 
|  | }; | 
|  |  | 
|  | match decoder.decode(qrcode.as_ptr(), qrcode.len(), offset as i32) { | 
|  | Ok(d) => { | 
|  | put_data(&env, &qrdata, "cardno", &d.cardno); | 
|  | put_data(&env, &qrdata, "cardtype", &d.cardtype); | 
|  | put_data(&env, &qrdata, "uid", &d.uid); | 
|  | let sign = dlqrcode::transaction_sign(&d); | 
|  | put_data(&env, &qrdata, "sign", &sign); | 
|  | return JNI_TRUE; | 
|  | } | 
|  | Err(e) => { | 
|  | let s = format!("{:?}", e); | 
|  | put_data(&env, &qrdata, "error", &s); | 
|  | return JNI_FALSE; | 
|  | } | 
|  | }; | 
|  | } | 
|  |  | 
|  | const VERSION: &'static str = env!("CARGO_PKG_VERSION"); | 
|  |  | 
|  | #[no_mangle] | 
|  | pub unsafe extern "C" fn Java_com_supwisdom_dlsmk_DLSMKQrCode_version( | 
|  | env: JNIEnv, | 
|  | _: JClass, | 
|  | ) -> jstring { | 
|  | let s = env | 
|  | .new_string(String::from(VERSION)) | 
|  | .expect("would't get version"); | 
|  | s.into_inner() | 
|  | } | 
|  |  | 
|  | #[no_mangle] | 
|  | pub unsafe extern "C" fn Java_com_supwisdom_dlsmk_DLSMKQrCode_genTac( | 
|  | env: JNIEnv, | 
|  | _: JClass, | 
|  | qrsign: JString, | 
|  | cardno: JString, | 
|  | amount: jint, | 
|  | termDatetime: JString, | 
|  | result: JObject, | 
|  | ) -> jboolean { | 
|  | let qrsign = { | 
|  | let s = env.get_string(qrsign).expect("invalid key string").as_ptr(); | 
|  | String::from(CStr::from_ptr(s).to_string_lossy()) | 
|  | }; | 
|  | let cardno = { | 
|  | let s = env | 
|  | .get_string(cardno) | 
|  | .expect("invalid cardno string") | 
|  | .as_ptr(); | 
|  | String::from(CStr::from_ptr(s).to_string_lossy()) | 
|  | }; | 
|  |  | 
|  | let termDatetime = { | 
|  | let s = env | 
|  | .get_string(termDatetime) | 
|  | .expect("invalid datetime") | 
|  | .as_ptr(); | 
|  | String::from(CStr::from_ptr(s).to_string_lossy()) | 
|  | }; | 
|  |  | 
|  | let result = env.get_map(result).expect("invalid qrdata map"); | 
|  |  | 
|  | if termDatetime.len() != 14 { | 
|  | put_data(&env, &result, "error", "term datetime must be 14 chars"); | 
|  | return JNI_FALSE; | 
|  | } | 
|  |  | 
|  | match dlqrcode::transaction_tac(&cardno, amount, &termDatetime, &qrsign) { | 
|  | Ok(tac) => { | 
|  | put_data(&env, &result, "tac", &tac); | 
|  | JNI_TRUE | 
|  | } | 
|  | Err(e) => { | 
|  | put_data(&env, &result, "error", &format!("{:?}", e)); | 
|  | JNI_FALSE | 
|  | } | 
|  | } | 
|  | } | 
|  | } |