scuffle_rtmp/handshake/complex/
digest.rs1use std::io;
4
5use bytes::Bytes;
6use hmac::{Hmac, Mac};
7use sha2::Sha256;
8
9use super::error::ComplexHandshakeError;
10use super::{RTMP_DIGEST_LENGTH, SchemaVersion};
11use crate::handshake::{CHUNK_LENGTH, TIME_VERSION_LENGTH};
12
13pub struct DigestProcessor<'a> {
17 data: Bytes,
18 key: &'a [u8],
19}
20
21pub struct DigestResult {
26 pub left: Bytes,
28 pub digest: [u8; 32],
30 pub right: Bytes,
32}
33
34impl DigestResult {
35 pub fn write_to(&self, writer: &mut impl io::Write) -> io::Result<()> {
37 writer.write_all(&self.left)?;
38 writer.write_all(&self.digest)?;
39 writer.write_all(&self.right)?;
40
41 Ok(())
42 }
43}
44
45impl<'a> DigestProcessor<'a> {
46 pub const fn new(data: Bytes, key: &'a [u8]) -> Self {
48 Self { data, key }
49 }
50
51 pub fn read_digest(&self) -> Result<(Bytes, SchemaVersion), ComplexHandshakeError> {
57 if let Ok(digest) = self.generate_and_validate(SchemaVersion::Schema0) {
58 Ok((digest, SchemaVersion::Schema0))
59 } else {
60 let digest = self.generate_and_validate(SchemaVersion::Schema1)?;
61 Ok((digest, SchemaVersion::Schema1))
62 }
63 }
64
65 pub fn generate_and_fill_digest(&self, version: SchemaVersion) -> Result<DigestResult, ComplexHandshakeError> {
67 let (left_part, _, right_part) = self.split_message(version)?;
68 let computed_digest = self.make_digest(&left_part, &right_part)?;
69
70 Ok(DigestResult {
74 left: left_part,
75 digest: computed_digest,
76 right: right_part,
77 })
78 }
79
80 fn find_digest_offset(&self, version: SchemaVersion) -> Result<usize, ComplexHandshakeError> {
81 const OFFSET_LENGTH: usize = 4;
82
83 let schema_offset = match version {
86 SchemaVersion::Schema0 => CHUNK_LENGTH + TIME_VERSION_LENGTH,
87 SchemaVersion::Schema1 => TIME_VERSION_LENGTH,
88 };
89
90 Ok((*self.data.get(schema_offset).unwrap() as usize
95 + *self.data.get(schema_offset + 1).unwrap() as usize
96 + *self.data.get(schema_offset + 2).unwrap() as usize
97 + *self.data.get(schema_offset + 3).unwrap() as usize)
98 % (CHUNK_LENGTH - RTMP_DIGEST_LENGTH - OFFSET_LENGTH)
99 + schema_offset
100 + OFFSET_LENGTH)
101 }
102
103 fn split_message(&self, version: SchemaVersion) -> Result<(Bytes, Bytes, Bytes), ComplexHandshakeError> {
104 let digest_offset = self.find_digest_offset(version)?;
105
106 let left_part = self.data.slice(0..digest_offset);
116 let digest_data = self.data.slice(digest_offset..digest_offset + RTMP_DIGEST_LENGTH);
117 let right_part = self.data.slice(digest_offset + RTMP_DIGEST_LENGTH..);
118
119 Ok((left_part, digest_data, right_part))
120 }
121
122 pub fn make_digest(&self, left: &[u8], right: &[u8]) -> Result<[u8; 32], ComplexHandshakeError> {
124 let mut mac = Hmac::<Sha256>::new_from_slice(self.key).unwrap();
126 mac.update(left);
128 mac.update(right);
129
130 let result = mac.finalize().into_bytes();
132 if result.len() != RTMP_DIGEST_LENGTH {
133 return Err(ComplexHandshakeError::DigestLengthNotCorrect);
134 }
135
136 Ok(result.into())
138 }
139
140 fn generate_and_validate(&self, version: SchemaVersion) -> Result<Bytes, ComplexHandshakeError> {
141 let (left_part, digest_data, right_part) = self.split_message(version)?;
144
145 if digest_data == self.make_digest(&left_part, &right_part)?.as_ref() {
148 Ok(digest_data)
149 } else {
150 Err(ComplexHandshakeError::CannotGenerate)
154 }
155 }
156}