use {
super::{
define,
define::SchemaVersion,
errors::{DigestError, DigestErrorValue},
},
bytes::BytesMut,
bytesio::bytes_reader::BytesReader,
hmac::{Hmac, Mac, NewMac},
sha2::Sha256,
};
pub struct DigestProcessor {
reader: BytesReader,
key: BytesMut,
}
impl DigestProcessor {
pub fn new(data: BytesMut, key: BytesMut) -> Self {
Self {
reader: BytesReader::new(data),
key,
}
}
pub fn read_digest(&mut self) -> Result<(BytesMut, SchemaVersion), DigestError> {
if let Ok(digest) = self.generate_and_validate(SchemaVersion::Schema0) {
return Ok((digest, SchemaVersion::Schema0));
}
let digest = self.generate_and_validate(SchemaVersion::Schema1)?;
Ok((digest, SchemaVersion::Schema1))
}
pub fn generate_and_fill_digest(&mut self) -> Result<Vec<u8>, DigestError> {
let (left_part, _, right_part) = self.cook_raw_message(SchemaVersion::Schema0)?;
let raw_message = [left_part.clone(), right_part.clone()].concat();
let computed_digest = self.make_digest(raw_message)?;
let result = [left_part, computed_digest, right_part].concat();
Ok(result)
}
pub fn generate_digest(&mut self) -> Result<BytesMut, DigestError> {
let (left_part, _, right_part) = self.cook_raw_message(SchemaVersion::Schema0)?;
let raw_message = [left_part, right_part].concat();
let digest = self.make_digest(raw_message)?;
Ok(digest)
}
fn find_digest_offset(&mut self, version: SchemaVersion) -> Result<usize, DigestError> {
let mut digest_offset: usize = 0;
match version {
SchemaVersion::Schema0 => {
digest_offset += self.reader.get(772)? as usize;
digest_offset += self.reader.get(773)? as usize;
digest_offset += self.reader.get(774)? as usize;
digest_offset += self.reader.get(775)? as usize;
digest_offset %= 728;
digest_offset += 776;
}
SchemaVersion::Schema1 => {
digest_offset += self.reader.get(8)? as usize;
digest_offset += self.reader.get(9)? as usize;
digest_offset += self.reader.get(10)? as usize;
digest_offset += self.reader.get(11)? as usize;
digest_offset %= 728;
digest_offset += 12;
}
SchemaVersion::Unknown => {
return Err(DigestError {
value: DigestErrorValue::UnknowSchema,
});
}
}
Ok(digest_offset)
}
fn cook_raw_message(
&mut self,
version: SchemaVersion,
) -> Result<(BytesMut, BytesMut, BytesMut), DigestError> {
let digest_offset: usize = self.find_digest_offset(version)?;
let mut new_reader = BytesReader::new(self.reader.get_remaining_bytes());
let left_part = new_reader.read_bytes(digest_offset)?;
let digest_data = new_reader.read_bytes(define::RTMP_DIGEST_LENGTH)?;
let right_part = new_reader.extract_remaining_bytes();
Ok((left_part, digest_data, right_part))
}
pub fn make_digest(&mut self, raw_message: Vec<u8>) -> Result<BytesMut, DigestError> {
let mut mac = Hmac::<Sha256>::new_from_slice(&self.key[..]).unwrap();
mac.update(&raw_message);
let result = mac.finalize().into_bytes();
if result.len() != define::RTMP_DIGEST_LENGTH {
return Err(DigestError {
value: DigestErrorValue::DigestLengthNotCorrect,
});
}
let mut rv = BytesMut::new();
rv.extend_from_slice(result.as_slice());
Ok(rv)
}
fn generate_and_validate(&mut self, version: SchemaVersion) -> Result<BytesMut, DigestError> {
let (left_part, digest_data, right_part) = self.cook_raw_message(version)?;
let raw_message = [left_part, right_part].concat();
let computed_digest = self.make_digest(raw_message)?;
if digest_data == computed_digest {
return Ok(digest_data);
}
Err(DigestError {
value: DigestErrorValue::CannotGenerate,
})
}
}