use bh_jws_utils::{jwt, JwkPublic, JwtVerifier as _, SignatureVerifier, SigningAlgorithm};
use bherror::{
traits::{ForeignBoxed, ForeignError, PropagateError},
Error,
};
pub use iref::Uri;
pub use jwt::claims::SecondsSinceEpoch;
use serde::{Deserialize, Serialize};
pub use serde_json::{Map, Value};
use yoke::Yoke;
use crate::error::{FormatError, Result, SignatureError};
mod disclosure;
mod error;
mod path;
pub(crate) mod path_map;
pub use disclosure::*;
pub(crate) use error::*;
pub use path::*;
use crate::{
utils::SD_ALG_FIELD_NAME, Hasher, HashingAlgorithm, IssuerJwt, IssuerJwtHeader,
IssuerPublicKeyLookup,
};
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct CnfClaim {
pub jwk: JwkPublic,
}
pub type JsonObject = Map<String, Value>;
#[inline(always)]
pub(crate) fn into_object(value: Value) -> JsonObject {
if let Value::Object(object) = value {
object
} else {
panic!("Argument wasn't an object")
}
}
#[macro_export]
macro_rules! json_object {
($stuff:tt) => {
match ::serde_json::json!($stuff) {
::serde_json::Value::Object(o) => o,
_ => unreachable!("JSON literal wasn't an object"),
}
};
}
pub(crate) const SD: &str = "_sd";
pub(crate) const ELLIPSIS: &str = "...";
pub(crate) static RESERVED_CLAIM_NAMES: &[&str] = &[SD, SD_ALG_FIELD_NAME, ELLIPSIS];
pub(crate) struct ParsedSdJwtIssuance<State> {
pub(crate) jwt: jwt::Token<IssuerJwtHeader, IssuerJwt, State>,
pub(crate) disclosures: Vec<Disclosure>,
}
#[cfg_attr(test, derive(Debug))]
pub struct IssuedSdJwt(pub(crate) ParsedSdJwtIssuance<jwt::token::Signed>);
impl IssuedSdJwt {
pub fn into_string_compact(self) -> String {
crate::SdJwt::new(
self.0.jwt.into(),
self.0
.disclosures
.into_iter()
.map(Disclosure::into_string)
.collect(),
)
.to_string()
}
}
#[cfg_attr(test, derive(Debug))]
pub(crate) struct SdJwtUnverified<'a>(pub(crate) ParsedSdJwtIssuance<jwt::Unverified<'a>>);
impl SdJwtUnverified<'_> {
pub(crate) async fn verify<'a>(
self,
issuer_public_key_lookup: &impl IssuerPublicKeyLookup,
get_signature_verifier: impl FnOnce(SigningAlgorithm) -> Option<&'a dyn SignatureVerifier>,
) -> Result<(SdJwtSignatureVerified, SigningAlgorithm, JwkPublic), SignatureError> {
let (verifier, alg, key) = self
.get_signature_verifier_and_public_key(issuer_public_key_lookup, get_signature_verifier)
.await?;
let unverified_jwt = self.0.jwt;
let jwt = verifier
.verify_jwt_signature(unverified_jwt, &key)
.foreign_boxed_err(|| SignatureError::InvalidJwtSignature)?;
let disclosures = self.0.disclosures;
Ok((
SdJwtSignatureVerified(ParsedSdJwtIssuance { jwt, disclosures }),
alg,
key,
))
}
async fn get_signature_verifier_and_public_key<'a>(
&self,
issuer_public_key_lookup: &impl IssuerPublicKeyLookup,
get_signature_verifier: impl FnOnce(SigningAlgorithm) -> Option<&'a dyn SignatureVerifier>,
) -> Result<(&'a dyn SignatureVerifier, SigningAlgorithm, JwkPublic), SignatureError> {
let key = issuer_public_key_lookup
.lookup(&self.0.jwt.claims().iss, self.0.jwt.header())
.await
.with_err(|| SignatureError::PublicKeyLookupFailed)?;
let alleged_signing_algorithm = self.0.jwt.header().alg;
let verifier = get_signature_verifier(alleged_signing_algorithm).ok_or_else(|| {
Error::root(SignatureError::MissingSignatureVerifier(
alleged_signing_algorithm,
))
})?;
Ok((verifier, alleged_signing_algorithm, key))
}
}
impl crate::SdJwt {
pub(crate) fn parse(&self) -> Result<SdJwtUnverified<'_>, FormatError> {
let jwt =
jwt::Token::parse_unverified(&self.jwt).foreign_err(|| FormatError::NonParseableJwt)?;
let disclosures = self
.disclosures
.iter()
.cloned()
.map(Disclosure::try_from)
.collect::<Result<Vec<_>, _>>()?;
Ok(SdJwtUnverified(ParsedSdJwtIssuance { jwt, disclosures }))
}
pub(crate) async fn to_signature_verified_sd_jwt<'a>(
&self,
issuer_public_key_lookup: &impl IssuerPublicKeyLookup,
get_signature_verifier: impl FnOnce(SigningAlgorithm) -> Option<&'a dyn SignatureVerifier>,
) -> Result<(SdJwtSignatureVerified, SigningAlgorithm, JwkPublic), crate::Error> {
self.parse()
.match_err(|format_error| crate::Error::Format(format_error.clone()))?
.verify(issuer_public_key_lookup, get_signature_verifier)
.await
.match_err(|signature_error| crate::Error::Signature(signature_error.clone()))
}
}
pub(crate) struct SdJwtSignatureVerified(ParsedSdJwtIssuance<jwt::Verified>);
impl SdJwtSignatureVerified {
pub(crate) fn into_decoded(
self,
get_hasher: impl Fn(HashingAlgorithm) -> Option<Box<dyn Hasher>>,
) -> Result<SdJwtDecoded, crate::Error> {
SdJwtDecoded::new(self, get_hasher)
}
}
pub(crate) struct SdJwtDecoded {
decoded_claims: IssuerJwt,
disclosures_by_path: Yoke<DisclosureByPathTable<'static>, Vec<Disclosure>>,
hasher: Box<dyn Hasher>,
}
impl SdJwtDecoded {
pub(crate) fn new(
verified_sd_jwt: SdJwtSignatureVerified,
get_hasher: impl Fn(HashingAlgorithm) -> Option<Box<dyn Hasher>>,
) -> Result<Self, crate::Error> {
let ParsedSdJwtIssuance { jwt, disclosures } = verified_sd_jwt.0;
let full_payload = jwt.claims().to_object();
let mut owned_output = None;
let disclosures_by_path = Yoke::try_attach_to_cart(disclosures, |disclosures| {
let (decoded_claims, hasher, disclosures_by_path) =
crate::decoder::decode_disclosed_claims(&full_payload, disclosures, get_hasher)
.match_err(|err| crate::Error::Decoding(err.clone()))?;
owned_output = Some((decoded_claims, hasher));
Ok(disclosures_by_path)
})?;
let (decoded_claims, hasher) = owned_output.unwrap();
let decoded_claims = serde_json::from_value(decoded_claims.into())
.foreign_err(|| crate::Error::Format(FormatError::InvalidVcSchema))?;
Ok(Self {
decoded_claims,
disclosures_by_path,
hasher,
})
}
pub(crate) fn disclosures_by_path(&self) -> &DisclosureByPathTable<'_> {
self.disclosures_by_path.get()
}
pub(crate) fn claims(&self) -> &IssuerJwt {
&self.decoded_claims
}
pub(crate) fn into_claims(self) -> IssuerJwt {
self.decoded_claims
}
pub(crate) fn key_binding_public_key(&self) -> &JwkPublic {
&self.decoded_claims.cnf.jwk
}
pub(crate) fn hasher(&self) -> &dyn Hasher {
&*self.hasher
}
}