From 2d2206bfe287897bcb65923b290799527ca9b3e3 Mon Sep 17 00:00:00 2001 From: roblabla Date: Sun, 27 Jan 2019 00:37:49 +0000 Subject: [PATCH] It works, kinda. --- src/bin/linkle_clap.rs | 86 +++++++++- src/format/nca.rs | 294 +++++++++++++++++++++++++++-------- src/format/nca/structures.rs | 7 +- src/pki.rs | 37 +++-- src/utils.rs | 18 ++- 5 files changed, 358 insertions(+), 84 deletions(-) diff --git a/src/bin/linkle_clap.rs b/src/bin/linkle_clap.rs index 41bec97..86e4079 100644 --- a/src/bin/linkle_clap.rs +++ b/src/bin/linkle_clap.rs @@ -5,6 +5,7 @@ extern crate linkle; use std::fs::{OpenOptions, File}; use std::path::{Path, PathBuf}; use std::process; +use std::io::Read; use structopt::StructOpt; use linkle::error::ResultExt; @@ -97,6 +98,42 @@ enum Opt { #[structopt(short = "d", long = "dev")] dev: bool, + /// Keyfile + #[structopt(parse(from_os_str), short = "k", long = "keyset")] + keyfile: Option, + }, + + /// Create an NCA file. + #[structopt(name = "nca")] + Nca { + /// The input JSON NCA header to create this NCA from. + #[structopt(parse(from_os_str), long = "header-json")] + header_file: PathBuf, + + /// The output NCA location + #[structopt(parse(from_os_str))] + output_file: PathBuf, + + /// Sets the output file to extract the section0 to. + #[structopt(parse(from_os_str), long = "section0")] + section0_file: Option, + + /// Sets the output file to extract the section1 to. + #[structopt(parse(from_os_str), long = "section1")] + section1_file: Option, + + /// Sets the output file to extract the section2 to. + #[structopt(parse(from_os_str), long = "section2")] + section2_file: Option, + + /// Sets the output file to extract the section3 to. + #[structopt(parse(from_os_str), long = "section3")] + section3_file: Option, + + /// Use development keys instead of retail + #[structopt(short = "d", long = "dev")] + dev: bool, + /// Keyfile #[structopt(parse(from_os_str), short = "k", long = "keyset")] keyfile: Option, @@ -172,8 +209,7 @@ fn extract_nca(input_file: &Path, is_dev: bool, key_path: Option<&Path>, } else { linkle::pki::Keys::new_retail(key_path).unwrap() }; - println!("{:#?}", keys); - /*let nca = linkle::format::nca::Nca::from_file(&keys, File::open(input_file)?).unwrap(); + let nca = linkle::format::nca::Nca::from_file(&keys, File::open(input_file)?).unwrap(); if let Some(output_header_json) = output_header_json { let mut output_header_json = File::create(output_header_json)?; nca.write_json(&mut output_header_json).unwrap(); @@ -197,7 +233,42 @@ fn extract_nca(input_file: &Path, is_dev: bool, key_path: Option<&Path>, let mut output_section3 = File::create(output_section3)?; let mut section = nca.section(3).unwrap(); std::io::copy(&mut section, &mut output_section3)?; - }*/ + } + Ok(()) +} + +fn create_nca(is_dev: bool, key_path: Option<&Path>, header_file: &Path, + output_nca: &Path, + section0_path: Option<&Path>, section1_path: Option<&Path>, + section2_path: Option<&Path>, section3_path: Option<&Path>) -> Result<(), linkle::error::Error> { + let keys = if is_dev { + linkle::pki::Keys::new_dev(key_path).unwrap() + } else { + linkle::pki::Keys::new_retail(key_path).unwrap() + }; + let nca: linkle::format::nca::NcaJson = serde_json::from_reader(File::open(header_file)?).unwrap(); + let mut output_nca = linkle::format::nca::Nca::nca_writer(nca, File::create(output_nca)?, &keys)?; + if let Some(section_path) = section0_path { + let mut section = output_nca.section(0)?; + std::io::copy(&mut File::open(section_path)?, &mut section)?; + section.finalize()?; + } + if let Some(section_path) = section1_path { + let mut section = output_nca.section(1)?; + std::io::copy(&mut File::open(section_path)?, &mut section)?; + section.finalize()?; + } + if let Some(section_path) = section2_path { + let mut section = output_nca.section(2)?; + std::io::copy(&mut File::open(section_path)?, &mut section)?; + section.finalize()?; + } + if let Some(section_path) = section3_path { + let mut section = output_nca.section(3)?; + std::io::copy(&mut File::open(section_path)?, &mut section)?; + section.finalize()?; + } + output_nca.finalize()?; Ok(()) } @@ -211,10 +282,17 @@ fn process_args(app: &Opt) { Opt::NcaExtract { ref input_file, ref header_file, ref section0_file, ref section1_file, ref section2_file, ref section3_file, dev, ref keyfile } => extract_nca(input_file, *dev, to_opt_ref(keyfile), to_opt_ref(header_file), to_opt_ref(section0_file), to_opt_ref(section1_file), to_opt_ref(section2_file), to_opt_ref(section3_file)), + Opt::Nca { ref header_file, ref output_file, ref section0_file, + ref section1_file, ref section2_file, + ref section3_file, dev, ref keyfile } => create_nca(*dev, to_opt_ref(keyfile), header_file, output_file, to_opt_ref(section0_file), to_opt_ref(section1_file), to_opt_ref(section2_file), to_opt_ref(section3_file)), }; if let Err(e) = res { - println!("Error: {}", e); + if let Ok(_) = std::env::var("RUST_BACKTRACE") { + println!("Error: {:?}", e); + } else { + println!("Error: {}", e); + } process::exit(1) } } diff --git a/src/format/nca.rs b/src/format/nca.rs index 558db38..1772e4c 100644 --- a/src/format/nca.rs +++ b/src/format/nca.rs @@ -31,7 +31,8 @@ use std::cmp::{min, max}; use plain::Plain; use serde_derive::{Deserialize, Serialize}; use byteorder::{BE, ByteOrder}; -use crate::utils::{align_down, TryClone, ReadRange}; +use crate::utils::{align_down, align_up, TryClone, ReadRange}; +use sha2::{Sha256, Digest}; #[derive(Debug, Serialize, Deserialize, Clone, Copy)] #[repr(u8)] @@ -51,8 +52,20 @@ impl From for KeyType { } #[derive(Debug, Serialize, Deserialize, Clone, Copy)] +#[repr(u8)] enum CryptoType { - None, Xts, Ctr, Bktr + None = 0, Xts = 1, Ctr = 2, Bktr = 3 +} + +impl From for RawCryptType { + fn from(from: CryptoType) -> RawCryptType { + match from { + CryptoType::None => RawCryptType::None, + CryptoType::Xts => RawCryptType::Xts, + CryptoType::Ctr => RawCryptType::Ctr, + CryptoType::Bktr => RawCryptType::Bktr, + } + } } impl From for CryptoType { @@ -114,6 +127,8 @@ enum NcaFormat { pub struct SectionJson { media_start_offset: u32, media_end_offset: u32, + unknown1: u32, + unknown2: u32, crypto: CryptoType, fstype: FsType, nounce: u64, @@ -138,6 +153,7 @@ pub struct NcaJson { content_type: ContentType, key_revision: u8, key_type: KeyType, + nca_size: u64, title_id: TitleId, sdk_version: u32, // TODO: Better format xts_key: AesXtsKey, @@ -178,18 +194,21 @@ fn decrypt_header(pki: &Keys, file: &mut Read) -> Result { // TODO: Check if NCA is already decrypted let header_key = pki.header_key().as_ref().ok_or(Error::MissingKey("header_key", Backtrace::new()))?; - header_key.decrypt(&header[..0x400], &mut decrypted_header[..0x400], 0, 0x200)?; + decrypted_header[..0x400].copy_from_slice(&header[..0x400]); + header_key.decrypt(&mut decrypted_header[..0x400], 0, 0x200)?; let raw_nca = *RawNca::from_bytes(&decrypted_header).expect("RawNca to be of the right size"); match &raw_nca.magic { b"NCA3" => { - header_key.decrypt(&header, &mut decrypted_header, 0, 0x200)?; + decrypted_header.copy_from_slice(&header); + header_key.decrypt(&mut decrypted_header, 0, 0x200)?; }, b"NCA2" => { for (i, fsheader) in raw_nca.fs_headers.iter().enumerate() { let offset = 0x400 + i * 0x200; - if fsheader._0x148[0] != 0 || fsheader._0x148[..0xB7] != fsheader._0x148[1..] { - header_key.decrypt(&header[offset..offset + 0x200], &mut decrypted_header[offset..offset + 0x200], 0, 0x200)?; + if &fsheader._0x148[..] != &[0; 0xB8][..] { + decrypted_header[offset..offset + 0x200].copy_from_slice(&header[offset..offset + 0x200]); + header_key.decrypt(&mut decrypted_header[offset..offset + 0x200], 0, 0x200)?; } else { decrypted_header[offset..offset + 0x200].copy_from_slice(&[0; 0x200]); } @@ -201,6 +220,24 @@ fn decrypt_header(pki: &Keys, file: &mut Read) -> Result { Ok(*RawNca::from_bytes(&decrypted_header).expect("RawNca to be of the right size")) } +fn encrypt_header<'a>(pki: &Keys, header: &'a mut RawNca) -> Result<&'a [u8], Error> { + let header_key = pki.header_key().as_ref().ok_or(Error::MissingKey("header_key", Backtrace::new()))?; + + let header_bytes = match &header.magic { + b"NCA3" => { + let mut header_bytes = unsafe { + // Safety: RawNca has no padding + plain::as_mut_bytes(header) + }; + header_key.encrypt(&mut header_bytes, 0, 0x200)?; + header_bytes + }, + _ => unimplemented!() + }; + + Ok(header_bytes) +} + impl Nca { pub fn from_file(pki: &Keys, mut file: R) -> Result, Error> { let header = decrypt_header(pki, &mut file)?; @@ -261,6 +298,8 @@ impl Nca { nounce: fs.section_ctr, media_start_offset: section.media_start_offset, media_end_offset: section.media_end_offset, + unknown1: section.unknown1, + unknown2: section.unknown2, }); } } @@ -277,6 +316,7 @@ impl Nca { content_type: ContentType::from(header.content_type), key_revision: master_key_revision, key_type: KeyType::from(header.key_index), + nca_size: header.nca_size, title_id: TitleId(header.title_id), // TODO: Store the SDK version in a more human readable format. sdk_version: header.sdk_version, @@ -315,14 +355,15 @@ impl Nca { } } - pub fn section(&self, id: usize) -> Result>>, Error> { + pub fn section(&self, id: usize) -> Result>>, Error> { let mut raw_section = self.raw_section(id)?; let (start_offset, size) = match raw_section.state.json.fstype { FsType::Pfs0 { pfs0_offset, pfs0_size, .. } => (pfs0_offset, pfs0_size), _ => (0, raw_section.state.json.size()) }; raw_section.seek_aligned(io::SeekFrom::Start(start_offset)); - Ok(ReadRange::new(raw_section, start_offset, size)) + let json = raw_section.state.json.clone(); + Ok(VerificationStream::new(raw_section, json)) } } @@ -334,14 +375,41 @@ impl Nca { } impl Nca { - pub fn nca_writer(nca_json: NcaJson, output: W) -> Nca { - Nca { + pub fn nca_writer(nca_json: NcaJson, output: W, pki: &Keys) -> Result, Error> { + let mut nca = Nca { stream: output, json: nca_json, + }; + nca.write_header(pki)?; + Ok(nca) + } + + pub fn finalize(mut self) -> Result<(), Error> { + for (idx, section) in self.json.sections.iter().enumerate() { + if let Some(section) = section { + let mut io = self.raw_section(idx)?; + match section.fstype { + FsType::Pfs0 { hash_table_offset, hash_table_size, + pfs0_offset, pfs0_size, .. } => { + let hash_table_end = hash_table_offset + hash_table_size; + io.seek_aligned(io::SeekFrom::Start(hash_table_end))?; + let data = vec![0; (pfs0_offset - hash_table_end) as usize]; + io.write_all(&data)?; + + let pfs0_end = align_up(pfs0_offset + pfs0_size, 16); + let pfs0_absolute_end = pfs0_end + section.media_start_offset as u64 * 512; + io.seek_aligned(io::SeekFrom::Start(pfs0_end))?; + let data = vec![0; (section.media_end_offset as u64 * 512 - pfs0_absolute_end) as usize]; + io.write_all(&data)?; + }, + _ => unimplemented!() + } + } } + Ok(()) } - pub fn write_nca(&self, pki: &Keys, mut sections: [Option<&mut Read>; 4]) -> Result<(), Error> { + fn write_header(&mut self, pki: &Keys) -> Result<(), Error> { let key_area_key = get_key_area_key(pki, self.json.key_revision as _, self.json.key_type)?; let mut header = RawNca { @@ -356,7 +424,7 @@ impl Nca { content_type: self.json.content_type as u8, crypto_type: if self.json.key_revision == 0 { 0 } else { 2 }, key_index: self.json.key_type as u8, - nca_size: 0, + nca_size: self.json.nca_size, title_id: self.json.title_id.0, _padding0: SkipDebug(0), sdk_version: self.json.sdk_version, @@ -369,44 +437,69 @@ impl Nca { unknown1: 0, unknown2: 0, }; 4], - section_hashes: [[0; 0x20]; 4], + section_hashes: [[0; 0x20]; 4], // Derived from fs_headers encrypted_xts_key: key_area_key.encrypt_xts_key(&self.json.xts_key), encrypted_ctr_key: key_area_key.encrypt_key(&self.json.ctr_key), - unknown_new_key: [0; 0x10], + unknown_new_key: key_area_key.encrypt_key(&Aes128Key([0; 0x10])), _padding2: SkipDebug([0; 0xC0]), fs_headers: [RawNcaFsHeader { version: 0, - partition_type: RawPartitionType::RomFs, - fs_type: RawFsType::RomFs, - crypt_type: RawCryptType::Ctr, + partition_type: RawPartitionType(0), + fs_type: RawFsType(0), + crypt_type: RawCryptType(0), _0x5: [0; 0x3], superblock: RawSuperblock { - pfs0: RawPfs0Superblock { - master_hash: [0; 0x20], - block_size: 0, - always_2: 2, - hash_table_offset: 0, - hash_table_size: 0, - pfs0_offset: 0, - pfs0_size: 0, - _0x48: SkipDebug([0; 0xF0]), - } + raw: [0; 0x138], }, section_ctr: 0, _0x148: [0; 0xB8] }; 4] }; - for (idx, section) in sections.iter_mut().enumerate() { + for (idx, section) in self.json.sections.iter().enumerate() { if let Some(section) = section { - let mut section_writer = self.section(idx)?; - std::io::copy(section, &mut section_writer)?; + header.section_entries[idx].media_start_offset = section.media_start_offset; + header.section_entries[idx].media_end_offset = section.media_end_offset; + header.section_entries[idx].unknown1 = section.unknown1; + header.section_entries[idx].unknown2 = section.unknown2; + + header.fs_headers[idx].crypt_type = section.crypto.into(); + header.fs_headers[idx].section_ctr = section.nounce; + + match section.fstype { + FsType::Pfs0 { master_hash, block_size, hash_table_offset, + hash_table_size, pfs0_offset, pfs0_size } => { + header.fs_headers[idx].version = 2; + header.fs_headers[idx].partition_type = RawPartitionType::Pfs0; + header.fs_headers[idx].fs_type = RawFsType::Pfs0; + + header.fs_headers[idx].superblock = RawSuperblock { + pfs0: RawPfs0Superblock { + master_hash: master_hash.0, block_size, + always_2: 2, + hash_table_offset, hash_table_size, + pfs0_offset, pfs0_size, + _0x48: SkipDebug([0; 0xF0]) + } + }; + }, + _ => unimplemented!() + } + + let fs_header_bytes = unsafe { + // Safety: RawNcaFsHeader has no padding. + plain::as_bytes(&header.fs_headers[idx]) + }; + let hash = Sha256::digest(fs_header_bytes); + header.section_hashes[idx].copy_from_slice(hash.as_slice()); } } + let header_bytes = encrypt_header(pki, &mut header)?; + self.stream.write_all(header_bytes)?; + Ok(()) } - } impl SectionJson { @@ -438,8 +531,8 @@ struct CryptoStreamState { json: SectionJson, } -impl CryptoStream { - fn seek_aligned(&mut self, from: io::SeekFrom) { +impl CryptoStream { + fn seek_aligned(&mut self, from: io::SeekFrom) -> std::io::Result<()> { let new_offset = match from { io::SeekFrom::Start(cur) => cur, io::SeekFrom::Current(val) => (self.state.offset as i64 + val) as u64, @@ -448,7 +541,9 @@ impl CryptoStream { if new_offset % 16 != 0 { panic!("Seek not aligned"); } + self.stream.seek(io::SeekFrom::Start(new_offset))?; self.state.offset = new_offset; + Ok(()) } } @@ -539,7 +634,7 @@ impl Read for CryptoStream { } } -impl Write for CryptoStream { +impl Write for CryptoStream { fn write(&mut self, mut buf: &[u8]) -> io::Result { let previous_leftovers = (self.state.offset % 16) as usize; let previous_leftovers_written = if previous_leftovers != 0 { @@ -550,11 +645,16 @@ impl Write for CryptoStream { let size = to - previous_leftovers; self.buffer[previous_leftovers..to].copy_from_slice(&buf[..size]); - if to == 16 { - // We are done handling this block. Write it to disk. - // TODO: Bubble up the error. - self.state.encrypt(&mut self.buffer).unwrap(); - self.stream.write_all(&self.buffer)?; + // We are done handling this block. Write it to disk. + // TODO: Bubble up the error. + self.state.encrypt(&mut self.buffer).unwrap(); + self.stream.write_all(&self.buffer)?; + self.state.decrypt(&mut self.buffer).unwrap(); + + if to != 16 { + self.stream.seek(io::SeekFrom::Current(-16))?; + } else { + self.buffer = [0; 16]; } self.state.offset += size as u64; @@ -578,7 +678,12 @@ impl Write for CryptoStream { // be processed in a subsequent write. Note that this will not work // at all if you mix reads and writes... let from = align_down(buf.len(), 16); + self.buffer = [0; 16]; self.buffer[..leftovers].copy_from_slice(&buf[from..buf.len()]); + self.state.encrypt(&mut self.buffer).unwrap(); + self.stream.write_all(&self.buffer)?; + self.state.decrypt(&mut self.buffer).unwrap(); + self.stream.seek(io::SeekFrom::Current(-16))?; self.state.offset += leftovers as u64; } @@ -590,7 +695,7 @@ impl Write for CryptoStream { } } -impl Seek for CryptoStream { +impl Seek for CryptoStream { fn seek(&mut self, from: io::SeekFrom) -> io::Result { self.state.offset = match from { io::SeekFrom::Start(cur) => cur, @@ -608,41 +713,108 @@ impl Seek for CryptoStream { } } -/*pub struct VerificationStream { - stream: R, - hashes_start: u64, - data_off: u64, - data_size: u64, +pub struct VerificationStream { + stream: ReadRange, + section: SectionJson, + cur_off: u64, + curblock: [u8; 4096], // Hopefully a block isn't ever bigger than that... } impl VerificationStream { - fn new(stream: R, hashes_start: u64, data_start: u64, data_size: u64) -> Self { + fn new(stream: R, section: SectionJson) -> Self { + let (start_offset, size) = match section.fstype { + FsType::Pfs0 { pfs0_offset, pfs0_size, .. } => (pfs0_offset, pfs0_size), + _ => (0, section.size()), + }; VerificationStream { - stream, - hashes_start, - data_off: data_start, - data_size + stream: ReadRange::new(stream, start_offset, size), + section, + cur_off: 0, + curblock: [0; 4096], } } } impl Read for VerificationStream { fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.stream.seek(SeekFrom::Start(data_off))?; - self.stream.read(buf); + self.stream.read(buf) + // TODO: Now, verify the buffer. + } +} + +fn write_hash(section: &SectionJson, cur_off: u64, stream: &mut ReadRange, block: &[u8]) -> io::Result<()> { + match section.fstype { + FsType::Pfs0 { hash_table_offset, .. } => { + let hash = Sha256::digest(block); + let hash_pos = hash_table_offset + 0x20 * (cur_off / 4096); + stream.as_inner_mut().seek(io::SeekFrom::Start(hash_pos)).unwrap(); + stream.as_inner_mut().write_all(hash.as_slice()).unwrap(); + }, + _ => unimplemented!() + } + Ok(()) +} +impl VerificationStream { + pub fn finalize(mut self) -> Result<(), Error> { + if self.cur_off % 4096 != 0 { + // there are leftovers, write them. + let curblock_off = (self.cur_off % 4096) as usize; + self.stream.write_all(&self.curblock[..curblock_off])?; + write_hash(&self.section, self.cur_off, &mut self.stream, &self.curblock[..curblock_off])?; + } + Ok(()) + } +} + +impl Write for VerificationStream { + fn write(&mut self, mut buf: &[u8]) -> io::Result { + let buflen = buf.len(); + if self.cur_off % 4096 != 0 { + // First, handle leftovers. + let curblock_off = (self.cur_off % 4096) as usize; + let leftovers = self.curblock[curblock_off..].len(); + self.curblock[curblock_off..].copy_from_slice(&buf[..leftovers]); + + if curblock_off + leftovers == 4096 { + self.stream.write_all(&self.curblock)?; + let pos = self.stream.pos_in_stream(); + write_hash(&self.section, self.cur_off, &mut self.stream, &self.curblock)?; + self.stream.as_inner_mut().seek(io::SeekFrom::Start(pos)).unwrap(); + } + self.cur_off += leftovers as u64; + buf = &buf[leftovers..]; + } + self.stream.write_all(&buf[..align_down(buf.len(), 4096)])?; + let chunks_exact = buf.chunks_exact(4096); + for block in chunks_exact { + let pos = self.stream.pos_in_stream(); + write_hash(&self.section, self.cur_off, &mut self.stream, block)?; + self.stream.as_inner_mut().seek(io::SeekFrom::Start(pos)).unwrap(); + self.cur_off += 4096; + } + + let remainder = buf.chunks_exact(4096).remainder(); + self.curblock[..remainder.len()].copy_from_slice(remainder); + self.cur_off += remainder.len() as u64; + Ok(buflen) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) } } impl Seek for VerificationStream { fn seek(&mut self, from: io::SeekFrom) -> io::Result { - let from = match from { - io::SeekFrom::Start(cur) => io::SeekFrom::Start(self.start_at + cur), - io::SeekFrom::Current(val) => io::SeekFrom::Start(self.pos + val), - io::SeekFrom::End(val) => io::SeekFrom::Start(val), - }; - self.stream.seek(self.start_at + from) + unimplemented!(); + /*let seek_pos = self.stream.seek(from)?; + if seek_pos % 4096 == 0 { + // It's all fine, let's reset curblock to full 0s. + self.curblock = [0; 4096]; + } else { + self.stream.seek(io::SeekFrom::Start(align_down(seek_pos, 4096)))?; + self.stream.read_exact(&mut self.curblock)?; + self.stream.seek(io::SeekFrom::Start(seek_pos))?; + }*/ } } - -impl -*/ diff --git a/src/format/nca/structures.rs b/src/format/nca/structures.rs index ca19ff1..0f5ace0 100644 --- a/src/format/nca/structures.rs +++ b/src/format/nca/structures.rs @@ -70,6 +70,7 @@ pub union RawSuperblock { //romfs_superblock: RomfsSuperblock, //bktrs_superblock: BktrSuperblock, //nca0_romfs_superblock: Nca0RomfsSuperblock, + pub raw: [u8; 0x138] } assert_eq_size!(assert_superblock_size; RawSuperblock, [u8; 0x138]); @@ -89,21 +90,21 @@ assert_eq_size!(assert_nca_fs_header_size; RawNcaFsHeader, [u8; 0x148 + 0xB8]); enum_with_val! { #[derive(Clone, Copy)] - pub struct RawPartitionType(u8) { + pub struct RawPartitionType(pub u8) { RomFs = 0, Pfs0 = 1 } } enum_with_val! { #[derive(Clone, Copy)] - pub struct RawFsType(u8) { + pub struct RawFsType(pub u8) { Pfs0 = 2, RomFs = 3 } } enum_with_val! { #[derive(Clone, Copy)] - pub struct RawCryptType(u8) { + pub struct RawCryptType(pub u8) { None = 1, Xts = 2, Ctr = 3, Bktr = 4 } } diff --git a/src/pki.rs b/src/pki.rs index e17b5d6..d1a0224 100644 --- a/src/pki.rs +++ b/src/pki.rs @@ -15,12 +15,12 @@ use getset::Getters; use cmac::crypto_mac::Mac; #[derive(Clone, Copy)] -pub struct Aes128Key([u8; 0x10]); +pub struct Aes128Key(pub [u8; 0x10]); #[derive(Clone, Copy)] -pub struct AesXtsKey([u8; 0x20]); -pub struct EncryptedKeyblob([u8; 0xB0]); -pub struct Keyblob([u8; 0x90]); -pub struct Modulus([u8; 0x100]); +pub struct AesXtsKey(pub [u8; 0x20]); +pub struct EncryptedKeyblob(pub [u8; 0xB0]); +pub struct Keyblob(pub [u8; 0x90]); +pub struct Modulus(pub [u8; 0x100]); impl_debug_deserialize_serialize_hexstring!(Aes128Key); impl_debug_deserialize_serialize_hexstring!(AesXtsKey); @@ -134,22 +134,35 @@ fn get_tweak(mut sector: usize) -> [u8; 0x10] { } impl AesXtsKey { - pub fn decrypt(&self, src: &[u8], dst: &mut [u8], mut sector: usize, sector_size: usize) -> Result<(), Error> { - if src.len() != dst.len() { - return Err(Error::Crypto(String::from("Src len different from dst len"), Backtrace::new())); + pub fn decrypt(&self, data: &mut [u8], mut sector: usize, sector_size: usize) -> Result<(), Error> { + if data.len() % sector_size != 0 { + return Err(Error::Crypto(String::from("Length must be multiple of sectors!"), Backtrace::new())); + } + + for i in (0..data.len()).step_by(sector_size){ + let tweak = get_tweak(sector); + + let key1 = Aes128::new(GenericArray::from_slice(&self.0[0x00..0x10])); + let key2 = Aes128::new(GenericArray::from_slice(&self.0[0x10..0x20])); + let mut crypter = Xts128::::new(key1, key2, GenericArray::from_slice(&tweak)); + crypter.decrypt_nopad(&mut data[i..i + sector_size])?; + sector += 1; } - if src.len() % sector_size != 0 { + Ok(()) + } + + pub fn encrypt(&self, data: &mut [u8], mut sector: usize, sector_size: usize) -> Result<(), Error> { + if data.len() % sector_size != 0 { return Err(Error::Crypto(String::from("Length must be multiple of sectors!"), Backtrace::new())); } - dst.copy_from_slice(src); - for i in (0..src.len()).step_by(sector_size){ + for i in (0..data.len()).step_by(sector_size) { let tweak = get_tweak(sector); let key1 = Aes128::new(GenericArray::from_slice(&self.0[0x00..0x10])); let key2 = Aes128::new(GenericArray::from_slice(&self.0[0x10..0x20])); let mut crypter = Xts128::::new(key1, key2, GenericArray::from_slice(&tweak)); - crypter.decrypt_nopad(&mut dst[i..i + sector_size])?; + crypter.encrypt_nopad(&mut data[i..i + sector_size])?; sector += 1; } Ok(()) diff --git a/src/utils.rs b/src/utils.rs index 24451e1..d1af203 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -5,12 +5,12 @@ use std::io; // TODO: Document #[macro_export] macro_rules! enum_with_val { - ($(#[$meta:meta])* $vis:vis struct $ident:ident($ty:ty) { + ($(#[$meta:meta])* $vis:vis struct $ident:ident($consvis:vis $ty:ty) { $($variant:ident = $num:expr),* $(,)* }) => { $(#[$meta])* #[derive(PartialEq, Eq)] - $vis struct $ident($ty); + $vis struct $ident($consvis $ty); impl $ident { $(#[allow(non_upper_case_globals)] $vis const $variant: $ident = $ident($num);)* } @@ -18,8 +18,8 @@ macro_rules! enum_with_val { impl ::core::fmt::Debug for $ident { fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { match self { - $(&$ident::$variant => f.write_str(stringify!($ident)),)* - &$ident(v) => write!(f, "UNKNOWN({})", v), + $(&$ident::$variant => write!(f, "{}::{}", stringify!($ident), stringify!($variant)),)* + &$ident(v) => write!(f, "{}::UNKNOWN({})", stringify!($ident), v), } } } @@ -104,6 +104,11 @@ macro_rules! impl_debug_deserialize_serialize_hexstring { } } +pub fn align_up + BitAnd + Copy>(addr: T, align: T) -> T +{ + align_down(addr + (align - T::one()), align) +} + pub fn align_down + BitAnd + Copy>(addr: T, align: T) -> T { addr & !(align - T::one()) @@ -138,6 +143,11 @@ impl ReadRange { pub fn pos_in_stream(&self) -> u64 { self.start_from + self.inner_pos } + + /// Very dangerous. Don't use. + pub fn as_inner_mut(&mut self) -> &mut R { + &mut self.inner + } } impl io::Read for ReadRange {