1use self::entry::{EntryId, PkgEntry};
44use self::header::PkgHeader;
45use self::keys::{fake_pfs_key, pkg_key3};
46use aes::cipher::generic_array::GenericArray;
47use aes::cipher::{BlockDecryptMut, KeyIvInit};
48use sha2::Digest;
49use snafu::{ResultExt, Snafu};
50use std::io::Read;
51
52use open_error::*;
53
54pub mod entry;
55pub mod header;
56pub mod keys;
57
58#[must_use]
65pub struct Pkg<R: AsRef<[u8]>> {
66 raw: R,
67 header: PkgHeader,
68 entry_key3: Vec<u8>,
69 ekpfs: Vec<u8>,
70}
71
72impl<R: AsRef<[u8]>> std::fmt::Debug for Pkg<R> {
73 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
74 f.debug_struct("Pkg")
75 .field("header", &self.header)
76 .field("entry_count", &self.entry_count())
77 .finish_non_exhaustive()
78 }
79}
80
81impl<R: AsRef<[u8]>> Pkg<R> {
82 pub fn new(raw: R) -> Result<Self, OpenError> {
99 let header = PkgHeader::read(raw.as_ref()).context(ReadHeaderFailedSnafu)?;
100
101 let mut pkg = Self {
102 raw,
103 header,
104 entry_key3: Vec::new(),
105 ekpfs: Vec::new(),
106 };
107 pkg.load_entry_key3()?;
108 pkg.load_ekpfs()?;
109 Ok(pkg)
110 }
111
112 pub fn header(&self) -> &PkgHeader {
114 &self.header
115 }
116
117 #[must_use]
119 pub fn entry_count(&self) -> usize {
120 self.header.entry_count()
121 }
122
123 pub fn entries(&self) -> PkgEntries<'_> {
144 PkgEntries {
145 raw: self.raw.as_ref(),
146 table_offset: self.header.table_offset(),
147 current: 0,
148 total: self.header.entry_count(),
149 }
150 }
151
152 pub fn entry_data(&self, entry: &PkgEntry) -> Result<Vec<u8>, EntryDataError> {
180 if entry.is_encrypted() && (entry.key_index() != 3 || self.entry_key3.is_empty()) {
182 return Err(EntryDataError::NoDecryptionKey {
183 key_index: entry.key_index(),
184 });
185 }
186
187 let offset = entry.data_offset();
189 let padded_size = if entry.is_encrypted() {
190 (entry.data_size() + 15) & !15 } else {
192 entry.data_size()
193 };
194
195 let raw_data = self
196 .raw
197 .as_ref()
198 .get(offset..(offset + padded_size))
199 .ok_or(EntryDataError::InvalidDataOffset)?;
200
201 if entry.is_encrypted() {
203 if raw_data.len() % 16 != 0 {
204 return Err(EntryDataError::MisalignedData {
205 size: raw_data.len(),
206 });
207 }
208
209 let mut decrypted = self.decrypt_entry_data(entry, raw_data);
210 decrypted.truncate(entry.data_size());
212 Ok(decrypted)
213 } else {
214 Ok(raw_data.to_vec())
215 }
216 }
217
218 #[must_use]
222 pub fn get_pfs_image(&self) -> Option<PfsImage<'_>> {
223 let offset = self.header.pfs_offset();
224 let size = self.header.pfs_size();
225 let data = self.raw.as_ref().get(offset..(offset + size))?;
226 Some(PfsImage {
227 data,
228 ekpfs: &self.ekpfs,
229 })
230 }
231
232 pub fn find_entry(&self, id: EntryId) -> Result<(PkgEntry, usize), FindEntryError> {
236 self.find_entry_raw(id.as_u32())
237 }
238
239 pub fn find_entry_raw(&self, id: u32) -> Result<(PkgEntry, usize), FindEntryError> {
243 for num in 0..self.header.entry_count() {
244 let offset = self.header.table_offset() + num * PkgEntry::RAW_SIZE;
245 let raw = self
246 .raw
247 .as_ref()
248 .get(offset..(offset + PkgEntry::RAW_SIZE))
249 .ok_or(FindEntryError::InvalidOffset { num })?;
250
251 let entry =
252 PkgEntry::read(raw).map_err(|source| FindEntryError::ReadFailed { source })?;
253
254 if entry.id() == id {
255 return Ok((entry, num));
256 }
257 }
258
259 Err(FindEntryError::NotFound)
260 }
261
262 fn load_ekpfs(&mut self) -> Result<(), OpenError> {
263 let (entry, _) = match self.find_entry(EntryId::PfsImageKey) {
265 Ok(v) => v,
266 Err(e) => match e {
267 FindEntryError::NotFound => return Err(OpenError::PfsImageKeyNotFound),
268 _ => return Err(OpenError::FindPfsImageKeyFailed { source: e }),
269 },
270 };
271
272 let data = self
274 .entry_data(&entry)
275 .context(open_error::GetPfsImageKeyFailedSnafu)?;
276
277 let fake_key = fake_pfs_key();
279 self.ekpfs = fake_key
280 .decrypt(rsa::Pkcs1v15Encrypt, &data)
281 .context(DecryptEkpfsFailedSnafu)?;
282
283 Ok(())
284 }
285
286 fn decrypt_entry_data(&self, entry: &PkgEntry, mut encrypted: &[u8]) -> Vec<u8> {
287 debug_assert_eq!(encrypted.len() % 16, 0);
288
289 let (key, iv) = self.derive_entry_key3(entry);
291 let mut decryptor = cbc::Decryptor::<aes::Aes128>::new(&key.into(), &iv.into());
292
293 let mut out = Vec::with_capacity(encrypted.len());
295
296 while !encrypted.is_empty() {
297 let mut block = [0u8; 16];
298 encrypted.read_exact(&mut block).unwrap();
299 decryptor.decrypt_block_mut(GenericArray::from_mut_slice(&mut block));
300 out.extend_from_slice(&block);
301 }
302
303 out
304 }
305
306 fn derive_entry_key3(&self, entry: &PkgEntry) -> ([u8; 16], [u8; 16]) {
308 let mut sha256 = sha2::Sha256::new();
310 sha256.update(entry.as_bytes());
311 sha256.update(&self.entry_key3);
312 let secret = sha256.finalize();
313
314 let (iv, key) = secret.split_at(16);
316 (key.try_into().unwrap(), iv.try_into().unwrap())
317 }
318
319 fn load_entry_key3(&mut self) -> Result<(), OpenError> {
320 let (entry, index) = match self.find_entry(EntryId::EntryKeys) {
322 Ok(v) => v,
323 Err(e) => match e {
324 FindEntryError::NotFound => return Err(OpenError::EntryKeyNotFound),
325 _ => return Err(OpenError::FindEntryKeyFailed { source: e }),
326 },
327 };
328
329 let offset = entry.data_offset();
331 let size = entry.data_size();
332 let mut data = self
333 .raw
334 .as_ref()
335 .get(offset..(offset + size))
336 .ok_or(OpenError::InvalidEntryOffset { num: index })?;
337
338 let mut seed = [0u8; 32];
340 if data.read_exact(&mut seed).is_err() {
341 return Err(OpenError::InvalidEntryOffset { num: index });
342 };
343
344 let mut digests: [[u8; 32]; 7] = [[0u8; 32]; 7];
346 digests
347 .iter_mut()
348 .try_for_each(|digest| data.read_exact(digest))
349 .map_err(|_| OpenError::InvalidEntryOffset { num: index })?;
350
351 let mut keys: [[u8; 256]; 7] = [[0u8; 256]; 7];
353 keys.iter_mut()
354 .try_for_each(|key| data.read_exact(key))
355 .map_err(|_| OpenError::InvalidEntryOffset { num: index })?;
356
357 let key3 = pkg_key3();
359 self.entry_key3 = key3
360 .decrypt(rsa::Pkcs1v15Encrypt, &keys[3])
361 .context(DecryptEntryKeyFailedSnafu { key_index: 3usize })?;
362
363 Ok(())
364 }
365}
366
367#[derive(Debug)]
369pub struct PfsImage<'a> {
370 pub data: &'a [u8],
372 pub ekpfs: &'a [u8],
374}
375
376#[must_use = "iterators are lazy and do nothing unless consumed"]
378pub struct PkgEntries<'a> {
379 raw: &'a [u8],
380 table_offset: usize,
381 current: usize,
382 total: usize,
383}
384
385impl std::fmt::Debug for PkgEntries<'_> {
386 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
387 f.debug_struct("PkgEntries")
388 .field("current", &self.current)
389 .field("total", &self.total)
390 .finish_non_exhaustive()
391 }
392}
393
394impl Iterator for PkgEntries<'_> {
395 type Item = Result<(usize, PkgEntry), EntryReadError>;
396
397 fn next(&mut self) -> Option<Self::Item> {
398 if self.current >= self.total {
399 return None;
400 }
401
402 let num = self.current;
403 self.current += 1;
404
405 let offset = self.table_offset + num * PkgEntry::RAW_SIZE;
406 let raw = match self.raw.get(offset..(offset + PkgEntry::RAW_SIZE)) {
407 Some(v) => v,
408 None => return Some(Err(EntryReadError::InvalidOffset { num })),
409 };
410
411 Some(
412 PkgEntry::read(raw)
413 .map_err(|source| EntryReadError::ReadFailed { source })
414 .map(|entry| (num, entry)),
415 )
416 }
417
418 fn size_hint(&self) -> (usize, Option<usize>) {
419 let remaining = self.total - self.current;
420 (remaining, Some(remaining))
421 }
422}
423
424impl ExactSizeIterator for PkgEntries<'_> {}
425
426#[derive(Debug, Snafu)]
427#[snafu(module)]
428#[non_exhaustive]
429pub enum OpenError {
430 #[snafu(display("invalid PKG header"))]
431 ReadHeaderFailed { source: header::ReadError },
432
433 #[snafu(display("no PKG entry key available"))]
434 EntryKeyNotFound,
435
436 #[snafu(display("failed to find entry key"))]
437 FindEntryKeyFailed { source: FindEntryError },
438
439 #[snafu(display("entry #{num} has invalid data offset"))]
440 InvalidEntryOffset { num: usize },
441
442 #[snafu(display("cannot decrypt entry key #{key_index}"))]
443 DecryptEntryKeyFailed {
444 key_index: usize,
445 source: rsa::errors::Error,
446 },
447
448 #[snafu(display("no PFS image key in the PKG"))]
449 PfsImageKeyNotFound,
450
451 #[snafu(display("failed to get PFS image key"))]
452 GetPfsImageKeyFailed { source: EntryDataError },
453
454 #[snafu(display("failed to find PFS image key"))]
455 FindPfsImageKeyFailed { source: FindEntryError },
456
457 #[snafu(display("cannot decrypt EKPFS"))]
458 DecryptEkpfsFailed { source: rsa::errors::Error },
459}
460
461#[derive(Debug, Snafu)]
462#[snafu(module)]
463#[non_exhaustive]
464pub enum FindEntryError {
465 #[snafu(display("failed to read entry"))]
466 ReadFailed { source: entry::EntryError },
467
468 #[snafu(display("entry #{num} has invalid offset"))]
469 InvalidOffset { num: usize },
470
471 #[snafu(display("the specified entry was not found"))]
472 NotFound,
473}
474
475#[derive(Debug, Snafu)]
476#[snafu(module)]
477#[non_exhaustive]
478pub enum EntryReadError {
479 #[snafu(display("entry #{num} has invalid offset"))]
480 InvalidOffset { num: usize },
481
482 #[snafu(display("failed to read entry"))]
483 ReadFailed { source: entry::EntryError },
484}
485
486#[derive(Debug, Snafu)]
487#[snafu(module)]
488#[non_exhaustive]
489pub enum EntryDataError {
490 #[snafu(display("no decryption key available for key index {key_index}"))]
491 NoDecryptionKey { key_index: usize },
492
493 #[snafu(display("entry has invalid data offset"))]
494 InvalidDataOffset,
495
496 #[snafu(display(
497 "encrypted entry data is not block-aligned (size {size} is not a multiple of 16)"
498 ))]
499 MisalignedData { size: usize },
500}