blob: 9bb11143542a0457c6b5a6d00d67dea0d7c47ee7 [file] [log] [blame]
extern crate base64_url;
extern crate hex;
use std::fmt;
use std::slice;
use std::time::Instant;
use std::time::SystemTime;
use sha2::{Digest, Sha256};
use aes::Aes256;
use block_modes::block_padding::Pkcs7;
use block_modes::{BlockMode, Cbc};
use totp_rs::{Algorithm, TOTP};
type Aes256Cbc = Cbc<Aes256, Pkcs7>;
const TOTP_STEP: u64 = 5;
const TOTP_SKEW: u8 = 3;
static QR_FIELD_DILIMITER: &str = ":";
pub struct DaliQrCode {
master_key: Vec<u8>,
iv: Vec<u8>,
totp_step: u64,
totp_skew: u8,
totp_seed: Vec<u8>,
}
#[derive(Debug)]
pub struct DaliQrData {
pub uid: String,
pub cardno: String,
pub cardtype: String,
pub totp: String,
pub nonce: String,
sign: Vec<u8>,
}
impl DaliQrData {
fn new() -> Self {
Self {
uid: String::from(""),
cardno: String::from(""),
cardtype: String::from(""),
totp: String::from(""),
nonce: String::from(""),
sign: Vec::new(),
}
}
fn from_qrcode(qr_fields: &Vec<Vec<u8>>) -> Result<Self> {
if qr_fields.len() < 6 {
return Err(DecodeError::new("qrcode fields length must grater than 6."));
}
let sign = qr_fields[5].to_vec();
Ok(Self {
uid: String::from_utf8_lossy(&qr_fields[0].as_slice()).to_string(),
cardno: String::from_utf8_lossy(&qr_fields[1].as_slice()).to_string(),
cardtype: String::from_utf8_lossy(&qr_fields[2].as_slice()).to_string(),
totp: String::from_utf8_lossy(&qr_fields[3].as_slice()).to_string(),
nonce: String::from_utf8_lossy(&qr_fields[4].as_slice()).to_string(),
sign: sign,
})
}
fn update_sign(&mut self, sign: &Vec<u8>) {
self.sign = sign.to_vec();
}
fn to_qrdata(&self) -> String {
let v = vec![
String::from(&self.uid),
String::from(&self.cardno),
String::from(&self.cardtype),
String::from(&self.totp),
String::from(&self.nonce),
];
v.join(QR_FIELD_DILIMITER)
}
}
#[derive(Debug, Clone)]
pub struct DecodeError {
message: String,
}
impl fmt::Display for DecodeError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Decode Qrcode error {}", self.message)
}
}
impl DecodeError {
fn new(message: &str) -> Self {
Self {
message: String::from(message),
}
}
}
type Result<T> = std::result::Result<T, DecodeError>;
const KEY_LEN: usize = 32;
impl DaliQrCode {
pub fn new(
key: [u8; KEY_LEN],
iv: Option<[u8; 16]>,
step: Option<u64>,
skew: Option<u8>,
seed: Option<Vec<u8>>,
) -> Result<Self> {
let key = key.to_vec();
if key.len() != KEY_LEN {
return Err(DecodeError::new(&format!(
"key size must be {} bytes",
KEY_LEN
)));
}
let iv = if let Some(v) = iv {
v.to_vec()
} else {
hex::decode("55b6f5b3287c535f8274b99354676d0e").unwrap()
};
if iv.len() != 16 {
return Err(DecodeError::new("IV size must be 16 bytes"));
}
let step = if let Some(s) = step { s } else { TOTP_STEP };
let skew = if let Some(s) = skew { s } else { TOTP_SKEW };
let seed = if let Some(s) = seed {
s
} else {
hex::decode("125ea2f97689988b6501").unwrap()
};
Ok(Self {
master_key: key,
iv: iv,
totp_seed: seed,
totp_skew: skew,
totp_step: step,
})
}
pub fn decode(&self, qrcode: *const u8, len: usize, secs_offset: i32) -> Result<DaliQrData> {
let qrcode = match self.decode_qrcode(qrcode, len) {
Ok(q) => q,
Err(e) => return Err(e),
};
let mut qr_fields: Vec<Vec<u8>> = Vec::new();
let v = QR_FIELD_DILIMITER.as_bytes()[0];
let mut j = 0;
for i in 0..qrcode.len() {
if qrcode[i] == v {
if i > j {
let mut s = Vec::new();
s.extend_from_slice(&qrcode[j..i]);
qr_fields.push(s);
}
j = i + 1;
}
}
if j < qrcode.len() {
let mut s = Vec::new();
s.extend_from_slice(&qrcode[j..]);
qr_fields.push(s);
}
let qr_data = match DaliQrData::from_qrcode(&qr_fields) {
Ok(d) => d,
Err(e) => return Err(e),
};
match self.check_qrcode_sign(&qr_data) {
Ok(result) if !result => return Err(DecodeError::new("invalidate qrcode")),
Ok(_) => (),
Err(e) => return Err(e),
}
let totp = self.new_totp();
let time = self.totp_time(secs_offset);
if totp.check(&qr_data.totp, time) {
Ok(qr_data)
} else {
Err(DecodeError::new("qrcode totp error"))
}
}
fn check_qrcode_sign(&self, qr_data: &DaliQrData) -> Result<bool> {
let sign = self.calc_sign(qr_data);
if qr_data.sign == sign {
Ok(true)
} else {
Ok(false)
}
}
fn decode_qrcode(
&self,
qrcode: *const u8,
len: usize,
) -> std::result::Result<Vec<u8>, DecodeError> {
let cipher = match Aes256Cbc::new_var(&self.master_key, &self.iv) {
Ok(c) => c,
Err(e) => return Err(DecodeError::new(&format!("aes key error {:?}", e))),
};
let qrcode = unsafe {
let s = slice::from_raw_parts(qrcode, len);
println!("qr input : {}", String::from_utf8_lossy(s));
if let Ok(code) = base64_url::decode(s) {
code
} else {
return Err(DecodeError::new("data base64 decode error"));
}
};
if qrcode.len() % 16 != 0 {
return Err(DecodeError::new("Input data length error"));
}
match cipher.decrypt_vec(&qrcode) {
Ok(data) => Ok(data),
Err(e) => Err(DecodeError::new(&format!("block error {:?}", e))),
}
}
fn totp_time(&self, secs_offset: i32) -> u64 {
let time = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_secs();
if secs_offset > 0 {
time + (secs_offset as u64)
} else {
time - (secs_offset as u64)
}
}
fn new_totp(&self) -> TOTP<Vec<u8>> {
let seed = self.totp_seed.clone();
TOTP::new(Algorithm::SHA1, 8, self.totp_skew, self.totp_step, seed)
}
fn encode_qrcode(&self, qr_data: &DaliQrData) -> Result<String> {
let plain_text = qr_data.to_qrdata();
let cipher = match Aes256Cbc::new_var(&self.master_key, &self.iv) {
Ok(c) => c,
Err(e) => return Err(DecodeError::new(&format!("aes key error {:?}", e))),
};
let mut buffer = Vec::new();
buffer.extend_from_slice(&plain_text.as_bytes());
buffer.push(QR_FIELD_DILIMITER.as_bytes()[0]);
buffer.extend_from_slice(qr_data.sign.as_slice());
let crypt_data = cipher.encrypt_vec(buffer.as_slice());
Ok(base64_url::encode(crypt_data.as_slice()))
}
fn calc_sign(&self, qr_data: &DaliQrData) -> Vec<u8> {
let mut hasher = Sha256::new();
hasher.update("{dlsmk_}".as_bytes());
hasher.update(qr_data.uid.as_bytes());
let salt = hasher.finalize();
let mut hasher = Sha256::new();
hasher.update(qr_data.to_qrdata().as_bytes());
hasher.update(salt);
hasher.finalize().to_vec()
}
pub fn encode(&self, qr_data: &mut DaliQrData, secs_offset: i32) -> Result<String> {
if qr_data.nonce.len() == 0 {
qr_data.nonce = format!("{:02}", Instant::now().elapsed().as_secs() % 100);
}
if qr_data.totp.len() == 0 {
let totp = self.new_totp();
let time = self.totp_time(secs_offset);
qr_data.totp = totp.generate(time);
}
let sign = self.calc_sign(qr_data);
qr_data.update_sign(&sign);
self.encode_qrcode(qr_data)
}
}
#[cfg(test)]
mod tests {
use super::*;
use base64::decode;
use std::convert::TryInto;
const KEYLEN: usize = 32;
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
#[test]
fn aes_test() {
let mut key = [0u8; KEYLEN];
let s = decode("Vbb1syh8U1+CdLmTVGdtDiVvKBQ81n4GmgBEO/ohSbU=").unwrap();
key.clone_from_slice(&s.as_slice()[..KEYLEN]);
let iv: [u8; 16] = {
let s = hex::decode("55b6f5b3287c535f8274b99354676d0e").unwrap();
s.into_boxed_slice().as_ref().try_into().unwrap()
};
let aes_key = Aes256Cbc::new_var(&key, &iv).unwrap();
let plaintext = String::from("hello ldldldf ldfldl dslfasdamf sdmfdfdf");
let mut buffer = Vec::new();
buffer.extend_from_slice(plaintext.as_bytes());
println!(
"plain len : {} , buffer len : {}",
plaintext.len(),
buffer.len()
);
let cipher_data = aes_key.encrypt_vec(&buffer);
println!(
"cipher len : {}, last {}",
cipher_data.len(),
cipher_data[cipher_data.len() - 1] as u32
);
let aes_key = Aes256Cbc::new_var(&key, &iv).unwrap();
let _ = aes_key.decrypt_vec(&cipher_data);
}
fn get_key() -> ([u8; KEYLEN], [u8; 16]) {
let mut key = [0u8; KEYLEN];
let s = decode("Vbb1syh8U1+CdLmTVGdtDiVvKBQ81n4GmgBEO/ohSbU=").unwrap();
key.clone_from_slice(&s.as_slice()[..KEYLEN]);
let iv: [u8; 16] = {
let s = hex::decode("55b6f5b3287c535f8274b99354676d0e").unwrap();
s.into_boxed_slice().as_ref().try_into().unwrap()
};
(key, iv)
}
#[test]
fn check_qrcode_encode() {
let expect_qrcode = String::from("6lHyFX_vg5U2hymn8OsdNUD7dT0-sCmEQkKrm9cnzHlku6-FYxuL6nP5YR2Fve8Sfj-Asd-3dfQUkaiqqbfQWO8B_811B3uhHmGm9IjlpLicz_c1H1_ORb9tJl-IhMKu");
// let buffer = base64_url::decode(expect_qrcode.as_bytes()).unwrap();
// println!("encrypt buffer <{}>", buffer.len());
// println!("decode b64<{}>", hex::encode(buffer.clone()));
// let aes_key = Aes256Cbc::new_var(&key, &iv).unwrap();
// let data = aes_key.decrypt_vec(&buffer).unwrap();
// println!("data : {}", String::from_utf8_lossy(data.as_slice()));
let (key, iv) = get_key();
let mut qr_data = DaliQrData::new();
qr_data.uid = String::from("0a5de6ce985d43989b7ebe64ad8eb9c3");
qr_data.cardno = String::from("00001252");
qr_data.cardtype = String::from("80");
qr_data.nonce = String::from("ac");
qr_data.totp = String::from("50053019");
let dali_qrcode = DaliQrCode::new(key, Some(iv), None, None, None).unwrap();
match dali_qrcode.encode(&mut qr_data, 0) {
Ok(qrcode) => {
assert_eq!(qrcode, expect_qrcode);
}
Err(e) => {
panic!("error {}", e);
}
}
}
#[test]
fn check_qrcode_decode() {
let (key, iv) = get_key();
let mut qr_data = DaliQrData::new();
qr_data.uid = String::from("0a5de6ce985d43989b7ebe64ad8eb9c3");
qr_data.cardno = String::from("00001252");
qr_data.cardtype = String::from("80");
let dali_qrcode = DaliQrCode::new(key, Some(iv), None, None, None).unwrap();
match dali_qrcode.encode(&mut qr_data, 0) {
Ok(qrcode) => {
if let Err(e) = dali_qrcode.decode(qrcode.as_ptr(), qrcode.len(), 0) {
panic!("error {}", e);
}
}
Err(e) => {
panic!("error {}", e);
}
}
}
}