use alloc::{string::String, vec, vec::Vec};
use core::{convert::Infallible, fmt};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use super::{sealed, Format};
use crate::{
header::{self, JoseHeaderBuilder, JoseHeaderBuilderError},
jws::{PayloadData, SignError, Signer},
Base64UrlString, JoseHeader,
};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub(crate) struct Signature {
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) protected: Option<Base64UrlString>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) header: Option<serde_json::Map<String, Value>>,
pub(crate) signature: Base64UrlString,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct JsonGeneral {
pub(crate) payload: Option<Base64UrlString>,
pub(crate) signatures: Vec<Signature>,
}
impl fmt::Display for JsonGeneral {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let repr = if f.alternate() {
serde_json::to_string_pretty(&self).map_err(|_| fmt::Error)?
} else {
serde_json::to_string(&self).map_err(|_| fmt::Error)?
};
f.write_str(&repr)
}
}
impl Format for JsonGeneral {}
impl sealed::SealedFormat<JsonGeneral> for JsonGeneral {
type JwsHeader = Vec<JoseHeader<JsonGeneral, header::Jws>>;
type SerializedJwsHeader = (
Option<Base64UrlString>,
Option<serde_json::Map<String, Value>>,
);
fn update_header<S: AsRef<[u8]>>(header: &mut Self::JwsHeader, signer: &dyn Signer<S>) {
let Some(first) = header.first_mut() else {
return;
};
first.overwrite_alg_and_key_id(signer.algorithm(), signer.key_id());
}
fn serialize_header(
mut header: Self::JwsHeader,
) -> Result<Self::SerializedJwsHeader, SignError<Infallible>> {
let len = header.len();
let Some(header) = header.pop().filter(|_| len == 1) else {
return Err(SignError::HeaderCountMismatch);
};
let (protected, unprotected) = header.into_values().map_err(SignError::InvalidHeader)?;
let protected = match protected {
Some(hdr) => {
let json = serde_json::to_string(&hdr).map_err(SignError::SerializeHeader)?;
let encoded = Base64UrlString::encode(json);
Some(encoded)
}
None => None,
};
Ok((protected, unprotected))
}
fn message_from_header(hdr: &Self::SerializedJwsHeader) -> Option<&[u8]> {
hdr.0.as_ref().map(|x| x.as_bytes())
}
fn finalize(
header: Self::SerializedJwsHeader,
payload: Option<PayloadData>,
signature: &[u8],
) -> Result<Self, serde_json::Error> {
let payload = payload.map(|PayloadData::Standard(b64)| b64);
let signature = Base64UrlString::encode(signature);
Ok(JsonGeneral {
payload,
signatures: vec![Signature {
protected: header.0,
header: header.1,
signature,
}],
})
}
fn finalize_jws_header_builder(
value_ref: &mut Result<Self::JwsHeader, JoseHeaderBuilderError>,
new_builder: JoseHeaderBuilder<JsonGeneral, header::Jws>,
) {
let header = match new_builder.build() {
Ok(header) => header,
Err(err) => {
*value_ref = Err(err);
return;
}
};
match value_ref {
Ok(headers) => headers.push(header),
Err(_) => *value_ref = Ok(vec![header]),
}
}
}