use chrono::Utc;
use dryoc::constants::{
CRYPTO_SIGN_PUBLICKEYBYTES,
CRYPTO_SIGN_SECRETKEYBYTES
};
use dryoc::sign::SigningKeyPair;
use base64::{Engine as _, engine::general_purpose};
use crate::error::NcryptfError as Error;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Token {
pub access_token: String,
pub refresh_token: String,
pub ikm: Vec<u8>,
pub signature: Vec<u8>,
pub expires_at: i64,
}
impl Token {
pub fn new(lifetime: i64) -> Token {
let now = Utc::now().timestamp();
let expires_at: i64;
if lifetime >= 0 {
expires_at = now + lifetime;
} else {
expires_at = now;
}
return Self {
access_token: general_purpose::URL_SAFE.encode(crate::util::randombytes_buf(48)),
refresh_token: general_purpose::URL_SAFE.encode(crate::util::randombytes_buf(64)),
ikm: crate::util::randombytes_buf(32),
signature: crate::util::randombytes_buf(64),
expires_at: expires_at,
};
}
pub fn from(
access_token: String,
refresh_token: String,
ikm: Vec<u8>,
signature: Vec<u8>,
expires_at: i64,
) -> Result<Token, Error> {
if ikm.len() != 32 {
return Err(Error::InvalidArgument(format!(
"Initial key material should be {} bytes",
32
)));
}
if signature.len() != 64 {
return Err(Error::InvalidArgument(format!(
"Signature secret key should be {} bytes",
64
)));
}
return Ok(Self {
access_token,
refresh_token,
ikm,
signature,
expires_at,
});
}
pub fn from_json(json: serde_json::Value) -> Result<Self, Error> {
if json.get("access_token").is_none()
|| json.get("refresh_token").is_none()
|| json.get("ikm").is_none()
|| json.get("signing").is_none()
|| json.get("expires_at").is_none()
{
return Err(Error::InvalidArgument(format!(
"The provided JSON object is not valid for tokenization."
)));
}
return match Token::from(
json.get("access_token").unwrap().to_string(),
json.get("refresh_token").unwrap().to_string(),
general_purpose::STANDARD.decode(json.get("ikm").unwrap().as_str().unwrap()).unwrap(),
general_purpose::STANDARD.decode(json.get("signing").unwrap().as_str().unwrap()).unwrap(),
json.get("expires_at").unwrap().as_i64().unwrap(),
) {
Ok(token) => return Ok(token),
Err(_error) => Err(_error),
};
}
pub fn is_expired(&self) -> bool {
let now = Utc::now().timestamp();
return now > self.expires_at;
}
pub fn get_signature_public_key(&self) -> Result<Vec<u8>, Error> {
if self.signature.len() != CRYPTO_SIGN_SECRETKEYBYTES {
return Err(Error::TokenSignatureSize(format!(
"Signature secret key should be {} bytes",
CRYPTO_SIGN_SECRETKEYBYTES
)));
}
let mut secret_key_bytes = [0u8; CRYPTO_SIGN_SECRETKEYBYTES];
secret_key_bytes.copy_from_slice(&self.signature);
let keypair: SigningKeyPair<[u8; CRYPTO_SIGN_PUBLICKEYBYTES], [u8; CRYPTO_SIGN_SECRETKEYBYTES]> = SigningKeyPair::from_secret_key(secret_key_bytes);
return Ok(keypair.public_key.to_vec());
}
}