Tang Cheng | 527387f | 2020-11-03 09:44:55 +0800 | [diff] [blame] | 1 | extern crate base64; |
| 2 | extern crate hex; |
| 3 | |
Tang Cheng | 8864389 | 2020-10-31 22:00:07 +0800 | [diff] [blame] | 4 | #[cfg(test)] |
| 5 | mod tests { |
| 6 | #[test] |
| 7 | fn it_works() { |
| 8 | assert_eq!(2 + 2, 4); |
| 9 | } |
Tang Cheng | 527387f | 2020-11-03 09:44:55 +0800 | [diff] [blame] | 10 | |
| 11 | #[test] |
| 12 | fn test_key() { |
| 13 | let key = "wDp3/3NPEi+R0peokVv010GkDk1mRTp3tUB/lCEVRAA="; |
| 14 | let key_buffer = base64::decode_config(key, base64::STANDARD).unwrap(); |
| 15 | let s = hex::encode(key_buffer.as_slice()); |
Tang Cheng | b66a4d8 | 2020-11-03 11:17:43 +0800 | [diff] [blame] | 16 | let key_string = hex::decode(format!("{}{:04x}{:02x}", s, 5, 3)).unwrap(); |
Tang Cheng | 527387f | 2020-11-03 09:44:55 +0800 | [diff] [blame] | 17 | println!( |
| 18 | "key : {}", |
| 19 | base64::encode_config(key_string, base64::URL_SAFE) |
| 20 | ); |
| 21 | } |
Tang Cheng | 8864389 | 2020-10-31 22:00:07 +0800 | [diff] [blame] | 22 | } |
| 23 | |
| 24 | // use std::ffi::{CStr, CString}; |
| 25 | // use std::os::raw::{c_char, c_uchar, c_ulong}; |
| 26 | |
| 27 | // #[no_mangle] |
| 28 | // pub extern "C" fn dlsmk_decode( |
| 29 | // key: *const c_uchar, |
| 30 | // qrcode: *const c_uchar, |
| 31 | // qrlen: c_ulong, |
| 32 | // ) -> *mut c_char { |
| 33 | // CString::new("Hello ".to_owned()).unwrap().into_raw() |
| 34 | // } |
| 35 | |
Tang Cheng | 8864389 | 2020-10-31 22:00:07 +0800 | [diff] [blame] | 36 | #[cfg(any(target_family = "unix", target_os = "android"))] |
Tang Cheng | 8864389 | 2020-10-31 22:00:07 +0800 | [diff] [blame] | 37 | #[allow(non_snake_case)] |
| 38 | pub mod android { |
Tang Cheng | 527387f | 2020-11-03 09:44:55 +0800 | [diff] [blame] | 39 | extern crate base64; |
Tang Cheng | 8864389 | 2020-10-31 22:00:07 +0800 | [diff] [blame] | 40 | extern crate jni; |
| 41 | |
Tang Cheng | 8d07250 | 2020-10-31 22:02:25 +0800 | [diff] [blame] | 42 | use self::jni::objects::{JClass, JMap, JObject, JString}; |
Tang Cheng | 0691adc | 2020-11-03 11:43:58 +0800 | [diff] [blame] | 43 | use self::jni::sys::{jboolean, jint, jlong, jstring, JNI_FALSE, JNI_TRUE}; |
Tang Cheng | 8864389 | 2020-10-31 22:00:07 +0800 | [diff] [blame] | 44 | use self::jni::JNIEnv; |
Tang Cheng | 333dedb | 2020-11-03 12:27:37 +0800 | [diff] [blame^] | 45 | use std::ffi::CStr; |
Tang Cheng | 8864389 | 2020-10-31 22:00:07 +0800 | [diff] [blame] | 46 | |
Tang Cheng | 527387f | 2020-11-03 09:44:55 +0800 | [diff] [blame] | 47 | use log::debug; |
| 48 | |
Tang Cheng | 8864389 | 2020-10-31 22:00:07 +0800 | [diff] [blame] | 49 | use dlqrcode::DaliQrCode; |
| 50 | |
Tang Cheng | 8d07250 | 2020-10-31 22:02:25 +0800 | [diff] [blame] | 51 | fn put_data(env: &JNIEnv, map: &JMap, key: &str, value: &str) { |
| 52 | map.put( |
| 53 | *env.new_string(key).unwrap(), |
| 54 | *env.new_string(value).unwrap(), |
| 55 | ) |
| 56 | .unwrap(); |
| 57 | } |
| 58 | |
Tang Cheng | 8864389 | 2020-10-31 22:00:07 +0800 | [diff] [blame] | 59 | #[no_mangle] |
| 60 | pub unsafe extern "C" fn Java_com_supwisdom_dlsmk_DLSMKQrCode_decode( |
| 61 | env: JNIEnv, |
| 62 | _: JClass, |
| 63 | key_hex: JString, |
| 64 | qrcode: JString, |
| 65 | offset: jlong, |
| 66 | result: JObject, |
| 67 | ) -> jboolean { |
Tang Cheng | 8864389 | 2020-10-31 22:00:07 +0800 | [diff] [blame] | 68 | let key = { |
Tang Cheng | 8d07250 | 2020-10-31 22:02:25 +0800 | [diff] [blame] | 69 | let s = env |
| 70 | .get_string(key_hex) |
| 71 | .expect("invalid key string") |
| 72 | .as_ptr(); |
Tang Cheng | 333dedb | 2020-11-03 12:27:37 +0800 | [diff] [blame^] | 73 | String::from(CStr::from_ptr(s).to_string_lossy()) |
Tang Cheng | 8d07250 | 2020-10-31 22:02:25 +0800 | [diff] [blame] | 74 | }; |
| 75 | |
| 76 | let qrcode = { |
| 77 | let s = env |
| 78 | .get_string(qrcode) |
| 79 | .expect("invalid qrcode string") |
| 80 | .as_ptr(); |
Tang Cheng | 333dedb | 2020-11-03 12:27:37 +0800 | [diff] [blame^] | 81 | String::from(CStr::from_ptr(s).to_string_lossy()) |
Tang Cheng | 8d07250 | 2020-10-31 22:02:25 +0800 | [diff] [blame] | 82 | }; |
| 83 | |
| 84 | let qrdata = env.get_map(result).expect("invalid qrdata map"); |
| 85 | |
Tang Cheng | 527387f | 2020-11-03 09:44:55 +0800 | [diff] [blame] | 86 | let (key, step, skew) = { |
Tang Cheng | 8d07250 | 2020-10-31 22:02:25 +0800 | [diff] [blame] | 87 | let mut k = [0u8; 32]; |
Tang Cheng | 333dedb | 2020-11-03 12:27:37 +0800 | [diff] [blame^] | 88 | if let Ok(v) = base64::decode_config(key, base64::URL_SAFE) { |
Tang Cheng | 527387f | 2020-11-03 09:44:55 +0800 | [diff] [blame] | 89 | if v.len() != 35 { |
| 90 | put_data(&env, &qrdata, "error", "key format error"); |
| 91 | return JNI_FALSE; |
| 92 | } |
| 93 | k.clone_from_slice(&v.as_slice()[..32]); |
| 94 | let step = ((v[32] as u64) << 8) | (v[33] as u64); |
| 95 | let skew = v[34] as u8; |
| 96 | (k, step, skew) |
Tang Cheng | 8864389 | 2020-10-31 22:00:07 +0800 | [diff] [blame] | 97 | } else { |
Tang Cheng | 8d07250 | 2020-10-31 22:02:25 +0800 | [diff] [blame] | 98 | put_data( |
| 99 | &env, |
| 100 | &qrdata, |
| 101 | "error", |
| 102 | "key must be hex format and 64 characters", |
| 103 | ); |
Tang Cheng | 8864389 | 2020-10-31 22:00:07 +0800 | [diff] [blame] | 104 | return JNI_FALSE; |
| 105 | } |
| 106 | }; |
| 107 | |
Tang Cheng | 527387f | 2020-11-03 09:44:55 +0800 | [diff] [blame] | 108 | debug!("TOTP step <{}> , skew <{}>", step, skew); |
| 109 | let decoder = match DaliQrCode::new(key, None, Some(step), Some(skew), None) { |
Tang Cheng | 8864389 | 2020-10-31 22:00:07 +0800 | [diff] [blame] | 110 | Ok(d) => d, |
Tang Cheng | 8d07250 | 2020-10-31 22:02:25 +0800 | [diff] [blame] | 111 | Err(e) => { |
| 112 | let s = format!("invalid input parameter {:?}", e); |
| 113 | put_data(&env, &qrdata, "error", &s); |
| 114 | return JNI_FALSE; |
| 115 | } |
Tang Cheng | 8864389 | 2020-10-31 22:00:07 +0800 | [diff] [blame] | 116 | }; |
| 117 | |
Tang Cheng | 333dedb | 2020-11-03 12:27:37 +0800 | [diff] [blame^] | 118 | |
Tang Cheng | 527387f | 2020-11-03 09:44:55 +0800 | [diff] [blame] | 119 | match decoder.decode(qrcode.as_ptr(), qrcode.len(), offset as i32) { |
Tang Cheng | 8864389 | 2020-10-31 22:00:07 +0800 | [diff] [blame] | 120 | Ok(d) => { |
Tang Cheng | 527387f | 2020-11-03 09:44:55 +0800 | [diff] [blame] | 121 | put_data(&env, &qrdata, "cardno", &d.cardno); |
| 122 | put_data(&env, &qrdata, "cardtype", &d.cardtype); |
| 123 | put_data(&env, &qrdata, "uid", &d.uid); |
| 124 | let sign = dlqrcode::transaction_sign(&d); |
| 125 | put_data(&env, &qrdata, "sign", &sign); |
Tang Cheng | 8864389 | 2020-10-31 22:00:07 +0800 | [diff] [blame] | 126 | return JNI_TRUE; |
| 127 | } |
| 128 | Err(e) => { |
Tang Cheng | 8d07250 | 2020-10-31 22:02:25 +0800 | [diff] [blame] | 129 | let s = format!("{:?}", e); |
| 130 | put_data(&env, &qrdata, "error", &s); |
Tang Cheng | 8864389 | 2020-10-31 22:00:07 +0800 | [diff] [blame] | 131 | return JNI_FALSE; |
Tang Cheng | 8d07250 | 2020-10-31 22:02:25 +0800 | [diff] [blame] | 132 | } |
Tang Cheng | 8864389 | 2020-10-31 22:00:07 +0800 | [diff] [blame] | 133 | }; |
| 134 | } |
Tang Cheng | 527387f | 2020-11-03 09:44:55 +0800 | [diff] [blame] | 135 | |
Tang Cheng | 0691adc | 2020-11-03 11:43:58 +0800 | [diff] [blame] | 136 | const VERSION: &'static str = env!("CARGO_PKG_VERSION"); |
| 137 | |
| 138 | #[no_mangle] |
Tang Cheng | 333dedb | 2020-11-03 12:27:37 +0800 | [diff] [blame^] | 139 | pub unsafe extern "C" fn Java_com_supwisdom_dlsmk_DLSMKQrCode_version( |
| 140 | env: JNIEnv, |
| 141 | _: JClass, |
| 142 | ) -> jstring { |
| 143 | let s = env |
| 144 | .new_string(String::from(VERSION)) |
| 145 | .expect("would't get version"); |
Tang Cheng | 0691adc | 2020-11-03 11:43:58 +0800 | [diff] [blame] | 146 | s.into_inner() |
| 147 | } |
| 148 | |
Tang Cheng | 527387f | 2020-11-03 09:44:55 +0800 | [diff] [blame] | 149 | #[no_mangle] |
| 150 | pub unsafe extern "C" fn Java_com_supwisdom_dlsmk_DLSMKQrCode_genTac( |
| 151 | env: JNIEnv, |
| 152 | _: JClass, |
| 153 | qrsign: JString, |
| 154 | cardno: JString, |
| 155 | amount: jint, |
| 156 | termDatetime: JString, |
| 157 | result: JObject, |
| 158 | ) -> jboolean { |
| 159 | let qrsign = { |
| 160 | let s = env.get_string(qrsign).expect("invalid key string").as_ptr(); |
Tang Cheng | 333dedb | 2020-11-03 12:27:37 +0800 | [diff] [blame^] | 161 | String::from(CStr::from_ptr(s).to_string_lossy()) |
Tang Cheng | 527387f | 2020-11-03 09:44:55 +0800 | [diff] [blame] | 162 | }; |
| 163 | let cardno = { |
| 164 | let s = env |
| 165 | .get_string(cardno) |
| 166 | .expect("invalid cardno string") |
| 167 | .as_ptr(); |
Tang Cheng | 333dedb | 2020-11-03 12:27:37 +0800 | [diff] [blame^] | 168 | String::from(CStr::from_ptr(s).to_string_lossy()) |
Tang Cheng | 527387f | 2020-11-03 09:44:55 +0800 | [diff] [blame] | 169 | }; |
| 170 | |
| 171 | let termDatetime = { |
| 172 | let s = env |
| 173 | .get_string(termDatetime) |
| 174 | .expect("invalid datetime") |
| 175 | .as_ptr(); |
Tang Cheng | 333dedb | 2020-11-03 12:27:37 +0800 | [diff] [blame^] | 176 | String::from(CStr::from_ptr(s).to_string_lossy()) |
Tang Cheng | 527387f | 2020-11-03 09:44:55 +0800 | [diff] [blame] | 177 | }; |
| 178 | |
| 179 | let result = env.get_map(result).expect("invalid qrdata map"); |
| 180 | |
| 181 | if termDatetime.len() != 14 { |
| 182 | put_data(&env, &result, "error", "term datetime must be 14 chars"); |
| 183 | return JNI_FALSE; |
| 184 | } |
| 185 | |
Tang Cheng | 333dedb | 2020-11-03 12:27:37 +0800 | [diff] [blame^] | 186 | match dlqrcode::transaction_tac( |
| 187 | &cardno, |
| 188 | amount, |
| 189 | &termDatetime, |
| 190 | &qrsign |
| 191 | ) { |
Tang Cheng | 527387f | 2020-11-03 09:44:55 +0800 | [diff] [blame] | 192 | Ok(tac) => { |
| 193 | put_data(&env, &result, "tac", &tac); |
| 194 | JNI_TRUE |
| 195 | } |
| 196 | Err(e) => { |
| 197 | put_data(&env, &result, "error", &format!("{:?}", e)); |
| 198 | JNI_FALSE |
| 199 | } |
| 200 | } |
| 201 | } |
Tang Cheng | 8864389 | 2020-10-31 22:00:07 +0800 | [diff] [blame] | 202 | } |