identity_jose/jwu/
serde.rsuse core::str;
use crate::error::Error;
use crate::error::Result;
use crate::jose::JoseHeader;
use crate::jws::JwsHeader;
const DEFAULT_B64: bool = true;
const PREDEFINED: &[&str] = &[
"alg", "jku", "jwk", "kid", "x5u", "x5c", "x5t", "x5t#s256", "typ", "cty", "crit", "enc", "zip", "epk", "apu", "apv",
"iv", "tag", "p2s", "p2c",
];
const PERMITTED_CRITS: &[&str] = &["b64"];
pub(crate) fn parse_utf8(slice: &(impl AsRef<[u8]> + ?Sized)) -> Result<&str> {
str::from_utf8(slice.as_ref()).map_err(Error::InvalidUtf8)
}
pub(crate) fn filter_non_empty_bytes<'a, T, U>(value: T) -> Option<&'a [u8]>
where
T: Into<Option<&'a U>>,
U: AsRef<[u8]> + ?Sized + 'a,
{
value.into().map(AsRef::as_ref).filter(|value| !value.is_empty())
}
pub(crate) fn create_message(header: &[u8], claims: &[u8]) -> Vec<u8> {
let capacity: usize = header.len() + 1 + claims.len();
let mut message: Vec<u8> = Vec::with_capacity(capacity);
message.extend(header);
message.push(b'.');
message.extend(claims);
message
}
pub(crate) fn extract_b64(header: Option<&JwsHeader>) -> bool {
header.and_then(JwsHeader::b64).unwrap_or(DEFAULT_B64)
}
pub(crate) fn validate_jws_headers(protected: Option<&JwsHeader>, unprotected: Option<&JwsHeader>) -> Result<()> {
validate_disjoint(protected, unprotected)?;
validate_crit(protected, unprotected)?;
validate_b64(protected, unprotected)?;
Ok(())
}
pub(crate) fn validate_crit<T>(protected: Option<&T>, unprotected: Option<&T>) -> Result<()>
where
T: JoseHeader,
{
if unprotected.map(|header| header.has_claim("crit")).unwrap_or_default() {
return Err(Error::InvalidParam("unprotected crit"));
}
let values: Option<&[String]> = protected.and_then(|header| header.common().crit());
if values.map(|values| values.is_empty()).unwrap_or_default() {
return Err(Error::InvalidParam("empty crit"));
}
let values: &[String] = values.unwrap_or_default();
for value in values {
if PREDEFINED.contains(&&**value) {
return Err(Error::InvalidParam("crit contains pre-defined parameters"));
}
if !PERMITTED_CRITS.contains(&AsRef::<str>::as_ref(value)) {
return Err(Error::InvalidParam("unpermitted crit"));
}
let exists: bool = protected
.map(|header| header.has_claim(value))
.or_else(|| unprotected.map(|header| header.has_claim(value)))
.unwrap_or_default();
if !exists {
return Err(Error::InvalidParam("crit"));
}
}
Ok(())
}
pub(crate) fn validate_b64(protected: Option<&JwsHeader>, unprotected: Option<&JwsHeader>) -> Result<()> {
if unprotected.and_then(JwsHeader::b64).is_some() {
return Err(Error::InvalidParam("unprotected `b64` parameter"));
}
let b64: Option<bool> = protected.and_then(|header| header.b64());
let crit: Option<&[String]> = protected.and_then(|header| header.crit());
match (b64, crit) {
(Some(_), Some(values)) if values.iter().any(|value| value == "b64") => Ok(()),
(Some(_), None) => Err(Error::InvalidParam(
"`b64` param must be included in the crit parameter values",
)),
_ => Ok(()),
}
}
pub(crate) fn validate_disjoint(protected: Option<&JwsHeader>, unprotected: Option<&JwsHeader>) -> Result<()> {
let is_disjoint: bool = match (protected, unprotected) {
(Some(protected), Some(unprotected)) => protected.is_disjoint(unprotected),
_ => true,
};
if is_disjoint {
Ok(())
} else {
Err(Error::InvalidContent(
"protected and unprotected headers are not disjoint",
))
}
}