use crate::crypt::Cipher;
use crate::dat::DatPayload;
use crate::dat_key::Kid;
use crate::error::DatError;
use crate::sign::{SignKey, VerifyKey};
use crate::util::{decode_base64_url_no_pad, encode_base64_url_no_pad, get_dat_dot_indices, now_unix_timestamp, to_kid, to_utf8};
use std::fmt::Write;
pub struct DatKeyActive<T: Kid> {
pub(crate) kid: T,
pub(crate) sign_key: Option<SignKey>,
pub(crate) verify_key: VerifyKey,
pub(crate) cipher: Cipher,
pub(crate) token_ttl: i64,
}
impl <T: Kid> DatKeyActive<T> {
pub fn to_payload(&self, dat: &'_ str) -> Result<DatPayload, DatError> {
self.to_payload_with_indices(dat, get_dat_dot_indices(dat)?)
}
pub fn to_payload_with_indices(&self, dat: &'_ str, di: [usize; 4]) -> Result<DatPayload, DatError> {
let exp = dat[0 .. di[0]]
.parse::<i64>()
.map_err(|_| DatError::InvalidDatFormat)?;
if exp < now_unix_timestamp() {
return Err(DatError::InvalidDatFormat)
}
let kid = to_kid(&dat[di[0] + 1 .. di[1]])?;
let plain_base64 = &dat[di[1] + 1 .. di[2]];
let secure_base64 = &dat[di[2] + 1 .. di[3]];
let sign = &dat[di[3] + 1 ..];
if sign.is_empty() {
return Err(DatError::InvalidDatFormat);
}
let body = &dat[0 ..di[3]];
if self.kid != kid {
return Err(DatError::InvalidDatFormat);
}
if self.verify_key.verify(body.as_bytes(), &*decode_base64_url_no_pad(sign)?).is_err() {
return Err(DatError::InvalidDatFormat)
}
self.decode_payload_base64(plain_base64, secure_base64)
}
pub(crate) fn decode_payload_base64(&self, plain_base64: &str, secure_base64: &str) -> Result<DatPayload, DatError> {
let plain = to_utf8(decode_base64_url_no_pad(plain_base64)?)?;
let secure = to_utf8(self.cipher.decrypt(decode_base64_url_no_pad(secure_base64)?.as_slice())?)?;
Ok((plain, secure))
}
pub fn to_dat<U: AsRef<[u8]>>(&self, plain: U, secure: U) -> Result<String, DatError> {
let plain = encode_base64_url_no_pad(plain);
let secure = encode_base64_url_no_pad(self.cipher.encrypt(secure.as_ref())?);
let expire = now_unix_timestamp() + self.token_ttl;
let kid = &self.kid;
let mut body = String::with_capacity(100 + plain.len() + secure.len());
write!(body, "{expire}.{kid}.{plain}.{secure}").map_err(|_| DatError::IoError)?;
let sign_ref = self.sign_key.as_ref().ok_or_else(|| DatError::UnSupportSignKeyIsVerifyOnly)?;
let sign = encode_base64_url_no_pad(sign_ref.sign(body.as_bytes()));
body.write_char('.').map_err(|_| DatError::IoError)?;
body.write_str(&sign).map_err(|_| DatError::IoError)?;
Ok(body)
}
}