use std::{
str,
};
use subtle::ConstantTimeEq;
use crate::core::{Base64Encodable, Footer, Header, ImplicitAssertion, ImplicitAssertionCapable, PasetoError, Payload, PurposeTrait, VersionTrait};
#[derive(Default, Copy, Clone)]
pub struct Paseto<'a, Version, Purpose>
where
Version: VersionTrait,
Purpose: PurposeTrait,
{
pub(crate) header: Header<Version, Purpose>,
pub(crate) payload: Payload<'a>,
pub(crate) footer: Option<Footer<'a>>,
pub(crate) implicit_assertion: Option<ImplicitAssertion<'a>>,
}
impl<'a, Version: VersionTrait, Purpose: PurposeTrait> Paseto<'a, Version, Purpose> {
pub const MAX_TOKEN_SIZE: usize = 64 * 1024;
pub const MAX_FOOTER_SIZE: usize = 1024;
#[must_use]
pub fn builder() -> Self {
Self::default()
}
pub fn set_payload(&mut self, payload: Payload<'a>) -> &mut Self {
self.payload = payload;
self
}
pub fn set_footer(&mut self, footer: Footer<'a>) -> &mut Self {
self.footer = Some(footer);
self
}
pub(crate) fn format_token(&self, encrypted_payload: &str) -> String {
let header = &self.header;
self.footer.map(|f| f.encode()).map_or_else(
|| format!("{header}{encrypted_payload}"),
|f| format!("{header}{encrypted_payload}.{f}"),
)
}
pub(crate) fn parse_raw_token(
raw_token: &'a str,
footer: impl Into<Option<Footer<'a>>> + Copy,
v: &Version,
p: &Purpose,
) -> Result<Vec<u8>, PasetoError> {
if raw_token.len() > Self::MAX_TOKEN_SIZE {
return Err(PasetoError::TokenTooLarge);
}
let potential_parts = raw_token.split('.').collect::<Vec<_>>();
let parts_len = potential_parts.len();
if !(3..=4).contains(&parts_len) {
return Err(PasetoError::IncorrectSize);
}
let version_part = potential_parts.first().ok_or(PasetoError::IncorrectSize)?;
let purpose_part = potential_parts.get(1).ok_or(PasetoError::IncorrectSize)?;
let payload_part = potential_parts.get(2).ok_or(PasetoError::IncorrectSize)?;
if parts_len == 4 {
let footer_part = potential_parts.get(3).ok_or(PasetoError::IncorrectSize)?;
if footer_part.len() > Self::MAX_FOOTER_SIZE {
return Err(PasetoError::FooterTooLarge);
}
let footer = footer.into().unwrap_or_default();
let found_footer = Footer::from(*footer_part);
if !footer.constant_time_equals(found_footer) {
return Err(PasetoError::FooterInvalid);
}
}
let potential_header = format!("{version_part}.{purpose_part}.");
let expected_header = format!("{v}.{p}.");
if !bool::from(potential_header.as_bytes().ct_eq(expected_header.as_bytes())) {
return Err(PasetoError::WrongHeader);
}
let encrypted_payload = Payload::from(*payload_part);
Ok(encrypted_payload.decode()?)
}
}
impl<'a, Version, Purpose> Paseto<'a, Version, Purpose>
where
Purpose: PurposeTrait,
Version: ImplicitAssertionCapable,
{
pub fn set_implicit_assertion(&mut self, implicit_assertion: ImplicitAssertion<'a>) -> &mut Self {
self.implicit_assertion = Some(implicit_assertion);
self
}
}