Skip to content

Commit

Permalink
Add CMAC verification of keyblobs, generate encrypted keyblobs
Browse files Browse the repository at this point in the history
  • Loading branch information
roblabla committed Jan 24, 2019
1 parent 935fed2 commit 9ba46f8
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 14 deletions.
37 changes: 37 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,10 @@ block-modes = "0.2"
aes = "0.3"
num-traits = "0.2"
derive_more = "0.13"
cmac = "0.2.0"

[features]
binaries = ["structopt", "cargo_metadata", "scroll", "goblin"]

[patch.crates-io]
block-modes = { git = 'https://github.com/roblabla/block-ciphers' }
block-modes = { git = 'https://github.com/roblabla/block-ciphers' }
22 changes: 14 additions & 8 deletions src/bin/linkle_clap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,11 @@ enum Opt {

/// Use development keys instead of retail
#[structopt(short = "d", long = "dev")]
dev: bool
dev: bool,

/// Keyfile
#[structopt(parse(from_os_str), short = "k", long = "keyset")]
keyfile: Option<PathBuf>,
}
}

Expand Down Expand Up @@ -159,15 +163,17 @@ fn to_opt_ref<U: ?Sized, T: AsRef<U>>(s: &Option<T>) -> Option<&U> {
s.as_ref().map(AsRef::as_ref)
}

fn extract_nca(input_file: &Path, is_dev: bool, output_header_json: Option<&Path>,
fn extract_nca(input_file: &Path, is_dev: bool, key_path: Option<&Path>,
output_header_json: Option<&Path>,
output_section0: Option<&Path>, output_section1: Option<&Path>,
output_section2: Option<&Path>, output_section3: Option<&Path>) -> std::io::Result<()> {
output_section2: Option<&Path>, output_section3: Option<&Path>) -> Result<(), linkle::error::Error> {
let keys = if is_dev {
linkle::pki::Keys::new_dev(None).unwrap()
linkle::pki::Keys::new_dev(key_path).unwrap()
} else {
linkle::pki::Keys::new_retail(None).unwrap()
linkle::pki::Keys::new_retail(key_path).unwrap()
};
let nca = linkle::format::nca::Nca::from_file(&keys, File::open(input_file)?).unwrap();
println!("{:#?}", keys);
/*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();
Expand All @@ -191,7 +197,7 @@ fn extract_nca(input_file: &Path, is_dev: bool, output_header_json: 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(())
}

Expand All @@ -204,7 +210,7 @@ fn process_args(app: &Opt) {
Opt::Romfs { ref input_directory, ref output_file } => create_romfs(input_directory, output_file),
Opt::NcaExtract { ref input_file, ref header_file, ref section0_file,
ref section1_file, ref section2_file,
ref section3_file, dev } => extract_nca(input_file, *dev, 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)),
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)),
};

if let Err(e) = res {
Expand Down
8 changes: 8 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ pub enum Error {
MissingSection(usize, Backtrace),
#[display(fmt = "Invalid NCA: {}.", _0)]
InvalidNca(&'static str, Backtrace),
#[display(fmt = "Invalid keyblob {}: {}.", _1, _0)]
MacError(cmac::crypto_mac::MacError, usize, Backtrace),
}

impl Error {
Expand Down Expand Up @@ -75,3 +77,9 @@ impl From<BlockModeError> for Error {
Error::BlockMode(err, Backtrace::new())
}
}

impl From<(usize, cmac::crypto_mac::MacError)> for Error {
fn from((id, err): (usize, cmac::crypto_mac::MacError)) -> Error {
Error::MacError(err, id, Backtrace::new())
}
}
33 changes: 28 additions & 5 deletions src/pki.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ use std::io::{self, ErrorKind};
use std::path::Path;
use crate::error::Error;
use aes::Aes128;
use cmac::Cmac;
use block_modes::{Ctr128, Xts128, BlockModeIv, BlockMode};
use block_modes::block_padding::ZeroPadding;
use aes::block_cipher_trait::generic_array::GenericArray;
use aes::block_cipher_trait::BlockCipher;
use getset::Getters;
use cmac::crypto_mac::Mac;

#[derive(Clone, Copy)]
pub struct Aes128Key([u8; 0x10]);
Expand All @@ -26,11 +28,30 @@ impl_debug_deserialize_serialize_hexstring!(EncryptedKeyblob);
impl_debug_deserialize_serialize_hexstring!(Keyblob);
impl_debug_deserialize_serialize_hexstring!(Modulus);

impl Keyblob {
fn encrypt(&self, key: &Aes128Key, mac_key: &Aes128Key, keyblob_id: usize) -> Result<EncryptedKeyblob, Error> {
let mut encrypted_keyblob = [0; 0xB0];
encrypted_keyblob[0x20..].copy_from_slice(&self.0);

let mut crypter = Ctr128::<Aes128, ZeroPadding>::new_fixkey(GenericArray::from_slice(&key.0), GenericArray::from_slice(&encrypted_keyblob[0x10..0x20]));
crypter.encrypt_nopad(&mut encrypted_keyblob[0x20..])?;

let mut cmac = Cmac::<Aes128>::new_varkey(&mac_key.0[..]).unwrap();
cmac.input(&encrypted_keyblob[0x10..]);
encrypted_keyblob[..0x10].copy_from_slice(cmac.result().code().as_slice());
Ok(EncryptedKeyblob(encrypted_keyblob))
}
}

impl EncryptedKeyblob {
fn decrypt(&self, key: &Aes128Key) -> Result<Keyblob, Error> {
fn decrypt(&self, key: &Aes128Key, mac_key: &Aes128Key, keyblob_id: usize) -> Result<Keyblob, Error> {
let mut keyblob = [0; 0x90];
keyblob.copy_from_slice(&self.0[0x20..]);

let mut cmac = Cmac::<Aes128>::new_varkey(&mac_key.0[..]).unwrap();
cmac.input(&self.0[0x10..]);
cmac.verify(&self.0[..0x10]).map_err(|err| (keyblob_id, err))?;

let mut crypter = Ctr128::<Aes128, ZeroPadding>::new_fixkey(GenericArray::from_slice(&key.0), GenericArray::from_slice(&self.0[0x10..0x20]));
crypter.decrypt_nopad(&mut keyblob)?;

Expand Down Expand Up @@ -473,10 +494,12 @@ impl Keys {
}
}
for i in 0..6 {
match (&self.keyblob_keys[i], &self.keyblob_mac_keys[i], &self.encrypted_keyblobs[i]) {
(Some(keyblob_key), Some(_keyblob_mac_key), Some(encrypted_keyblob)) => {
// TODO: Calculate cmac
self.keyblobs[i] = Some(encrypted_keyblob.decrypt(keyblob_key)?);
match (&self.keyblob_keys[i], &self.keyblob_mac_keys[i], &mut self.encrypted_keyblobs[i], &mut self.keyblobs[i]) {
(Some(keyblob_key), Some(keyblob_mac_key), Some(encrypted_keyblob), ref mut keyblob @ None) => {
**keyblob = Some(encrypted_keyblob.decrypt(keyblob_key, keyblob_mac_key, i)?);
},
(Some(keyblob_key), Some(keyblob_mac_key), ref mut encrypted_keyblob @ None, Some(keyblob)) => {
**encrypted_keyblob = Some(keyblob.encrypt(keyblob_key, keyblob_mac_key, i)?);
},
_ => continue
}
Expand Down

0 comments on commit 9ba46f8

Please sign in to comment.