use std::{borrow::Cow, ops::Deref};
use gdp_rs::{proven::ProvenError, Proven};
use picky::jose::jws::JwsHeader;
pub use predicate::InvalidDPoPProofHeader;
use predicate::IsValidDPoPProofHeader;
use serde::{Deserialize, Serialize};
use self::{alg::DPoPProofAlg, jwk::DPoPProofJwk};
pub mod alg;
pub mod jwk;
pub const DPOP_PROOF_TYPE: &str = "dpop+jwt";
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(transparent)]
pub struct DPoPProofHeader<'inner>(Proven<Cow<'inner, JwsHeader>, IsValidDPoPProofHeader>);
impl<'inner> TryFrom<Cow<'inner, JwsHeader>> for DPoPProofHeader<'inner> {
type Error = ProvenError<Cow<'inner, JwsHeader>, InvalidDPoPProofHeader>;
#[inline]
fn try_from(jwk: Cow<'inner, JwsHeader>) -> Result<Self, Self::Error> {
Ok(Self(Proven::try_new(jwk)?))
}
}
impl<'inner> TryFrom<&'inner JwsHeader> for DPoPProofHeader<'inner> {
type Error = InvalidDPoPProofHeader;
fn try_from(value: &'inner JwsHeader) -> Result<Self, Self::Error> {
Self::try_from(Cow::Borrowed(value)).map_err(|e| e.error)
}
}
impl TryFrom<JwsHeader> for DPoPProofHeader<'static> {
type Error = ProvenError<JwsHeader, InvalidDPoPProofHeader>;
#[inline]
fn try_from(header: JwsHeader) -> Result<Self, Self::Error> {
Ok(Self(Proven::try_new(Cow::Owned(header)).map_err(|e| {
ProvenError {
error: e.error,
subject: (e.subject as Cow<JwsHeader>).into_owned(),
}
})?))
}
}
impl<'inner> From<DPoPProofHeader<'inner>> for Cow<'inner, JwsHeader> {
#[inline]
fn from(alg: DPoPProofHeader<'inner>) -> Self {
alg.0.into_subject()
}
}
impl<'inner> From<DPoPProofHeader<'inner>> for JwsHeader {
#[inline]
fn from(alg: DPoPProofHeader<'inner>) -> Self {
alg.0.into_subject().into_owned()
}
}
impl<'inner> Deref for DPoPProofHeader<'inner> {
type Target = JwsHeader;
#[inline]
fn deref(&self) -> &Self::Target {
self.0.as_ref()
}
}
impl<'inner> DPoPProofHeader<'inner> {
#[inline]
pub unsafe fn new_unchecked(header: Cow<'inner, JwsHeader>) -> Self {
Self(Proven::new_unchecked(header))
}
#[inline]
pub fn to_borrowed(&self) -> DPoPProofHeader<'_> {
unsafe { DPoPProofHeader::new_unchecked(Cow::Borrowed(self.0.as_ref())) }
}
#[inline]
pub fn into_owned(self) -> DPoPProofHeader<'static> {
unsafe { DPoPProofHeader::new_unchecked(Cow::Owned(self.0.into_subject().into_owned())) }
}
#[inline]
pub fn typ(&self) -> &str {
DPOP_PROOF_TYPE
}
#[inline]
pub fn alg(&self) -> DPoPProofAlg {
unsafe { DPoPProofAlg::new_unchecked(self.0.alg) }
}
#[inline]
pub fn jwk(&self) -> DPoPProofJwk<'_> {
unsafe {
DPoPProofJwk::new_unchecked(Cow::Borrowed(
self.0
.jwk
.as_ref()
.expect("Must be some, as checked at instantiation"),
))
}
}
}
mod predicate {
use std::borrow::{Borrow, Cow};
use gdp_rs::{
predicate::{Predicate, PurePredicate, SyncEvaluablePredicate},
proven::ProvenError,
};
use picky::jose::jws::JwsHeader;
use super::{
alg::{DPoPProofAlg, InvalidDPoPProofAlg},
jwk::{DPoPProofJwk, InvalidDPoPProofJwk},
DPOP_PROOF_TYPE,
};
#[derive(Debug)]
pub struct IsValidDPoPProofHeader;
impl<H: Borrow<JwsHeader>> Predicate<H> for IsValidDPoPProofHeader {
fn label() -> Cow<'static, str> {
"IsValidDPoPProofHeader".into()
}
}
#[derive(Debug, Clone, thiserror::Error)]
pub enum InvalidDPoPProofHeader {
#[error("Required param \"{0}\" is absent in given jose header.")]
RequiredParamIsAbsent(String),
#[error("Invalid dpop-proof typ in jose header.")]
InvalidTyp,
#[error("Invalid dpop-proof alg in jose header.")]
InvalidAlg(#[from] InvalidDPoPProofAlg),
#[error("Invalid dpop-proof jwk in jose header.")]
InvalidJwk(#[from] InvalidDPoPProofJwk),
}
impl<H: Borrow<JwsHeader>> SyncEvaluablePredicate<H> for IsValidDPoPProofHeader {
type EvalError = InvalidDPoPProofHeader;
fn evaluate_for(header_cow: &H) -> Result<(), Self::EvalError> {
let header: &JwsHeader = header_cow.borrow();
if let Some(typ) = header.typ.as_deref() {
if typ != DPOP_PROOF_TYPE {
return Err(InvalidDPoPProofHeader::InvalidTyp);
}
} else {
return Err(InvalidDPoPProofHeader::RequiredParamIsAbsent(
"typ".to_owned(),
));
}
if let Err(e) = DPoPProofAlg::try_from(header.alg) {
return Err(InvalidDPoPProofHeader::InvalidAlg(e.error));
}
if let Some(jwk) = header.jwk.as_ref() {
if let Err(ProvenError { error, .. }) = DPoPProofJwk::try_from(Cow::Borrowed(jwk)) {
return Err(InvalidDPoPProofHeader::InvalidJwk(error));
}
} else {
return Err(InvalidDPoPProofHeader::RequiredParamIsAbsent(
"jwk".to_owned(),
));
}
Ok(())
}
}
impl<H: Borrow<JwsHeader>> PurePredicate<H> for IsValidDPoPProofHeader {}
}