use crate::crypt::{CryptAlgorithm, CryptKey};
use crate::error::DatError;
use crate::sign::{SignAlgorithm, SignKey, VerifyKey};
use crate::util::{decode_base64_url_no_pad, encode_base64_url_no_pad, to_kid};
use std::fmt::{Debug, Display};
use std::str::FromStr;
use crate::dat_key_active::DatKeyActive;
pub type DatSplit<'a> = [&'a str; 5];
pub trait Kid: PartialEq + Display + FromStr + Clone {}
impl<T> Kid for T where T: PartialEq + Display + FromStr + Clone {}
pub const CONV_VERSION: &str = "1";
#[derive(Debug, Clone)]
pub struct DatKey<T: Kid> {
pub(crate) kid: T,
pub(crate) sign_alg: SignAlgorithm,
pub(crate) sign_key_vec: Vec<u8>,
pub(crate) verify_key_vec: Vec<u8>,
pub(crate) crypt_alg: CryptAlgorithm,
pub(crate) crypt_key_vec: Vec<u8>,
pub(crate) issue_begin: i64,
pub(crate) issue_end: i64,
pub(crate) token_ttl: i64,
}
impl <T: Kid> DatKey<T> {
pub fn generate(kid: T, sign_alg: SignAlgorithm, crypt_alg: CryptAlgorithm, issue_begin: i64, issue_end: i64, token_ttl: i64) -> Result<Self, DatError> {
let sign_key = SignKey::generate(sign_alg);
let sign_key_v = sign_key.to_bytes().to_vec();
let verify_key_v = sign_key.to_verify_key().to_bytes().to_vec();
let crypt_key_v = CryptKey::generate(crypt_alg).to_bytes().to_vec();
Self::from(kid, sign_alg, sign_key_v, verify_key_v, crypt_alg, crypt_key_v, issue_begin, issue_end, token_ttl)
}
pub fn from(kid: T, sign_alg: SignAlgorithm, sign_key_vec: Vec<u8>, verify_key_vec: Vec<u8>, crypt_alg: CryptAlgorithm, crypt_key_vec: Vec<u8>, issue_begin: i64, issue_end: i64, token_ttl: i64) -> Result<Self, DatError> {
if kid.to_string().contains(['.', '\r', '\n']) {
return Err(DatError::InvalidDatKidFormat);
}
Ok(DatKey { kid, sign_alg, sign_key_vec, verify_key_vec, crypt_alg, crypt_key_vec, issue_begin, issue_end, token_ttl, })
}
pub fn kid(&self) -> T { self.kid.clone() }
pub fn sign_alg(&self) -> SignAlgorithm { self.sign_alg }
pub fn sign_key_vec(&self) -> Vec<u8> { self.sign_key_vec.to_vec() }
pub fn verify_key_vec(&self) -> Vec<u8> { self.verify_key_vec.to_vec() }
pub fn crypt_alg(&self) -> CryptAlgorithm { self.crypt_alg }
pub fn crypt_key_vec(&self) -> Vec<u8> { self.crypt_key_vec.to_vec() }
pub fn issue_begin(&self) -> i64 { self.issue_begin }
pub fn issue_end(&self) -> i64 { self.issue_end }
pub fn token_ttl(&self) -> i64 { self.token_ttl }
pub fn key_expire(&self) -> i64 { self.issue_end + self.token_ttl }
pub fn sign_key(&self) -> Option<SignKey> {
if self.sign_key_vec.len() > 0 {
SignKey::from_bytes(self.sign_alg, self.sign_key_vec.as_ref()).ok()
} else {
None
}
}
pub fn sign_key_base64(&self) -> Result<String, DatError> {
if self.sign_key_vec.len() > 0 {
Ok(encode_base64_url_no_pad(&*self.sign_key_vec))
} else {
Err(DatError::UnSupportSignKeyIsVerifyOnly)
}
}
pub fn verify_key(&self) -> Result<VerifyKey, DatError> {
if self.verify_key_vec.len() > 0 {
VerifyKey::from_bytes(self.sign_alg, self.verify_key_vec.as_ref())
} else {
Ok(SignKey::from_bytes(self.sign_alg, self.sign_key_vec.as_ref())?.to_verify_key())
}
}
pub fn verify_key_base64(&self) -> Result<String, DatError> {
self.verify_key().map(|v| encode_base64_url_no_pad(&*v.to_bytes()))
}
pub fn format(&self, verify_only: bool) -> Result<String, DatError> {
let kid = self.kid.to_string();
let sign_alg = self.sign_alg.to_str();
let sign_key = if verify_only {
format!("~{}", self.verify_key_base64()?)
} else {
self.sign_key_base64()?
};
let crypt_alg = self.crypt_alg.to_str();
let crypt_key = encode_base64_url_no_pad(&*self.crypt_key_vec);
let issue_begin = self.issue_begin;
let issue_end = self.issue_end;
let token_ttl = self.token_ttl;
Ok(format!("{CONV_VERSION}.{kid}.{sign_alg}.{sign_key}.{crypt_alg}.{crypt_key}.{issue_begin}.{issue_end}.{token_ttl}"))
}
pub fn active(&self) -> Result<DatKeyActive<T>, DatError> {
let crypt_key = CryptKey::from_bytes(self.crypt_alg, &*self.crypt_key_vec)?;
Ok(DatKeyActive {
kid: self.kid.clone(),
sign_alg: self.sign_alg,
sign_key: self.sign_key(),
crypt_alg: self.crypt_alg,
verify_key: self.verify_key()?,
cipher: crypt_key.to_cipher(),
token_ttl: self.token_ttl,
})
}
}
impl <T: Kid> FromStr for DatKey<T> {
type Err = DatError;
fn from_str(format: &str) -> Result<Self, Self::Err> {
let split = format.split(".").collect::<Vec<&str>>();
match split[0] {
"1" => {
return if split.len() == 9 {
let kid = to_kid(split[1])?;
let sign_alg = SignAlgorithm::from_str(split[2])?;
let sign_or_verify_key = split[3];
let (sign_key, verify_key) = if sign_or_verify_key.starts_with('~') {
(vec![], decode_base64_url_no_pad(sign_or_verify_key[1..].as_bytes())?)
} else {
(decode_base64_url_no_pad(sign_or_verify_key)?, vec![])
};
let crypt_alg = CryptAlgorithm::from_str(split[4])?;
let crypt_key = decode_base64_url_no_pad(split[5])?;
let issue_begin = split[6].parse::<i64>().map_err(|_| DatError::InvalidDatKeyFormat)?;
let issue_end = split[7].parse::<i64>().map_err(|_| DatError::InvalidDatKeyFormat)?;
let token_ttl = split[8].parse::<i64>().map_err(|_| DatError::InvalidDatKeyFormat)?;
DatKey::from(kid, sign_alg, sign_key, verify_key, crypt_alg, crypt_key, issue_begin, issue_end, token_ttl)
} else {
Err(DatError::InvalidDatKeyFormat)
}
},
_ => {}
}
Err(DatError::UnSupportDatKeyVersion)
}
}
impl <T: Kid> PartialEq for DatKey<T> {
fn eq(&self, other: &Self) -> bool {
self.kid.eq(&other.kid)
}
}