use anyhow::{bail, Context, Result};
use base64::write::EncoderWriter;
use serde_json::value::Value;
use std::io::{Read, Write};
use crate::{JwsHeader, Sign};
static DOT_ARRAY: &[u8] = ".".as_bytes();
pub fn serialize(
algorithm: String,
header: JwsHeader,
payload: &mut impl Read,
signer: impl Sign,
) -> Result<Vec<u8>> {
let mut writer = SerializeJwsWriter::new(Vec::new(), algorithm, header, signer)?;
std::io::copy(payload, &mut writer)?;
writer.finish()
}
pub struct SerializeJwsWriter<W, S: Write> {
delegate: Option<W>,
encoder: EncoderWriter<S>,
}
impl<W, S> SerializeJwsWriter<W, S>
where
W: Write,
S: Sign,
{
pub fn new(
mut writer: W,
algorithm: String,
mut header: JwsHeader,
mut signer: S,
) -> Result<Self> {
header.insert("alg".to_owned(), Value::String(algorithm));
let encoded_header = {
let mut encoder = EncoderWriter::new(Vec::new(), base64::URL_SAFE_NO_PAD);
serde_json::to_writer(&mut encoder, &header)?;
encoder.finish()?
};
signer.write_all(&encoded_header)?;
signer.write_all(DOT_ARRAY)?;
writer.write_all(&encoded_header)?;
writer.write_all(DOT_ARRAY)?;
writer.write_all(DOT_ARRAY)?;
Ok(Self {
delegate: Some(writer),
encoder: EncoderWriter::new(signer, base64::URL_SAFE_NO_PAD),
})
}
pub fn finish(&mut self) -> Result<W> {
if self.delegate.is_none() {
bail!("Serializer has already had finish() called")
};
let signer = self.encoder.finish()?;
let signature = signer.get_sign()?;
let encoded_signature = {
let mut encoder = EncoderWriter::new(Vec::new(), base64::URL_SAFE_NO_PAD);
encoder.write_all(&signature)?;
encoder.finish()?
};
self.delegate
.as_mut()
.unwrap()
.write_all(&encoded_signature)?;
Ok(self.delegate.take().context("Writer must be present")?)
}
}
impl<W, S> Write for SerializeJwsWriter<W, S>
where
S: Write,
{
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.encoder.write(buf)
}
fn flush(&mut self) -> std::io::Result<()> {
self.encoder.flush()
}
}