use super::schedule::{HashAlg, Secret};
use alloc::vec::Vec;
pub(crate) struct Transcript {
alg: Option<HashAlg>,
buf: Vec<u8>,
}
impl Transcript {
pub(crate) fn new() -> Self {
Transcript {
alg: None,
buf: Vec::new(),
}
}
pub(crate) fn set_alg(&mut self, alg: HashAlg) {
self.alg = Some(alg);
}
pub(crate) fn update(&mut self, message: &[u8]) {
self.buf.extend_from_slice(message);
}
pub(crate) fn buffered_bytes(&self) -> &[u8] {
&self.buf
}
pub(crate) fn current_hash(&self) -> Secret {
let alg = self
.alg
.expect("transcript hash used before suite negotiated");
alg.hash(&self.buf)
}
#[cfg(feature = "ech")]
pub(crate) fn hash_with_appended(&self, extra: &[u8]) -> Secret {
let alg = self
.alg
.expect("transcript hash used before suite negotiated");
let mut tmp = Vec::with_capacity(self.buf.len() + extra.len());
tmp.extend_from_slice(&self.buf);
tmp.extend_from_slice(extra);
alg.hash(&tmp)
}
#[cfg(feature = "ech")]
pub(crate) fn replace_buf(&mut self, new_buf: Vec<u8>) {
self.buf = new_buf;
}
pub(crate) fn replace_with_message_hash(&mut self) {
let alg = self
.alg
.expect("transcript hash used before suite negotiated");
let h = alg.hash(&self.buf);
let mut synthetic = Vec::with_capacity(4 + h.as_slice().len());
synthetic.push(254); synthetic.extend_from_slice(&[0, 0]);
synthetic.push(h.as_slice().len() as u8);
synthetic.extend_from_slice(h.as_slice());
self.buf = synthetic;
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::hash::{Digest, Sha256};
#[test]
fn hash_matches_concatenation() {
let mut t = Transcript::new();
t.set_alg(HashAlg::Sha256);
t.update(b"hello ");
t.update(b"world");
assert_eq!(
t.current_hash().as_slice(),
Sha256::digest(b"hello world").as_ref()
);
}
#[test]
fn message_hash_rewrite() {
let mut t = Transcript::new();
t.set_alg(HashAlg::Sha256);
t.update(b"client hello 1");
let inner = Sha256::digest(b"client hello 1");
t.replace_with_message_hash();
let mut expected = alloc::vec![254u8, 0, 0, 32];
expected.extend_from_slice(inner.as_ref());
assert_eq!(
t.current_hash().as_slice(),
Sha256::digest(&expected).as_ref()
);
}
}