1use crate::{ImageDatKey, ProfileError, Result};
2use aes::Aes128;
3use cipher::{generic_array::GenericArray, BlockDecrypt, BlockEncrypt, KeyInit};
4use serde::{Deserialize, Serialize};
5use zeroize::Zeroizing;
6
7const MAGIC: &[u8; 6] = b"\x07\x08V2\x08\x07";
8pub const MAX_DAT_PLAINTEXT_BYTES: usize = 64 * 1024 * 1024;
9#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
10#[serde(rename_all = "camelCase", deny_unknown_fields)]
11pub struct DatLayout {
12 pub aes_plain_bytes: u32,
13 pub xor_bytes: u32,
14 pub control_byte: u8,
15}
16impl DatLayout {
17 pub fn observed(plain_len: usize) -> Result<Self> {
18 if !(1024..=MAX_DAT_PLAINTEXT_BYTES).contains(&plain_len) {
19 return Err(ProfileError::InvalidDat);
20 }
21 Ok(Self {
22 aes_plain_bytes: 1024,
23 xor_bytes: (plain_len - 1024).min(1024 * 1024) as u32,
24 control_byte: 1,
25 })
26 }
27 fn cipher_bytes(self) -> Result<usize> {
28 if self.aes_plain_bytes == 0
29 || self.aes_plain_bytes as usize > MAX_DAT_PLAINTEXT_BYTES
30 || self.xor_bytes as usize > MAX_DAT_PLAINTEXT_BYTES
31 || self.control_byte != 1
32 {
33 return Err(ProfileError::InvalidDat);
34 }
35 Ok((self.aes_plain_bytes as usize / 16 + 1) * 16)
36 }
37}
38pub fn parse_dat_v2(data: &[u8]) -> Result<DatLayout> {
39 if data.len() < 15
40 || data.len() > MAX_DAT_PLAINTEXT_BYTES + 31
41 || data.get(..6) != Some(MAGIC.as_slice())
42 {
43 return Err(ProfileError::InvalidDat);
44 }
45 let mut h = [0u8; 4];
46 h.copy_from_slice(&data[6..10]);
47 let mut t = [0u8; 4];
48 t.copy_from_slice(&data[10..14]);
49 let layout = DatLayout {
50 aes_plain_bytes: u32::from_le_bytes(h),
51 xor_bytes: u32::from_le_bytes(t),
52 control_byte: data[14],
53 };
54 let cipher = layout.cipher_bytes()?;
55 if data.len() < 15 + cipher + layout.xor_bytes as usize {
56 return Err(ProfileError::InvalidDat);
57 }
58 Ok(layout)
59}
60pub fn decode_dat_v2(data: &[u8], key: &ImageDatKey) -> Result<(Vec<u8>, DatLayout)> {
63 let layout = parse_dat_v2(data)?;
64 let end = 15 + layout.cipher_bytes()?;
65 let cipher = Aes128::new_from_slice(key.aes.expose()).map_err(|_| ProfileError::InvalidDat)?;
66 let mut head = Zeroizing::new(data[15..end].to_vec());
67 for b in head.chunks_exact_mut(16) {
68 cipher.decrypt_block(GenericArray::from_mut_slice(b));
69 }
70 let pad = usize::from(*head.last().ok_or(ProfileError::InvalidDat)?);
71 if !(1..=16).contains(&pad)
72 || pad > head.len()
73 || head[head.len() - pad..]
74 .iter()
75 .any(|b| usize::from(*b) != pad)
76 {
77 return Err(ProfileError::InvalidDat);
78 }
79 let unpadded = head.len() - pad;
80 head.truncate(unpadded);
81 if head.len() != layout.aes_plain_bytes as usize {
82 return Err(ProfileError::InvalidDat);
83 }
84 let tail = data.len() - layout.xor_bytes as usize;
85 let output_len = head.len() + tail - end + layout.xor_bytes as usize;
86 if output_len > MAX_DAT_PLAINTEXT_BYTES {
87 return Err(ProfileError::ResourceLimit);
88 }
89 let mut output = Vec::with_capacity(output_len);
90 output.extend_from_slice(&head);
91 output.extend_from_slice(&data[end..tail]);
92 output.extend(data[tail..].iter().map(|b| b ^ key.xor));
93 Ok((output, layout))
94}
95pub fn encode_dat_v2(
96 plaintext: &[u8],
97 key: &ImageDatKey,
98 layout: Option<DatLayout>,
99) -> Result<Vec<u8>> {
100 if plaintext.len() > MAX_DAT_PLAINTEXT_BYTES {
101 return Err(ProfileError::ResourceLimit);
102 }
103 let layout = match layout {
104 Some(l) => l,
105 None => DatLayout::observed(plaintext.len())?,
106 };
107 let cipher_bytes = layout.cipher_bytes()?;
108 let h = layout.aes_plain_bytes as usize;
109 let t = layout.xor_bytes as usize;
110 if h.checked_add(t).is_none_or(|v| v > plaintext.len()) {
111 return Err(ProfileError::InvalidDat);
112 }
113 let cipher = Aes128::new_from_slice(key.aes.expose()).map_err(|_| ProfileError::InvalidDat)?;
114 let mut head = Zeroizing::new(plaintext[..h].to_vec());
115 head.resize(cipher_bytes, (cipher_bytes - h) as u8);
116 for b in head.chunks_exact_mut(16) {
117 cipher.encrypt_block(GenericArray::from_mut_slice(b));
118 }
119 let mut output = Vec::with_capacity(15 + cipher_bytes + plaintext.len() - h);
120 output.extend_from_slice(MAGIC);
121 output.extend_from_slice(&layout.aes_plain_bytes.to_le_bytes());
122 output.extend_from_slice(&layout.xor_bytes.to_le_bytes());
123 output.push(layout.control_byte);
124 output.extend_from_slice(&head);
125 output.extend_from_slice(&plaintext[h..plaintext.len() - t]);
126 output.extend(plaintext[plaintext.len() - t..].iter().map(|b| b ^ key.xor));
127 Ok(output)
128}