blob: a28a70d1cecf1390f754e93a25b4496e2be6b764 [file] [log] [blame]
Tang Cheng88643892020-10-31 22:00:07 +08001extern crate base64_url;
2extern crate hex;
3
4use std::fmt;
5use std::slice;
6use std::time::Instant;
7use std::time::SystemTime;
8
9use sha2::{Digest, Sha256};
10
11use aes::Aes256;
12use block_modes::block_padding::Pkcs7;
13use block_modes::{BlockMode, Cbc};
14use totp_rs::{Algorithm, TOTP};
15
16type Aes256Cbc = Cbc<Aes256, Pkcs7>;
17
18const TOTP_STEP: u64 = 5;
19const TOTP_SKEW: u8 = 3;
20static QR_FIELD_DILIMITER: &str = ":";
21
22pub struct DaliQrCode {
23 master_key: Vec<u8>,
24 iv: Vec<u8>,
25 totp_step: u64,
26 totp_skew: u8,
27 totp_seed: Vec<u8>,
28}
29
30#[derive(Debug)]
31pub struct DaliQrData {
32 pub uid: String,
33 pub cardno: String,
34 pub cardtype: String,
35 pub totp: String,
36 pub nonce: String,
37 sign: Vec<u8>,
38}
39
40impl DaliQrData {
Tang Cheng8d072502020-10-31 22:02:25 +080041 #[allow(dead_code)]
Tang Cheng88643892020-10-31 22:00:07 +080042 fn new() -> Self {
43 Self {
Tang Cheng31462302020-11-02 09:18:47 +080044 uid: Default::default(),
45 cardno: Default::default(),
46 cardtype: Default::default(),
47 totp: Default::default(),
48 nonce: Default::default(),
Tang Cheng88643892020-10-31 22:00:07 +080049 sign: Vec::new(),
50 }
51 }
52
53 fn from_qrcode(qr_fields: &Vec<Vec<u8>>) -> Result<Self> {
54 if qr_fields.len() < 6 {
55 return Err(DecodeError::new("qrcode fields length must grater than 6."));
56 }
57 let sign = qr_fields[5].to_vec();
58 Ok(Self {
59 uid: String::from_utf8_lossy(&qr_fields[0].as_slice()).to_string(),
60 cardno: String::from_utf8_lossy(&qr_fields[1].as_slice()).to_string(),
61 cardtype: String::from_utf8_lossy(&qr_fields[2].as_slice()).to_string(),
62 totp: String::from_utf8_lossy(&qr_fields[3].as_slice()).to_string(),
63 nonce: String::from_utf8_lossy(&qr_fields[4].as_slice()).to_string(),
64 sign: sign,
65 })
66 }
67
68 fn update_sign(&mut self, sign: &Vec<u8>) {
69 self.sign = sign.to_vec();
70 }
71
72 fn to_qrdata(&self) -> String {
73 let v = vec![
74 String::from(&self.uid),
75 String::from(&self.cardno),
76 String::from(&self.cardtype),
77 String::from(&self.totp),
78 String::from(&self.nonce),
79 ];
80 v.join(QR_FIELD_DILIMITER)
81 }
82}
83
84#[derive(Debug, Clone)]
85pub struct DecodeError {
86 message: String,
87}
88
89impl fmt::Display for DecodeError {
90 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
91 write!(f, "Decode Qrcode error {}", self.message)
92 }
93}
94
95impl DecodeError {
96 fn new(message: &str) -> Self {
97 Self {
98 message: String::from(message),
99 }
100 }
101}
102
103type Result<T> = std::result::Result<T, DecodeError>;
104
105const KEY_LEN: usize = 32;
106impl DaliQrCode {
107 pub fn new(
108 key: [u8; KEY_LEN],
109 iv: Option<[u8; 16]>,
110 step: Option<u64>,
111 skew: Option<u8>,
112 seed: Option<Vec<u8>>,
113 ) -> Result<Self> {
114 let key = key.to_vec();
115 if key.len() != KEY_LEN {
116 return Err(DecodeError::new(&format!(
117 "key size must be {} bytes",
118 KEY_LEN
119 )));
120 }
121 let iv = if let Some(v) = iv {
122 v.to_vec()
123 } else {
124 hex::decode("55b6f5b3287c535f8274b99354676d0e").unwrap()
125 };
126
127 if iv.len() != 16 {
128 return Err(DecodeError::new("IV size must be 16 bytes"));
129 }
130
Tang Cheng31462302020-11-02 09:18:47 +0800131 let totp_step = step.unwrap_or(TOTP_STEP);
132 let totp_skew = skew.unwrap_or(TOTP_SKEW);
133 let totp_seed = seed.unwrap_or(hex::decode("125ea2f97689988b6501").unwrap());
Tang Cheng88643892020-10-31 22:00:07 +0800134
135 Ok(Self {
136 master_key: key,
Tang Cheng31462302020-11-02 09:18:47 +0800137 iv,
138 totp_seed,
139 totp_skew,
140 totp_step,
Tang Cheng88643892020-10-31 22:00:07 +0800141 })
142 }
143
Tang Cheng31462302020-11-02 09:18:47 +0800144 fn split_qrdata(&self, qrcode: &Vec<u8>, qrdata: &mut Vec<Vec<u8>>) -> Result<()> {
Tang Cheng88643892020-10-31 22:00:07 +0800145 let v = QR_FIELD_DILIMITER.as_bytes()[0];
146 let mut j = 0;
147 for i in 0..qrcode.len() {
148 if qrcode[i] == v {
149 if i > j {
150 let mut s = Vec::new();
151 s.extend_from_slice(&qrcode[j..i]);
Tang Cheng31462302020-11-02 09:18:47 +0800152 qrdata.push(s);
Tang Cheng88643892020-10-31 22:00:07 +0800153 }
154 j = i + 1;
155 }
156 }
157 if j < qrcode.len() {
158 let mut s = Vec::new();
159 s.extend_from_slice(&qrcode[j..]);
Tang Cheng31462302020-11-02 09:18:47 +0800160 qrdata.push(s);
Tang Cheng88643892020-10-31 22:00:07 +0800161 }
Tang Cheng31462302020-11-02 09:18:47 +0800162 Ok(())
163 }
Tang Cheng88643892020-10-31 22:00:07 +0800164
Tang Cheng31462302020-11-02 09:18:47 +0800165 pub fn decode(&self, qrcode: *const u8, len: usize, secs_offset: i32) -> Result<DaliQrData> {
166 let qrcode = self.decode_qrcode(qrcode, len)?;
167 let mut qr_fields: Vec<Vec<u8>> = Vec::new();
168
169 self.split_qrdata(&qrcode, &mut qr_fields)?;
170
171 let qr_data = DaliQrData::from_qrcode(&qr_fields)?;
172 if qr_data.uid.len() < 16 {
173 return Err(DecodeError::new("uid must grater than 16"));
Tang Cheng88643892020-10-31 22:00:07 +0800174 }
175
Tang Cheng31462302020-11-02 09:18:47 +0800176 self.check_qrcode_sign(&qr_data)?;
177
Tang Cheng88643892020-10-31 22:00:07 +0800178 let totp = self.new_totp();
179 let time = self.totp_time(secs_offset);
180 if totp.check(&qr_data.totp, time) {
181 Ok(qr_data)
182 } else {
183 Err(DecodeError::new("qrcode totp error"))
184 }
185 }
186
Tang Cheng31462302020-11-02 09:18:47 +0800187 fn check_qrcode_sign(&self, qr_data: &DaliQrData) -> Result<()> {
Tang Cheng88643892020-10-31 22:00:07 +0800188 let sign = self.calc_sign(qr_data);
189 if qr_data.sign == sign {
Tang Cheng31462302020-11-02 09:18:47 +0800190 Ok(())
Tang Cheng88643892020-10-31 22:00:07 +0800191 } else {
Tang Cheng31462302020-11-02 09:18:47 +0800192 Err(DecodeError::new("sign is invalid"))
Tang Cheng88643892020-10-31 22:00:07 +0800193 }
194 }
195
196 fn decode_qrcode(
197 &self,
198 qrcode: *const u8,
199 len: usize,
Tang Cheng31462302020-11-02 09:18:47 +0800200 ) -> Result<Vec<u8>> {
Tang Cheng88643892020-10-31 22:00:07 +0800201 let cipher = match Aes256Cbc::new_var(&self.master_key, &self.iv) {
202 Ok(c) => c,
203 Err(e) => return Err(DecodeError::new(&format!("aes key error {:?}", e))),
204 };
205
206 let qrcode = unsafe {
207 let s = slice::from_raw_parts(qrcode, len);
Tang Cheng88643892020-10-31 22:00:07 +0800208 if let Ok(code) = base64_url::decode(s) {
209 code
210 } else {
211 return Err(DecodeError::new("data base64 decode error"));
212 }
213 };
214
Tang Cheng88643892020-10-31 22:00:07 +0800215 match cipher.decrypt_vec(&qrcode) {
216 Ok(data) => Ok(data),
217 Err(e) => Err(DecodeError::new(&format!("block error {:?}", e))),
218 }
219 }
220
221 fn totp_time(&self, secs_offset: i32) -> u64 {
222 let time = SystemTime::now()
223 .duration_since(SystemTime::UNIX_EPOCH)
224 .unwrap()
225 .as_secs();
226 if secs_offset > 0 {
227 time + (secs_offset as u64)
228 } else {
229 time - (secs_offset as u64)
230 }
231 }
232
233 fn new_totp(&self) -> TOTP<Vec<u8>> {
234 let seed = self.totp_seed.clone();
235 TOTP::new(Algorithm::SHA1, 8, self.totp_skew, self.totp_step, seed)
236 }
237
238 fn encode_qrcode(&self, qr_data: &DaliQrData) -> Result<String> {
239 let plain_text = qr_data.to_qrdata();
240 let cipher = match Aes256Cbc::new_var(&self.master_key, &self.iv) {
241 Ok(c) => c,
242 Err(e) => return Err(DecodeError::new(&format!("aes key error {:?}", e))),
243 };
244 let mut buffer = Vec::new();
245 buffer.extend_from_slice(&plain_text.as_bytes());
246 buffer.push(QR_FIELD_DILIMITER.as_bytes()[0]);
247 buffer.extend_from_slice(qr_data.sign.as_slice());
248 let crypt_data = cipher.encrypt_vec(buffer.as_slice());
249 Ok(base64_url::encode(crypt_data.as_slice()))
250 }
251
252 fn calc_sign(&self, qr_data: &DaliQrData) -> Vec<u8> {
253 let mut hasher = Sha256::new();
254 hasher.update("{dlsmk_}".as_bytes());
255 hasher.update(qr_data.uid.as_bytes());
256 let salt = hasher.finalize();
257 let mut hasher = Sha256::new();
258 hasher.update(qr_data.to_qrdata().as_bytes());
259 hasher.update(salt);
260 hasher.finalize().to_vec()
261 }
262
263 pub fn encode(&self, qr_data: &mut DaliQrData, secs_offset: i32) -> Result<String> {
264 if qr_data.nonce.len() == 0 {
265 qr_data.nonce = format!("{:02}", Instant::now().elapsed().as_secs() % 100);
266 }
267 if qr_data.totp.len() == 0 {
268 let totp = self.new_totp();
269 let time = self.totp_time(secs_offset);
270 qr_data.totp = totp.generate(time);
271 }
272 let sign = self.calc_sign(qr_data);
273 qr_data.update_sign(&sign);
274 self.encode_qrcode(qr_data)
275 }
276}
277
278#[cfg(test)]
279mod tests {
280 use super::*;
281 use base64::decode;
282 use std::convert::TryInto;
283 const KEYLEN: usize = 32;
284 #[test]
285 fn it_works() {
286 assert_eq!(2 + 2, 4);
287 }
288
289 #[test]
290 fn aes_test() {
291 let mut key = [0u8; KEYLEN];
292 let s = decode("Vbb1syh8U1+CdLmTVGdtDiVvKBQ81n4GmgBEO/ohSbU=").unwrap();
293 key.clone_from_slice(&s.as_slice()[..KEYLEN]);
294
295 let iv: [u8; 16] = {
296 let s = hex::decode("55b6f5b3287c535f8274b99354676d0e").unwrap();
297 s.into_boxed_slice().as_ref().try_into().unwrap()
298 };
299
300 let aes_key = Aes256Cbc::new_var(&key, &iv).unwrap();
301 let plaintext = String::from("hello ldldldf ldfldl dslfasdamf sdmfdfdf");
302
303 let mut buffer = Vec::new();
304 buffer.extend_from_slice(plaintext.as_bytes());
305 println!(
306 "plain len : {} , buffer len : {}",
307 plaintext.len(),
308 buffer.len()
309 );
310 let cipher_data = aes_key.encrypt_vec(&buffer);
311
312 println!(
313 "cipher len : {}, last {}",
314 cipher_data.len(),
315 cipher_data[cipher_data.len() - 1] as u32
316 );
317
318 let aes_key = Aes256Cbc::new_var(&key, &iv).unwrap();
319 let _ = aes_key.decrypt_vec(&cipher_data);
320 }
321
322 fn get_key() -> ([u8; KEYLEN], [u8; 16]) {
323 let mut key = [0u8; KEYLEN];
324 let s = decode("Vbb1syh8U1+CdLmTVGdtDiVvKBQ81n4GmgBEO/ohSbU=").unwrap();
325 key.clone_from_slice(&s.as_slice()[..KEYLEN]);
326
327 let iv: [u8; 16] = {
328 let s = hex::decode("55b6f5b3287c535f8274b99354676d0e").unwrap();
329 s.into_boxed_slice().as_ref().try_into().unwrap()
330 };
331 (key, iv)
332 }
333
334 #[test]
335 fn check_qrcode_encode() {
336 let expect_qrcode = String::from("6lHyFX_vg5U2hymn8OsdNUD7dT0-sCmEQkKrm9cnzHlku6-FYxuL6nP5YR2Fve8Sfj-Asd-3dfQUkaiqqbfQWO8B_811B3uhHmGm9IjlpLicz_c1H1_ORb9tJl-IhMKu");
337 // let buffer = base64_url::decode(expect_qrcode.as_bytes()).unwrap();
338
339 // println!("encrypt buffer <{}>", buffer.len());
340 // println!("decode b64<{}>", hex::encode(buffer.clone()));
341 // let aes_key = Aes256Cbc::new_var(&key, &iv).unwrap();
342 // let data = aes_key.decrypt_vec(&buffer).unwrap();
343 // println!("data : {}", String::from_utf8_lossy(data.as_slice()));
344
345 let (key, iv) = get_key();
346
347 let mut qr_data = DaliQrData::new();
348 qr_data.uid = String::from("0a5de6ce985d43989b7ebe64ad8eb9c3");
349 qr_data.cardno = String::from("00001252");
350 qr_data.cardtype = String::from("80");
351 qr_data.nonce = String::from("ac");
352 qr_data.totp = String::from("50053019");
353
354 let dali_qrcode = DaliQrCode::new(key, Some(iv), None, None, None).unwrap();
355
356 match dali_qrcode.encode(&mut qr_data, 0) {
357 Ok(qrcode) => {
358 assert_eq!(qrcode, expect_qrcode);
359 }
360 Err(e) => {
361 panic!("error {}", e);
362 }
363 }
364 }
365
366 #[test]
367 fn check_qrcode_decode() {
368 let (key, iv) = get_key();
369 let mut qr_data = DaliQrData::new();
370 qr_data.uid = String::from("0a5de6ce985d43989b7ebe64ad8eb9c3");
371 qr_data.cardno = String::from("00001252");
372 qr_data.cardtype = String::from("80");
373
Tang Cheng31462302020-11-02 09:18:47 +0800374 let dali_qrcode = DaliQrCode::new(key, Some(iv), Some(60u64), Some(5u8), None).unwrap();
Tang Cheng88643892020-10-31 22:00:07 +0800375
376 match dali_qrcode.encode(&mut qr_data, 0) {
377 Ok(qrcode) => {
378 if let Err(e) = dali_qrcode.decode(qrcode.as_ptr(), qrcode.len(), 0) {
379 panic!("error {}", e);
380 }
381 }
382 Err(e) => {
383 panic!("error {}", e);
384 }
385 }
386 }
387}