1use std::iter::IntoIterator;
4use std::slice::Iter;
5
6use crate::{error::ValidateError, Cache};
7use crc::{Crc, CRC_32_ISO_HDLC};
8use nom::{combinator::cond, number::complete::be_u32};
9use runefs::{
10 codec::{Buffer, Encoded},
11 REFERENCE_TABLE_ID,
12};
13
14#[cfg(feature = "rs3")]
15use num_bigint::{BigInt, Sign};
16#[cfg(feature = "serde")]
17use serde::{Deserialize, Serialize};
18#[cfg(feature = "rs3")]
19use whirlpool::{Digest, Whirlpool};
20
21const CRC: Crc<u32> = Crc::<u32>::new(&CRC_32_ISO_HDLC);
22
23#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
25#[cfg_attr(not(feature = "rs3"), derive(Default))]
26#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
27pub struct Entry {
28 pub(crate) crc: u32,
29 pub(crate) version: u32,
30 #[cfg(feature = "rs3")]
31 pub(crate) hash: Vec<u8>,
32}
33
34#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
41#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
42pub struct Checksum {
43 index_count: usize,
44 entries: Vec<Entry>,
45}
46
47impl Checksum {
48 pub fn new(cache: &Cache) -> crate::Result<Self> {
54 Ok(Self {
55 index_count: cache.indices.count(),
56 entries: Self::entries(cache)?,
57 })
58 }
59
60 fn entries(cache: &Cache) -> crate::Result<Vec<Entry>> {
61 let entries: Vec<Entry> = (0..cache.indices.count())
62 .into_iter()
63 .filter_map(|idx_id| cache.read(REFERENCE_TABLE_ID, idx_id as u32).ok())
64 .enumerate()
65 .map(|(idx_id, buffer)| -> crate::Result<Entry> {
66 if buffer.is_empty() || idx_id == 47 {
67 Ok(Entry::default())
68 } else {
69 let mut digest = CRC.digest();
76 digest.update(&buffer);
77
78 #[cfg(feature = "rs3")]
79 let hash = {
80 let mut hasher = Whirlpool::new();
81 hasher.update(&buffer);
82 hasher.finalize().as_slice().to_vec()
83 };
84
85 let data = buffer.decode()?;
86 let (_, version) = cond(data[0] >= 6, be_u32)(&data[1..5])?;
87 let version = version.unwrap_or(0);
88
89 Ok(Entry {
90 crc: digest.finalize(),
91 version,
92 #[cfg(feature = "rs3")]
93 hash,
94 })
95 }
96 })
97 .filter_map(crate::Result::ok)
98 .collect();
99
100 Ok(entries)
101 }
102
103 pub fn encode(self) -> crate::Result<Buffer<Encoded>> {
116 let mut buffer = Vec::with_capacity(self.entries.len() * 8);
117
118 for entry in self.entries {
119 buffer.extend(&u32::to_be_bytes(entry.crc));
120 buffer.extend(&u32::to_be_bytes(entry.version));
121 }
122
123 Ok(Buffer::from(buffer).encode()?)
146 }
147
148 pub fn validate<'b, I>(&self, crcs: I) -> Result<(), ValidateError>
155 where
156 I: IntoIterator<Item = &'b u32>,
157 <I as IntoIterator>::IntoIter: ExactSizeIterator,
158 {
159 let crcs = crcs.into_iter();
160
161 if self.entries.len() != crcs.len() {
162 return Err(ValidateError::InvalidLength {
163 expected: self.entries.len(),
164 actual: crcs.len(),
165 });
166 }
167 for (index, (internal, external)) in self
168 .entries
169 .iter()
170 .map(|entry| &entry.crc)
171 .zip(crcs)
172 .enumerate()
173 {
174 if internal != external {
175 return Err(ValidateError::InvalidCrc {
176 idx: index,
177 internal: *internal,
178 external: *external,
179 });
180 }
181 }
182
183 Ok(())
184 }
185
186 #[allow(missing_docs)]
187 #[inline]
188 pub const fn index_count(&self) -> usize {
189 self.index_count
190 }
191
192 #[allow(missing_docs)]
193 #[inline]
194 pub fn iter(&self) -> Iter<'_, Entry> {
195 self.entries.iter()
196 }
197}
198
199#[cfg(feature = "rs3")]
201#[cfg_attr(docsrs, doc(cfg(feature = "rs3")))]
202#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
203#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
204pub struct RsaKeys<'a> {
205 pub(crate) exponent: &'a [u8],
206 pub(crate) modulus: &'a [u8],
207}
208
209#[cfg(feature = "rs3")]
210#[cfg_attr(docsrs, doc(cfg(feature = "rs3")))]
211impl<'a> RsaKeys<'a> {
212 pub const fn new(exponent: &'a [u8], modulus: &'a [u8]) -> Self {
214 Self { exponent, modulus }
215 }
216
217 pub fn encrypt(&self, hash: &[u8]) -> Vec<u8> {
220 let exp = BigInt::parse_bytes(self.exponent, 10).unwrap_or_default();
221 let mud = BigInt::parse_bytes(self.modulus, 10).unwrap_or_default();
222 BigInt::from_bytes_be(Sign::Plus, hash)
223 .modpow(&exp, &mud)
224 .to_bytes_be()
225 .1
226 }
227}
228
229#[cfg(feature = "rs3")]
251#[cfg_attr(docsrs, doc(cfg(feature = "rs3")))]
252#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
253#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
254pub struct RsaChecksum<'a> {
255 checksum: Checksum,
256 #[cfg_attr(feature = "serde", serde(borrow))]
257 rsa_keys: RsaKeys<'a>,
258}
259
260#[cfg(feature = "rs3")]
261impl<'a> RsaChecksum<'a> {
262 pub fn with_keys(cache: &Cache, rsa_keys: RsaKeys<'a>) -> crate::Result<Self> {
264 Ok(Self {
265 checksum: Checksum::new(cache)?,
266 rsa_keys,
267 })
268 }
269
270 pub fn encode(self) -> crate::Result<Buffer<Encoded>> {
272 let index_count = self.checksum.index_count - 1;
273 let mut buffer = vec![0; 81 * index_count];
274
275 buffer[0] = index_count as u8;
276 for (index, entry) in self.checksum.entries.iter().enumerate() {
277 let offset = index * 80;
278 buffer[offset + 1..=offset + 4].copy_from_slice(&u32::to_be_bytes(entry.crc));
279 buffer[offset + 5..=offset + 8].copy_from_slice(&u32::to_be_bytes(entry.version));
280 buffer[offset + 9..=offset + 12].copy_from_slice(&u32::to_be_bytes(0));
281 buffer[offset + 13..=offset + 16].copy_from_slice(&u32::to_be_bytes(0));
282 buffer[offset + 17..=offset + 80].copy_from_slice(&entry.hash);
283 }
284
285 let mut hasher = Whirlpool::new();
286 hasher.update(&buffer);
287 let mut hash = hasher.finalize().as_slice().to_vec();
288 hash.insert(0, 0);
289
290 buffer.extend(self.rsa_keys.encrypt(&hash));
291
292 Ok(Buffer::from(buffer))
293 }
294}
295
296#[cfg(feature = "rs3")]
297impl<'a> From<(&'a [u8], &'a [u8])> for RsaKeys<'a> {
298 fn from(keys: (&'a [u8], &'a [u8])) -> Self {
299 RsaKeys::new(keys.0, keys.1)
300 }
301}
302
303impl IntoIterator for Checksum {
304 type Item = Entry;
305 type IntoIter = std::vec::IntoIter<Entry>;
306
307 #[inline]
308 fn into_iter(self) -> Self::IntoIter {
309 self.entries.into_iter()
310 }
311}
312
313impl<'a> IntoIterator for &'a Checksum {
314 type Item = &'a Entry;
315 type IntoIter = Iter<'a, Entry>;
316
317 #[inline]
318 fn into_iter(self) -> Self::IntoIter {
319 self.entries.iter()
320 }
321}
322
323#[cfg(feature = "rs3")]
324impl<'a> IntoIterator for RsaChecksum<'a> {
325 type Item = Entry;
326 type IntoIter = std::vec::IntoIter<Entry>;
327
328 #[inline]
329 fn into_iter(self) -> Self::IntoIter {
330 self.checksum.entries.into_iter()
331 }
332}
333
334#[cfg(feature = "rs3")]
335impl<'a> IntoIterator for &'a RsaChecksum<'a> {
336 type Item = &'a Entry;
337 type IntoIter = Iter<'a, Entry>;
338
339 #[inline]
340 fn into_iter(self) -> Self::IntoIter {
341 self.checksum.entries.iter()
342 }
343}
344
345#[cfg(feature = "rs3")]
346impl Default for Entry {
347 #[inline]
348 fn default() -> Self {
349 Self {
350 crc: 0,
351 version: 0,
352 hash: vec![0; 64],
353 }
354 }
355}
356
357#[cfg(test)]
358use crate::test_util;
359
360#[test]
361fn new() -> crate::Result<()> {
362 let cache = test_util::osrs_cache()?;
363
364 assert!(Checksum::new(&cache).is_ok());
365 assert!(cache.checksum().is_ok());
366
367 Ok(())
368}
369
370#[test]
371fn encode() -> crate::Result<()> {
372 let cache = test_util::osrs_cache()?;
373 let buffer = Checksum::new(&cache)?.encode()?;
374
375 let hash = test_util::hash(&buffer);
376 assert_eq!(&hash, "b5364b06747801f28bb684e2db5be80ae78e1c97");
377 assert_eq!(buffer.len(), 173);
378
379 Ok(())
380}
381
382#[test]
383fn invalid_len() -> crate::Result<()> {
384 use crate::error::ValidateError;
385
386 let cache = test_util::osrs_cache()?;
387 let checksum = Checksum::new(&cache)?;
388
389 let crcs = [
390 1593884597, 1029608590, 16840364, 4209099954, 3716821437, 165713182, 686540367, 4262755489,
391 2208636505, 3047082366, 586413816, 2890424900, 3411535427, 3178880569, 153718440,
392 3849392898, 3628627685, 2813112885, 1461700456, 2751169400,
393 ];
394
395 assert_eq!(
396 checksum.validate(&crcs),
397 Err(ValidateError::InvalidLength {
398 expected: 21,
399 actual: 20
400 })
401 );
402
403 Ok(())
404}
405
406#[cfg(all(test, feature = "rs3"))]
407mod rsa {
408 use super::{RsaChecksum, RsaKeys};
409 use crate::test_util;
410 pub const EXPONENT: &'static [u8] = b"5206580307236375668350588432916871591810765290737810323990754121164270399789630501436083337726278206128394461017374810549461689174118305784406140446740993";
411 pub const MODULUS: &'static [u8] = b"6950273013450460376345707589939362735767433035117300645755821424559380572176824658371246045200577956729474374073582306250298535718024104420271215590565201";
412
413 #[test]
414 fn with_keys() -> crate::Result<()> {
415 let cache = test_util::rs3_cache()?;
416 let keys = RsaKeys::new(EXPONENT, MODULUS);
417 let buffer = RsaChecksum::with_keys(&cache, keys)?.encode()?;
418
419 let hash = test_util::hash(&buffer);
420 assert_eq!(&hash, "118e0146af6cf288630357eec6298c34a2430065");
421 assert_eq!(buffer.len(), 4681);
422
423 Ok(())
424 }
425}