rar_stream/parsing/rar5/
encryption_header.rs1use super::VintReader;
7use crate::crc32::crc32 as compute_crc32;
8use crate::error::{RarError, Result};
9
10#[derive(Debug, Clone, PartialEq, Eq)]
12pub struct Rar5EncryptionHeader {
13 pub version: u8,
15 pub flags: u8,
17 pub lg2_count: u8,
19 pub salt: [u8; 16],
21 pub check_value: Option<[u8; 12]>,
23}
24
25impl Rar5EncryptionHeader {
26 pub const FLAG_CHECK_PRESENT: u8 = 0x01;
28}
29
30pub struct Rar5EncryptionHeaderParser;
31
32impl Rar5EncryptionHeaderParser {
33 pub fn parse(data: &[u8]) -> Result<(Rar5EncryptionHeader, usize)> {
36 if data.len() < 4 {
37 return Err(RarError::InvalidHeader);
38 }
39
40 let mut pos = 0;
41
42 let crc32 = u32::from_le_bytes([data[0], data[1], data[2], data[3]]);
44 pos += 4;
45
46 let mut reader = VintReader::new(&data[pos..]);
48 let header_size = reader.read().ok_or(RarError::InvalidHeader)?;
49 let header_content_start = pos + reader.position();
50 pos += reader.position();
51
52 let mut reader = VintReader::new(&data[pos..]);
54 let header_type = reader.read().ok_or(RarError::InvalidHeader)?;
55 pos += reader.position();
56
57 if header_type != 4 {
58 return Err(RarError::InvalidHeaderType(header_type as u8));
59 }
60
61 let mut reader = VintReader::new(&data[pos..]);
63 let _header_flags = reader.read().ok_or(RarError::InvalidHeader)?;
64 pos += reader.position();
65
66 let mut reader = VintReader::new(&data[pos..]);
68 let version = reader.read().ok_or(RarError::InvalidHeader)? as u8;
69 pos += reader.position();
70
71 let mut reader = VintReader::new(&data[pos..]);
73 let flags = reader.read().ok_or(RarError::InvalidHeader)? as u8;
74 pos += reader.position();
75
76 if pos >= data.len() {
78 return Err(RarError::InvalidHeader);
79 }
80 let lg2_count = data[pos];
81 pos += 1;
82
83 if pos + 16 > data.len() {
85 return Err(RarError::InvalidHeader);
86 }
87 let mut salt = [0u8; 16];
88 salt.copy_from_slice(&data[pos..pos + 16]);
89 pos += 16;
90
91 let check_value = if flags & Rar5EncryptionHeader::FLAG_CHECK_PRESENT != 0 {
93 if pos + 12 > data.len() {
94 return Err(RarError::InvalidHeader);
95 }
96 let mut check = [0u8; 12];
97 check.copy_from_slice(&data[pos..pos + 12]);
98 Some(check)
100 } else {
101 None
102 };
103
104 let total_consumed = header_content_start
105 .checked_add(header_size as usize)
106 .ok_or(RarError::InvalidHeader)?;
107
108 if total_consumed > 4 && total_consumed <= data.len() {
110 let actual_crc = compute_crc32(&data[4..total_consumed]);
111 if actual_crc != crc32 {
112 return Err(RarError::CrcMismatch {
113 expected: crc32,
114 actual: actual_crc,
115 });
116 }
117 }
118
119 Ok((
120 Rar5EncryptionHeader {
121 version,
122 flags,
123 lg2_count,
124 salt,
125 check_value,
126 },
127 total_consumed,
128 ))
129 }
130
131 pub fn is_encryption_header(data: &[u8]) -> bool {
133 if data.len() < 7 {
134 return false;
135 }
136
137 let mut pos = 4;
139 let mut reader = VintReader::new(&data[pos..]);
140 if reader.read().is_none() {
141 return false;
142 }
143 pos += reader.position();
144
145 let mut reader = VintReader::new(&data[pos..]);
147 matches!(reader.read(), Some(4))
148 }
149}
150
151#[cfg(test)]
152mod tests {
153 use super::*;
154
155 #[test]
156 fn test_is_encryption_header() {
157 let mut data = vec![0u8; 20];
159 data[4] = 5; data[5] = 4; assert!(Rar5EncryptionHeaderParser::is_encryption_header(&data));
163 }
164
165 #[test]
166 fn test_is_not_encryption_header() {
167 let mut data = vec![0u8; 20];
169 data[4] = 5; data[5] = 2; assert!(!Rar5EncryptionHeaderParser::is_encryption_header(&data));
173 }
174}