blob: fdcad914a22f53cb564f5a0aa08c0d889b519e31 [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 {
44 uid: String::from(""),
45 cardno: String::from(""),
46 cardtype: String::from(""),
47 totp: String::from(""),
48 nonce: String::from(""),
49 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
131 let step = if let Some(s) = step { s } else { TOTP_STEP };
132
133 let skew = if let Some(s) = skew { s } else { TOTP_SKEW };
134
135 let seed = if let Some(s) = seed {
136 s
137 } else {
138 hex::decode("125ea2f97689988b6501").unwrap()
139 };
140
141 Ok(Self {
142 master_key: key,
143 iv: iv,
144 totp_seed: seed,
145 totp_skew: skew,
146 totp_step: step,
147 })
148 }
149
150 pub fn decode(&self, qrcode: *const u8, len: usize, secs_offset: i32) -> Result<DaliQrData> {
151 let qrcode = match self.decode_qrcode(qrcode, len) {
152 Ok(q) => q,
153 Err(e) => return Err(e),
154 };
155 let mut qr_fields: Vec<Vec<u8>> = Vec::new();
156 let v = QR_FIELD_DILIMITER.as_bytes()[0];
157 let mut j = 0;
158 for i in 0..qrcode.len() {
159 if qrcode[i] == v {
160 if i > j {
161 let mut s = Vec::new();
162 s.extend_from_slice(&qrcode[j..i]);
163 qr_fields.push(s);
164 }
165 j = i + 1;
166 }
167 }
168 if j < qrcode.len() {
169 let mut s = Vec::new();
170 s.extend_from_slice(&qrcode[j..]);
171 qr_fields.push(s);
172 }
173 let qr_data = match DaliQrData::from_qrcode(&qr_fields) {
174 Ok(d) => d,
175 Err(e) => return Err(e),
176 };
177
178 match self.check_qrcode_sign(&qr_data) {
179 Ok(result) if !result => return Err(DecodeError::new("invalidate qrcode")),
180 Ok(_) => (),
181 Err(e) => return Err(e),
182 }
183
184 let totp = self.new_totp();
185 let time = self.totp_time(secs_offset);
186 if totp.check(&qr_data.totp, time) {
187 Ok(qr_data)
188 } else {
189 Err(DecodeError::new("qrcode totp error"))
190 }
191 }
192
193 fn check_qrcode_sign(&self, qr_data: &DaliQrData) -> Result<bool> {
194 let sign = self.calc_sign(qr_data);
195 if qr_data.sign == sign {
196 Ok(true)
197 } else {
198 Ok(false)
199 }
200 }
201
202 fn decode_qrcode(
203 &self,
204 qrcode: *const u8,
205 len: usize,
206 ) -> std::result::Result<Vec<u8>, DecodeError> {
207 let cipher = match Aes256Cbc::new_var(&self.master_key, &self.iv) {
208 Ok(c) => c,
209 Err(e) => return Err(DecodeError::new(&format!("aes key error {:?}", e))),
210 };
211
212 let qrcode = unsafe {
213 let s = slice::from_raw_parts(qrcode, len);
Tang Cheng8d072502020-10-31 22:02:25 +0800214 // println!("qr input : {}", String::from_utf8_lossy(s));
Tang Cheng88643892020-10-31 22:00:07 +0800215 if let Ok(code) = base64_url::decode(s) {
216 code
217 } else {
218 return Err(DecodeError::new("data base64 decode error"));
219 }
220 };
221
222 if qrcode.len() % 16 != 0 {
223 return Err(DecodeError::new("Input data length error"));
224 }
225
226 match cipher.decrypt_vec(&qrcode) {
227 Ok(data) => Ok(data),
228 Err(e) => Err(DecodeError::new(&format!("block error {:?}", e))),
229 }
230 }
231
232 fn totp_time(&self, secs_offset: i32) -> u64 {
233 let time = SystemTime::now()
234 .duration_since(SystemTime::UNIX_EPOCH)
235 .unwrap()
236 .as_secs();
237 if secs_offset > 0 {
238 time + (secs_offset as u64)
239 } else {
240 time - (secs_offset as u64)
241 }
242 }
243
244 fn new_totp(&self) -> TOTP<Vec<u8>> {
245 let seed = self.totp_seed.clone();
246 TOTP::new(Algorithm::SHA1, 8, self.totp_skew, self.totp_step, seed)
247 }
248
249 fn encode_qrcode(&self, qr_data: &DaliQrData) -> Result<String> {
250 let plain_text = qr_data.to_qrdata();
251 let cipher = match Aes256Cbc::new_var(&self.master_key, &self.iv) {
252 Ok(c) => c,
253 Err(e) => return Err(DecodeError::new(&format!("aes key error {:?}", e))),
254 };
255 let mut buffer = Vec::new();
256 buffer.extend_from_slice(&plain_text.as_bytes());
257 buffer.push(QR_FIELD_DILIMITER.as_bytes()[0]);
258 buffer.extend_from_slice(qr_data.sign.as_slice());
259 let crypt_data = cipher.encrypt_vec(buffer.as_slice());
260 Ok(base64_url::encode(crypt_data.as_slice()))
261 }
262
263 fn calc_sign(&self, qr_data: &DaliQrData) -> Vec<u8> {
264 let mut hasher = Sha256::new();
265 hasher.update("{dlsmk_}".as_bytes());
266 hasher.update(qr_data.uid.as_bytes());
267 let salt = hasher.finalize();
268 let mut hasher = Sha256::new();
269 hasher.update(qr_data.to_qrdata().as_bytes());
270 hasher.update(salt);
271 hasher.finalize().to_vec()
272 }
273
274 pub fn encode(&self, qr_data: &mut DaliQrData, secs_offset: i32) -> Result<String> {
275 if qr_data.nonce.len() == 0 {
276 qr_data.nonce = format!("{:02}", Instant::now().elapsed().as_secs() % 100);
277 }
278 if qr_data.totp.len() == 0 {
279 let totp = self.new_totp();
280 let time = self.totp_time(secs_offset);
281 qr_data.totp = totp.generate(time);
282 }
283 let sign = self.calc_sign(qr_data);
284 qr_data.update_sign(&sign);
285 self.encode_qrcode(qr_data)
286 }
287}
288
289#[cfg(test)]
290mod tests {
291 use super::*;
292 use base64::decode;
293 use std::convert::TryInto;
294 const KEYLEN: usize = 32;
295 #[test]
296 fn it_works() {
297 assert_eq!(2 + 2, 4);
298 }
299
300 #[test]
301 fn aes_test() {
302 let mut key = [0u8; KEYLEN];
303 let s = decode("Vbb1syh8U1+CdLmTVGdtDiVvKBQ81n4GmgBEO/ohSbU=").unwrap();
304 key.clone_from_slice(&s.as_slice()[..KEYLEN]);
305
306 let iv: [u8; 16] = {
307 let s = hex::decode("55b6f5b3287c535f8274b99354676d0e").unwrap();
308 s.into_boxed_slice().as_ref().try_into().unwrap()
309 };
310
311 let aes_key = Aes256Cbc::new_var(&key, &iv).unwrap();
312 let plaintext = String::from("hello ldldldf ldfldl dslfasdamf sdmfdfdf");
313
314 let mut buffer = Vec::new();
315 buffer.extend_from_slice(plaintext.as_bytes());
316 println!(
317 "plain len : {} , buffer len : {}",
318 plaintext.len(),
319 buffer.len()
320 );
321 let cipher_data = aes_key.encrypt_vec(&buffer);
322
323 println!(
324 "cipher len : {}, last {}",
325 cipher_data.len(),
326 cipher_data[cipher_data.len() - 1] as u32
327 );
328
329 let aes_key = Aes256Cbc::new_var(&key, &iv).unwrap();
330 let _ = aes_key.decrypt_vec(&cipher_data);
331 }
332
333 fn get_key() -> ([u8; KEYLEN], [u8; 16]) {
334 let mut key = [0u8; KEYLEN];
335 let s = decode("Vbb1syh8U1+CdLmTVGdtDiVvKBQ81n4GmgBEO/ohSbU=").unwrap();
336 key.clone_from_slice(&s.as_slice()[..KEYLEN]);
337
338 let iv: [u8; 16] = {
339 let s = hex::decode("55b6f5b3287c535f8274b99354676d0e").unwrap();
340 s.into_boxed_slice().as_ref().try_into().unwrap()
341 };
342 (key, iv)
343 }
344
345 #[test]
346 fn check_qrcode_encode() {
347 let expect_qrcode = String::from("6lHyFX_vg5U2hymn8OsdNUD7dT0-sCmEQkKrm9cnzHlku6-FYxuL6nP5YR2Fve8Sfj-Asd-3dfQUkaiqqbfQWO8B_811B3uhHmGm9IjlpLicz_c1H1_ORb9tJl-IhMKu");
348 // let buffer = base64_url::decode(expect_qrcode.as_bytes()).unwrap();
349
350 // println!("encrypt buffer <{}>", buffer.len());
351 // println!("decode b64<{}>", hex::encode(buffer.clone()));
352 // let aes_key = Aes256Cbc::new_var(&key, &iv).unwrap();
353 // let data = aes_key.decrypt_vec(&buffer).unwrap();
354 // println!("data : {}", String::from_utf8_lossy(data.as_slice()));
355
356 let (key, iv) = get_key();
357
358 let mut qr_data = DaliQrData::new();
359 qr_data.uid = String::from("0a5de6ce985d43989b7ebe64ad8eb9c3");
360 qr_data.cardno = String::from("00001252");
361 qr_data.cardtype = String::from("80");
362 qr_data.nonce = String::from("ac");
363 qr_data.totp = String::from("50053019");
364
365 let dali_qrcode = DaliQrCode::new(key, Some(iv), None, None, None).unwrap();
366
367 match dali_qrcode.encode(&mut qr_data, 0) {
368 Ok(qrcode) => {
369 assert_eq!(qrcode, expect_qrcode);
370 }
371 Err(e) => {
372 panic!("error {}", e);
373 }
374 }
375 }
376
377 #[test]
378 fn check_qrcode_decode() {
379 let (key, iv) = get_key();
380 let mut qr_data = DaliQrData::new();
381 qr_data.uid = String::from("0a5de6ce985d43989b7ebe64ad8eb9c3");
382 qr_data.cardno = String::from("00001252");
383 qr_data.cardtype = String::from("80");
384
385 let dali_qrcode = DaliQrCode::new(key, Some(iv), None, None, None).unwrap();
386
387 match dali_qrcode.encode(&mut qr_data, 0) {
388 Ok(qrcode) => {
389 if let Err(e) = dali_qrcode.decode(qrcode.as_ptr(), qrcode.len(), 0) {
390 panic!("error {}", e);
391 }
392 }
393 Err(e) => {
394 panic!("error {}", e);
395 }
396 }
397 }
398}