blob: 8a98de0080cf3856832a616ab046bd3806c44cda [file] [log] [blame]
Tang Cheng527387f2020-11-03 09:44:55 +08001extern crate base64;
Tang Cheng88643892020-10-31 22:00:07 +08002extern crate hex;
3
Tang Cheng527387f2020-11-03 09:44:55 +08004use simple_logger::SimpleLogger;
Tang Cheng88643892020-10-31 22:00:07 +08005use std::fmt;
6use std::slice;
7use std::time::Instant;
8use std::time::SystemTime;
9
Tang Cheng527387f2020-11-03 09:44:55 +080010use log::debug;
11
Tang Cheng88643892020-10-31 22:00:07 +080012use sha2::{Digest, Sha256};
13
14use aes::Aes256;
15use block_modes::block_padding::Pkcs7;
16use block_modes::{BlockMode, Cbc};
17use totp_rs::{Algorithm, TOTP};
18
19type Aes256Cbc = Cbc<Aes256, Pkcs7>;
20
21const TOTP_STEP: u64 = 5;
22const TOTP_SKEW: u8 = 3;
23static QR_FIELD_DILIMITER: &str = ":";
24
Tang Cheng527387f2020-11-03 09:44:55 +080025static MASTER: &str = "wDp3/3NPEi+R0peokVv010GkDk1mRTp3tUB/lCEVRAA=";
26
27static MASTER_IV: [u8; 16] = [0u8; 16];
28
29fn setup_logger() -> bool {
30 SimpleLogger::new().init().unwrap();
31 true
32}
33
Tang Cheng88643892020-10-31 22:00:07 +080034pub struct DaliQrCode {
35 master_key: Vec<u8>,
36 iv: Vec<u8>,
37 totp_step: u64,
38 totp_skew: u8,
39 totp_seed: Vec<u8>,
40}
41
42#[derive(Debug)]
43pub struct DaliQrData {
44 pub uid: String,
45 pub cardno: String,
46 pub cardtype: String,
47 pub totp: String,
48 pub nonce: String,
49 sign: Vec<u8>,
50}
51
52impl DaliQrData {
Tang Cheng8d072502020-10-31 22:02:25 +080053 #[allow(dead_code)]
Tang Cheng88643892020-10-31 22:00:07 +080054 fn new() -> Self {
55 Self {
Tang Cheng31462302020-11-02 09:18:47 +080056 uid: Default::default(),
57 cardno: Default::default(),
58 cardtype: Default::default(),
59 totp: Default::default(),
60 nonce: Default::default(),
Tang Cheng88643892020-10-31 22:00:07 +080061 sign: Vec::new(),
62 }
63 }
64
65 fn from_qrcode(qr_fields: &Vec<Vec<u8>>) -> Result<Self> {
66 if qr_fields.len() < 6 {
67 return Err(DecodeError::new("qrcode fields length must grater than 6."));
68 }
69 let sign = qr_fields[5].to_vec();
70 Ok(Self {
71 uid: String::from_utf8_lossy(&qr_fields[0].as_slice()).to_string(),
72 cardno: String::from_utf8_lossy(&qr_fields[1].as_slice()).to_string(),
73 cardtype: String::from_utf8_lossy(&qr_fields[2].as_slice()).to_string(),
74 totp: String::from_utf8_lossy(&qr_fields[3].as_slice()).to_string(),
75 nonce: String::from_utf8_lossy(&qr_fields[4].as_slice()).to_string(),
76 sign: sign,
77 })
78 }
79
80 fn update_sign(&mut self, sign: &Vec<u8>) {
81 self.sign = sign.to_vec();
82 }
83
Tang Cheng527387f2020-11-03 09:44:55 +080084 pub fn to_qrdata(&self) -> String {
Tang Cheng88643892020-10-31 22:00:07 +080085 let v = vec![
86 String::from(&self.uid),
87 String::from(&self.cardno),
88 String::from(&self.cardtype),
89 String::from(&self.totp),
90 String::from(&self.nonce),
91 ];
92 v.join(QR_FIELD_DILIMITER)
93 }
94}
95
96#[derive(Debug, Clone)]
97pub struct DecodeError {
98 message: String,
99}
100
101impl fmt::Display for DecodeError {
102 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
103 write!(f, "Decode Qrcode error {}", self.message)
104 }
105}
106
107impl DecodeError {
108 fn new(message: &str) -> Self {
109 Self {
110 message: String::from(message),
111 }
112 }
113}
114
115type Result<T> = std::result::Result<T, DecodeError>;
116
117const KEY_LEN: usize = 32;
118impl DaliQrCode {
119 pub fn new(
120 key: [u8; KEY_LEN],
121 iv: Option<[u8; 16]>,
122 step: Option<u64>,
123 skew: Option<u8>,
124 seed: Option<Vec<u8>>,
125 ) -> Result<Self> {
Tang Cheng527387f2020-11-03 09:44:55 +0800126 setup_logger();
127
Tang Cheng88643892020-10-31 22:00:07 +0800128 let key = key.to_vec();
129 if key.len() != KEY_LEN {
130 return Err(DecodeError::new(&format!(
131 "key size must be {} bytes",
132 KEY_LEN
133 )));
134 }
135 let iv = if let Some(v) = iv {
136 v.to_vec()
137 } else {
138 hex::decode("55b6f5b3287c535f8274b99354676d0e").unwrap()
139 };
140
141 if iv.len() != 16 {
142 return Err(DecodeError::new("IV size must be 16 bytes"));
143 }
144
Tang Cheng31462302020-11-02 09:18:47 +0800145 let totp_step = step.unwrap_or(TOTP_STEP);
146 let totp_skew = skew.unwrap_or(TOTP_SKEW);
147 let totp_seed = seed.unwrap_or(hex::decode("125ea2f97689988b6501").unwrap());
Tang Cheng88643892020-10-31 22:00:07 +0800148
149 Ok(Self {
150 master_key: key,
Tang Cheng31462302020-11-02 09:18:47 +0800151 iv,
152 totp_seed,
153 totp_skew,
154 totp_step,
Tang Cheng88643892020-10-31 22:00:07 +0800155 })
156 }
157
Tang Cheng31462302020-11-02 09:18:47 +0800158 fn split_qrdata(&self, qrcode: &Vec<u8>, qrdata: &mut Vec<Vec<u8>>) -> Result<()> {
Tang Cheng88643892020-10-31 22:00:07 +0800159 let v = QR_FIELD_DILIMITER.as_bytes()[0];
160 let mut j = 0;
161 for i in 0..qrcode.len() {
162 if qrcode[i] == v {
163 if i > j {
164 let mut s = Vec::new();
165 s.extend_from_slice(&qrcode[j..i]);
Tang Cheng31462302020-11-02 09:18:47 +0800166 qrdata.push(s);
Tang Cheng88643892020-10-31 22:00:07 +0800167 }
168 j = i + 1;
169 }
170 }
171 if j < qrcode.len() {
172 let mut s = Vec::new();
173 s.extend_from_slice(&qrcode[j..]);
Tang Cheng31462302020-11-02 09:18:47 +0800174 qrdata.push(s);
Tang Cheng88643892020-10-31 22:00:07 +0800175 }
Tang Cheng31462302020-11-02 09:18:47 +0800176 Ok(())
177 }
Tang Cheng88643892020-10-31 22:00:07 +0800178
Tang Cheng31462302020-11-02 09:18:47 +0800179 pub fn decode(&self, qrcode: *const u8, len: usize, secs_offset: i32) -> Result<DaliQrData> {
180 let qrcode = self.decode_qrcode(qrcode, len)?;
181 let mut qr_fields: Vec<Vec<u8>> = Vec::new();
182
183 self.split_qrdata(&qrcode, &mut qr_fields)?;
Tang Cheng527387f2020-11-03 09:44:55 +0800184
Tang Cheng31462302020-11-02 09:18:47 +0800185 let qr_data = DaliQrData::from_qrcode(&qr_fields)?;
186 if qr_data.uid.len() < 16 {
187 return Err(DecodeError::new("uid must grater than 16"));
Tang Cheng88643892020-10-31 22:00:07 +0800188 }
189
Tang Cheng31462302020-11-02 09:18:47 +0800190 self.check_qrcode_sign(&qr_data)?;
191
Tang Cheng88643892020-10-31 22:00:07 +0800192 let totp = self.new_totp();
193 let time = self.totp_time(secs_offset);
194 if totp.check(&qr_data.totp, time) {
195 Ok(qr_data)
196 } else {
197 Err(DecodeError::new("qrcode totp error"))
198 }
199 }
200
Tang Cheng31462302020-11-02 09:18:47 +0800201 fn check_qrcode_sign(&self, qr_data: &DaliQrData) -> Result<()> {
Tang Cheng88643892020-10-31 22:00:07 +0800202 let sign = self.calc_sign(qr_data);
Tang Cheng527387f2020-11-03 09:44:55 +0800203 debug!("sign expect : {}", hex::encode(sign.clone()));
204 debug!("sign actual : {}", hex::encode(qr_data.sign.clone()));
205 if sign.iter().cloned().eq(qr_data.sign.iter().cloned()) {
Tang Cheng31462302020-11-02 09:18:47 +0800206 Ok(())
Tang Cheng88643892020-10-31 22:00:07 +0800207 } else {
Tang Cheng31462302020-11-02 09:18:47 +0800208 Err(DecodeError::new("sign is invalid"))
Tang Cheng88643892020-10-31 22:00:07 +0800209 }
210 }
211
Tang Cheng527387f2020-11-03 09:44:55 +0800212 fn decode_qrcode(&self, qrcode: *const u8, len: usize) -> Result<Vec<u8>> {
Tang Cheng88643892020-10-31 22:00:07 +0800213 let cipher = match Aes256Cbc::new_var(&self.master_key, &self.iv) {
214 Ok(c) => c,
215 Err(e) => return Err(DecodeError::new(&format!("aes key error {:?}", e))),
216 };
217
218 let qrcode = unsafe {
219 let s = slice::from_raw_parts(qrcode, len);
Tang Cheng527387f2020-11-03 09:44:55 +0800220 if let Ok(code) = base64::decode_config(s, base64::URL_SAFE) {
Tang Cheng88643892020-10-31 22:00:07 +0800221 code
222 } else {
223 return Err(DecodeError::new("data base64 decode error"));
224 }
225 };
226
Tang Cheng88643892020-10-31 22:00:07 +0800227 match cipher.decrypt_vec(&qrcode) {
228 Ok(data) => Ok(data),
229 Err(e) => Err(DecodeError::new(&format!("block error {:?}", e))),
230 }
231 }
232
233 fn totp_time(&self, secs_offset: i32) -> u64 {
234 let time = SystemTime::now()
235 .duration_since(SystemTime::UNIX_EPOCH)
236 .unwrap()
237 .as_secs();
Tang Cheng527387f2020-11-03 09:44:55 +0800238 debug!("totp system time : {}, offset {}", time, secs_offset);
Tang Cheng88643892020-10-31 22:00:07 +0800239 if secs_offset > 0 {
240 time + (secs_offset as u64)
241 } else {
Tang Cheng527387f2020-11-03 09:44:55 +0800242 time - (-secs_offset as u64)
Tang Cheng88643892020-10-31 22:00:07 +0800243 }
244 }
245
246 fn new_totp(&self) -> TOTP<Vec<u8>> {
247 let seed = self.totp_seed.clone();
248 TOTP::new(Algorithm::SHA1, 8, self.totp_skew, self.totp_step, seed)
249 }
250
251 fn encode_qrcode(&self, qr_data: &DaliQrData) -> Result<String> {
252 let plain_text = qr_data.to_qrdata();
253 let cipher = match Aes256Cbc::new_var(&self.master_key, &self.iv) {
254 Ok(c) => c,
255 Err(e) => return Err(DecodeError::new(&format!("aes key error {:?}", e))),
256 };
257 let mut buffer = Vec::new();
258 buffer.extend_from_slice(&plain_text.as_bytes());
259 buffer.push(QR_FIELD_DILIMITER.as_bytes()[0]);
260 buffer.extend_from_slice(qr_data.sign.as_slice());
261 let crypt_data = cipher.encrypt_vec(buffer.as_slice());
Tang Cheng527387f2020-11-03 09:44:55 +0800262 Ok(base64::encode_config(
263 crypt_data.as_slice(),
264 base64::URL_SAFE,
265 ))
Tang Cheng88643892020-10-31 22:00:07 +0800266 }
267
268 fn calc_sign(&self, qr_data: &DaliQrData) -> Vec<u8> {
269 let mut hasher = Sha256::new();
270 hasher.update("{dlsmk_}".as_bytes());
271 hasher.update(qr_data.uid.as_bytes());
272 let salt = hasher.finalize();
273 let mut hasher = Sha256::new();
Tang Cheng527387f2020-11-03 09:44:55 +0800274 debug!("qrdata sign : {}", qr_data.to_qrdata());
Tang Cheng88643892020-10-31 22:00:07 +0800275 hasher.update(qr_data.to_qrdata().as_bytes());
276 hasher.update(salt);
277 hasher.finalize().to_vec()
278 }
279
280 pub fn encode(&self, qr_data: &mut DaliQrData, secs_offset: i32) -> Result<String> {
281 if qr_data.nonce.len() == 0 {
282 qr_data.nonce = format!("{:02}", Instant::now().elapsed().as_secs() % 100);
283 }
284 if qr_data.totp.len() == 0 {
285 let totp = self.new_totp();
286 let time = self.totp_time(secs_offset);
287 qr_data.totp = totp.generate(time);
288 }
289 let sign = self.calc_sign(qr_data);
290 qr_data.update_sign(&sign);
Tang Cheng527387f2020-11-03 09:44:55 +0800291 debug!("encode qrcode sign : {}", hex::encode(sign));
Tang Cheng88643892020-10-31 22:00:07 +0800292 self.encode_qrcode(qr_data)
293 }
294}
295
Tang Cheng527387f2020-11-03 09:44:55 +0800296pub fn transaction_sign(qrdata: &DaliQrData) -> String {
297 let sign_str = qrdata.to_qrdata();
298 let key = Aes256Cbc::new_var(MASTER.as_bytes(), &MASTER_IV).unwrap();
299
300 let cipher = key.encrypt_vec(sign_str.as_bytes());
301 base64::encode_config(cipher, base64::URL_SAFE)
302}
303
304pub fn transaction_tac(
305 cardno: &str,
306 amount: i32,
307 term_date_time: &str,
308 sign: &str,
309) -> Result<String> {
310 let sign = unsafe {
311 if let Ok(s) = base64::decode_config(sign, base64::URL_SAFE) {
312 let key = Aes256Cbc::new_var(MASTER.as_bytes(), &MASTER_IV).unwrap();
313 if let Ok(k) = key.decrypt_vec(&s[..]) {
314 String::from_utf8_unchecked(k)
315 } else {
316 return Err(DecodeError::new("sign data error"));
317 }
318 } else {
319 return Err(DecodeError::new("sign format invalid"));
320 }
321 };
322
323 let fields: Vec<&str> = sign.split(QR_FIELD_DILIMITER).collect();
324 if fields.len() < 4 || fields[1] != cardno {
325 return Err(DecodeError::new("sign is invalidated"));
326 }
327
328 let tac_buffer = format!("{}{}{}", amount, term_date_time, "{dlsmk}");
329 let mut hasher = Sha256::new();
330 hasher.update(sign);
331 hasher.update(tac_buffer.as_bytes());
332 let tac = base64::encode_config(hasher.finalize(), base64::URL_SAFE);
333 Ok(tac)
334}
335
Tang Cheng88643892020-10-31 22:00:07 +0800336#[cfg(test)]
337mod tests {
338 use super::*;
339 use base64::decode;
340 use std::convert::TryInto;
341 const KEYLEN: usize = 32;
Tang Cheng527387f2020-11-03 09:44:55 +0800342
Tang Cheng88643892020-10-31 22:00:07 +0800343 #[test]
344 fn it_works() {
345 assert_eq!(2 + 2, 4);
346 }
347
348 #[test]
349 fn aes_test() {
350 let mut key = [0u8; KEYLEN];
351 let s = decode("Vbb1syh8U1+CdLmTVGdtDiVvKBQ81n4GmgBEO/ohSbU=").unwrap();
352 key.clone_from_slice(&s.as_slice()[..KEYLEN]);
353
354 let iv: [u8; 16] = {
355 let s = hex::decode("55b6f5b3287c535f8274b99354676d0e").unwrap();
356 s.into_boxed_slice().as_ref().try_into().unwrap()
357 };
358
359 let aes_key = Aes256Cbc::new_var(&key, &iv).unwrap();
360 let plaintext = String::from("hello ldldldf ldfldl dslfasdamf sdmfdfdf");
361
362 let mut buffer = Vec::new();
363 buffer.extend_from_slice(plaintext.as_bytes());
364 println!(
365 "plain len : {} , buffer len : {}",
366 plaintext.len(),
367 buffer.len()
368 );
369 let cipher_data = aes_key.encrypt_vec(&buffer);
370
371 println!(
372 "cipher len : {}, last {}",
373 cipher_data.len(),
374 cipher_data[cipher_data.len() - 1] as u32
375 );
376
377 let aes_key = Aes256Cbc::new_var(&key, &iv).unwrap();
378 let _ = aes_key.decrypt_vec(&cipher_data);
379 }
380
381 fn get_key() -> ([u8; KEYLEN], [u8; 16]) {
382 let mut key = [0u8; KEYLEN];
Tang Cheng527387f2020-11-03 09:44:55 +0800383 let s = base64::decode("wDp3/3NPEi+R0peokVv010GkDk1mRTp3tUB/lCEVRAA=").unwrap();
Tang Cheng88643892020-10-31 22:00:07 +0800384 key.clone_from_slice(&s.as_slice()[..KEYLEN]);
385
386 let iv: [u8; 16] = {
387 let s = hex::decode("55b6f5b3287c535f8274b99354676d0e").unwrap();
388 s.into_boxed_slice().as_ref().try_into().unwrap()
389 };
390 (key, iv)
391 }
392
393 #[test]
394 fn check_qrcode_encode() {
395 let expect_qrcode = String::from("6lHyFX_vg5U2hymn8OsdNUD7dT0-sCmEQkKrm9cnzHlku6-FYxuL6nP5YR2Fve8Sfj-Asd-3dfQUkaiqqbfQWO8B_811B3uhHmGm9IjlpLicz_c1H1_ORb9tJl-IhMKu");
396 // let buffer = base64_url::decode(expect_qrcode.as_bytes()).unwrap();
397
398 // println!("encrypt buffer <{}>", buffer.len());
399 // println!("decode b64<{}>", hex::encode(buffer.clone()));
400 // let aes_key = Aes256Cbc::new_var(&key, &iv).unwrap();
401 // let data = aes_key.decrypt_vec(&buffer).unwrap();
402 // println!("data : {}", String::from_utf8_lossy(data.as_slice()));
403
404 let (key, iv) = get_key();
405
406 let mut qr_data = DaliQrData::new();
407 qr_data.uid = String::from("0a5de6ce985d43989b7ebe64ad8eb9c3");
408 qr_data.cardno = String::from("00001252");
409 qr_data.cardtype = String::from("80");
410 qr_data.nonce = String::from("ac");
411 qr_data.totp = String::from("50053019");
412
413 let dali_qrcode = DaliQrCode::new(key, Some(iv), None, None, None).unwrap();
414
415 match dali_qrcode.encode(&mut qr_data, 0) {
416 Ok(qrcode) => {
417 assert_eq!(qrcode, expect_qrcode);
418 }
419 Err(e) => {
420 panic!("error {}", e);
421 }
422 }
423 }
424
Tang Cheng527387f2020-11-03 09:44:55 +0800425 fn get_qrdata(qr_data: &mut DaliQrData) -> () {
Tang Cheng88643892020-10-31 22:00:07 +0800426 qr_data.uid = String::from("0a5de6ce985d43989b7ebe64ad8eb9c3");
427 qr_data.cardno = String::from("00001252");
428 qr_data.cardtype = String::from("80");
Tang Cheng527387f2020-11-03 09:44:55 +0800429 }
Tang Cheng88643892020-10-31 22:00:07 +0800430
Tang Cheng527387f2020-11-03 09:44:55 +0800431 fn test_qr_decoder(decoder: &DaliQrCode, qrcode: &str, offset: i32) -> bool {
432 match decoder.decode(qrcode.as_ptr(), qrcode.len(), offset) {
433 Ok(_) => true,
434 Err(_) => false,
435 }
436 }
437
438 #[test]
439 fn check_qrcode_decode() {
440 let (key, iv) = get_key();
441
442 let mut qr_data = DaliQrData::new();
443 get_qrdata(&mut qr_data);
444
445 let dali_qrcode = DaliQrCode::new(key, Some(iv), Some(30u64), Some(3u8), None).unwrap();
Tang Cheng88643892020-10-31 22:00:07 +0800446
447 match dali_qrcode.encode(&mut qr_data, 0) {
448 Ok(qrcode) => {
Tang Cheng527387f2020-11-03 09:44:55 +0800449 // println!("qrdata : {:?}", qr_data);
450 debug!("encode qrcode : {}", qrcode);
451 assert_eq!(test_qr_decoder(&dali_qrcode, &qrcode, -20), true);
452 assert_eq!(test_qr_decoder(&dali_qrcode, &qrcode, -300), false);
453 assert_eq!(test_qr_decoder(&dali_qrcode, &qrcode, 49), true);
454 assert_eq!(test_qr_decoder(&dali_qrcode, &qrcode, 65), true);
Tang Cheng88643892020-10-31 22:00:07 +0800455 }
456 Err(e) => {
457 panic!("error {}", e);
458 }
459 }
460 }
Tang Cheng527387f2020-11-03 09:44:55 +0800461
462 #[test]
463 fn test_tac() {
464 let qrcode = "Ntd0wHly2IiweNP61JiewTmZ27JM3Vs-vjZaz45Ly8G1_lgcdkMw1QClLfKm-9pVTvT0pvrfhMpRgvh9UcQ26UYibVeczWYMtatN4x1OlsGM2cKXooCT1d-ika480wWq";
465 let tac = "8tck8-ljatwwcbtzlgty3l-tq7td3evbjvnzkuusrew";
466
467 let (key, iv) = get_key();
468 let decoder = DaliQrCode::new(key, Some(iv), Some(30u64), Some(3u8), None).unwrap();
469 match decoder.decode(qrcode.as_bytes().as_ptr(), qrcode.len(), -300) {
470 Ok(data) => {
471 let sign = transaction_sign(&data);
472 let actual_tac =
473 transaction_tac(&data.cardno, 100, "20201102135834", &sign).unwrap();
474 assert_eq!(actual_tac, tac);
475 }
476 Err(e) => panic!("qrcode error {:?}", e),
477 }
478 }
Tang Cheng88643892020-10-31 22:00:07 +0800479}