use crate::crypto_algorithm::CryptoAlgorithm;
use crate::crypto_key::CryptoKey;
use crate::dat::Dat;
use crate::dat_kid::Kid;
use crate::dat_payload::DatPayload;
use crate::error::DatError;
use crate::signature_algorithm::SignatureAlgorithm;
use crate::signature_key::{SignatureKey, SignatureKeyOutOption};
use crate::util::{decode_base64_url, encode_base64_url, encode_base64_url_out, now_unix_timestamp};
use crate::VERSION_DAT;
use std::cmp::PartialEq;
use std::str::FromStr;
pub struct DatKey<T: Kid> {
pub(crate) kid: T,
pub(crate) signature_key: SignatureKey,
pub(crate) crypto_key: CryptoKey,
pub(crate) issue_begin: u64,
pub(crate) issue_end: u64,
pub(crate) token_ttl: u64,
}
impl <T: Kid> DatKey<T> {
pub fn generate(kid: T, signature_algorithm: SignatureAlgorithm, crypto_algorithm: CryptoAlgorithm, issue_begin: u64, issue_end: u64, token_ttl: u64) -> Result<Self, DatError> {
Self::from(kid, SignatureKey::generate(signature_algorithm), CryptoKey::generate(crypto_algorithm), issue_begin, issue_end, token_ttl)
}
pub fn from(kid: T, signature_key: SignatureKey, crypto_key: CryptoKey, issue_begin: u64, issue_end: u64, token_ttl: u64) -> Result<Self, DatError> {
if kid.to_string().contains(['.', '\r', '\n']) {
return Err(DatError::InvalidDatKidFormat);
}
Ok(DatKey { kid, signature_key, crypto_key, issue_begin, issue_end, token_ttl })
}
pub fn kid(&self) -> T { self.kid.clone() }
pub fn signature_key(&self) -> SignatureKey { self.signature_key.clone() }
pub fn crypto_key(&self) -> CryptoKey { self.crypto_key.clone() }
pub fn issue_begin(&self) -> u64 { self.issue_begin }
pub fn issue_end(&self) -> u64 { self.issue_end }
pub fn token_ttl(&self) -> u64 { self.token_ttl }
pub fn key_expire(&self) -> u64 { self.issue_end + self.token_ttl }
pub fn expired_verifying(&self) -> bool {
self.key_expire() < now_unix_timestamp()
}
pub fn issuable(&self) -> bool {
let now = now_unix_timestamp();
self.has_signing_key() && self.issue_begin() <= now && self.issue_end() > now
}
pub fn has_signing_key(&self) -> bool {
self.signature_key.has_signing_key()
}
pub fn export(&self, signature_key_out_option: SignatureKeyOutOption) -> Result<String, DatError> {
let kid = self.kid.to_string();
let signature_algorithm = self.signature_key.algorithm();
let (sk, vk) = self.signature_key.to_bytes();
if sk.is_empty() && signature_key_out_option != SignatureKeyOutOption::VERIFYING {
return Err(DatError::VerifyOnlyKey)
}
let signature_key = match signature_key_out_option {
SignatureKeyOutOption::FULL => format!("{}~{}", encode_base64_url(sk), encode_base64_url(vk)),
SignatureKeyOutOption::SIGNING => encode_base64_url(sk),
SignatureKeyOutOption::VERIFYING => format!("~{}", encode_base64_url(vk)),
};
let crypto_algorithm = self.crypto_key.algorithm();
let crypto_key = encode_base64_url(self.crypto_key.to_bytes());
let issue_begin = self.issue_begin;
let issue_end = self.issue_end;
let token_ttl = self.token_ttl;
Ok(format!("{VERSION_DAT}.{kid}.{signature_algorithm}.{signature_key}.{crypto_algorithm}.{crypto_key}.{issue_begin}.{issue_end}.{token_ttl}"))
}
pub fn to_payload(&self, dat: Dat<T>) -> Result<DatPayload, DatError> {
if self.signature_key.verify(dat.body_bytes(), &dat.signature).is_err() {
return Err(DatError::InvalidDat)
}
self.to_payload_without_verify(dat)
}
pub fn to_payload_without_verify(&self, dat: Dat<T>) -> Result<DatPayload, DatError> {
let plain = dat.plain()?;
let secure = self.crypto_key.decrypt(dat.secure()?)?;
Ok(DatPayload {
expire: dat.expire(),
plain_bytes: plain,
secure_bytes: secure,
})
}
pub fn to_dat<U: AsRef<[u8]>>(&self, plain: U, secure: U) -> Result<String, DatError> {
let mut ib = itoa::Buffer::new();
let expire = ib.format(now_unix_timestamp() + self.token_ttl);
let kid = self.kid.to_string();
let plain = plain.as_ref();
let secure = secure.as_ref();
let mut v: String = String::with_capacity(50 + expire.len() + kid.len() + ((plain.len() + secure.len() + self.signature_key.signature_size()) * 4 / 3));
let sk = &self.signature_key;
v.push_str(expire);
v.push('.');
v.push_str(&*kid);
v.push('.');
encode_base64_url_out(plain, &mut v);
v.push('.');
encode_base64_url_out(self.crypto_key.encrypt(secure)?, &mut v);
let signature = sk.sign(v.as_bytes())?;
v.push('.');
encode_base64_url_out(&*signature, &mut v);
Ok(v)
}
}
impl <T: Kid> FromStr for DatKey<T> {
type Err = DatError;
fn from_str(format: &str) -> Result<Self, Self::Err> {
let parts = format.split(".").collect::<Vec<&str>>();
let count = parts.len();
let version = parts[0];
match version {
"2" | "1" => {
return if count == 9 {
let kid = parts[1].parse::<T>().map_err(|_| DatError::InvalidDatKidFormat)?;
let signature_algorithm = SignatureAlgorithm::from_str(parts[2])
.map_err(|_| DatError::UnknownDatSignatureAlgorithm)?;
let signature_key_str = parts[3];
let signature_key = if let Some(pos) = signature_key_str.find('~') {
if pos == 0 { SignatureKey::from_bytes(signature_algorithm, &[], &*decode_base64_url(signature_key_str[1..].as_bytes())?)
} else { SignatureKey::from_bytes(signature_algorithm, &*decode_base64_url(signature_key_str[..pos].as_bytes())?, &*decode_base64_url(signature_key_str[pos + 1..].as_bytes())?)
}
} else { SignatureKey::from_bytes(signature_algorithm, &*decode_base64_url(signature_key_str)?, &[])
}?;
let crypto_algorithm = CryptoAlgorithm::from_str(parts[4])
.map_err(|_| DatError::UnknownCryptoAlgorithm)?;
let crypto_key = CryptoKey::from_bytes(crypto_algorithm, &*decode_base64_url(parts[5])?)?;
let issue_begin = parts[6].parse::<u64>().map_err(|_| DatError::InvalidDatKeyFormat)?;
let issue_end = parts[7].parse::<u64>().map_err(|_| DatError::InvalidDatKeyFormat)?;
let token_ttl = parts[8].parse::<u64>().map_err(|_| DatError::InvalidDatKeyFormat)?;
DatKey::from(kid, signature_key, crypto_key, issue_begin, issue_end, token_ttl)
} else {
Err(DatError::InvalidDatKeyFormat)
}
},
_ => {}
}
Err(DatError::UnSupportDatKeyVersion)
}
}
impl <T: Kid> PartialEq<DatKey<T>> for DatKey<T> {
fn eq(&self, other: &DatKey<T>) -> bool {
self.kid.eq(&other.kid)
}
}
impl <T: Kid> PartialEq<T> for DatKey<T> {
fn eq(&self, other: &T) -> bool {
self.kid.eq(other)
}
}
impl <T: Kid> Clone for DatKey<T> {
fn clone(&self) -> Self {
DatKey::<T> {
kid: self.kid.clone(),
signature_key: self.signature_key.clone(),
crypto_key: self.crypto_key.clone(),
issue_begin: self.issue_begin,
issue_end: self.issue_end,
token_ttl: self.token_ttl,
}
}
}