mirror of
https://github.com/NinjaCheetah/rustii.git
synced 2025-06-05 23:11:02 -04:00
Added support for dev WADs, Ticket now throws error for v1 Tickets
This commit is contained in:
parent
5731b8d4d8
commit
561ada3d92
6
src/title/cert.rs
Normal file
6
src/title/cert.rs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
// title/cert.rs from rustii (c) 2025 NinjaCheetah & Contributors
|
||||||
|
// https://github.com/NinjaCheetah/rustii
|
||||||
|
//
|
||||||
|
// Implements the structures and methods required for validating the signatures of Wii titles.
|
||||||
|
|
||||||
|
|
@ -15,20 +15,20 @@ fn title_id_to_iv(title_id: [u8; 8]) -> [u8; 16] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Decrypt a Title Key using the specified common key.
|
// Decrypt a Title Key using the specified common key.
|
||||||
pub fn decrypt_title_key(title_key_enc: [u8; 16], common_key_index: u8, title_id: [u8; 8]) -> [u8; 16] {
|
pub fn decrypt_title_key(title_key_enc: [u8; 16], common_key_index: u8, title_id: [u8; 8], is_dev: Option<bool>) -> [u8; 16] {
|
||||||
let iv = title_id_to_iv(title_id);
|
let iv = title_id_to_iv(title_id);
|
||||||
type Aes128CbcDec = cbc::Decryptor<aes::Aes128>;
|
type Aes128CbcDec = cbc::Decryptor<aes::Aes128>;
|
||||||
let decryptor = Aes128CbcDec::new(&get_common_key(common_key_index, None).into(), &iv.into());
|
let decryptor = Aes128CbcDec::new(&get_common_key(common_key_index, is_dev).into(), &iv.into());
|
||||||
let mut title_key = title_key_enc;
|
let mut title_key = title_key_enc;
|
||||||
decryptor.decrypt_padded_mut::<ZeroPadding>(&mut title_key).unwrap();
|
decryptor.decrypt_padded_mut::<ZeroPadding>(&mut title_key).unwrap();
|
||||||
title_key
|
title_key
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encrypt a Title Key using the specified common key.
|
// Encrypt a Title Key using the specified common key.
|
||||||
pub fn encrypt_title_key(title_key_dec: [u8; 16], common_key_index: u8, title_id: [u8; 8]) -> [u8; 16] {
|
pub fn encrypt_title_key(title_key_dec: [u8; 16], common_key_index: u8, title_id: [u8; 8], is_dev: Option<bool>) -> [u8; 16] {
|
||||||
let iv = title_id_to_iv(title_id);
|
let iv = title_id_to_iv(title_id);
|
||||||
type Aes128CbcEnc = cbc::Encryptor<aes::Aes128>;
|
type Aes128CbcEnc = cbc::Encryptor<aes::Aes128>;
|
||||||
let encryptor = Aes128CbcEnc::new(&get_common_key(common_key_index, None).into(), &iv.into());
|
let encryptor = Aes128CbcEnc::new(&get_common_key(common_key_index, is_dev).into(), &iv.into());
|
||||||
let mut title_key = title_key_dec;
|
let mut title_key = title_key_dec;
|
||||||
encryptor.encrypt_padded_mut::<ZeroPadding>(&mut title_key, 16).unwrap();
|
encryptor.encrypt_padded_mut::<ZeroPadding>(&mut title_key, 16).unwrap();
|
||||||
title_key
|
title_key
|
||||||
|
@ -9,6 +9,7 @@ pub mod crypto;
|
|||||||
pub mod ticket;
|
pub mod ticket;
|
||||||
pub mod tmd;
|
pub mod tmd;
|
||||||
pub mod wad;
|
pub mod wad;
|
||||||
|
mod cert;
|
||||||
|
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
@ -3,10 +3,30 @@
|
|||||||
//
|
//
|
||||||
// Implements the structures and methods required for Ticket parsing and editing.
|
// Implements the structures and methods required for Ticket parsing and editing.
|
||||||
|
|
||||||
|
use std::error::Error;
|
||||||
|
use std::fmt;
|
||||||
use std::io::{Cursor, Read, Write};
|
use std::io::{Cursor, Read, Write};
|
||||||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||||
use crate::title::crypto::decrypt_title_key;
|
use crate::title::crypto::decrypt_title_key;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum TicketError {
|
||||||
|
UnsupportedVersion,
|
||||||
|
IOError(std::io::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for TicketError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
let description = match *self {
|
||||||
|
TicketError::UnsupportedVersion => "The provided Ticket is not a supported version (only v0 is supported).",
|
||||||
|
TicketError::IOError(_) => "The provided Ticket data was invalid.",
|
||||||
|
};
|
||||||
|
f.write_str(description)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for TicketError {}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[derive(Copy)]
|
#[derive(Copy)]
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -44,51 +64,55 @@ pub struct Ticket {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Ticket {
|
impl Ticket {
|
||||||
pub fn from_bytes(data: &[u8]) -> Result<Self, std::io::Error> {
|
pub fn from_bytes(data: &[u8]) -> Result<Self, TicketError> {
|
||||||
let mut buf = Cursor::new(data);
|
let mut buf = Cursor::new(data);
|
||||||
let signature_type = buf.read_u32::<BigEndian>()?;
|
let signature_type = buf.read_u32::<BigEndian>().map_err(TicketError::IOError)?;
|
||||||
let mut signature = [0u8; 256];
|
let mut signature = [0u8; 256];
|
||||||
buf.read_exact(&mut signature)?;
|
buf.read_exact(&mut signature).map_err(TicketError::IOError)?;
|
||||||
// Maybe this can be read differently?
|
// Maybe this can be read differently?
|
||||||
let mut padding1 = [0u8; 60];
|
let mut padding1 = [0u8; 60];
|
||||||
buf.read_exact(&mut padding1)?;
|
buf.read_exact(&mut padding1).map_err(TicketError::IOError)?;
|
||||||
let mut signature_issuer = [0u8; 64];
|
let mut signature_issuer = [0u8; 64];
|
||||||
buf.read_exact(&mut signature_issuer)?;
|
buf.read_exact(&mut signature_issuer).map_err(TicketError::IOError)?;
|
||||||
let mut ecdh_data = [0u8; 60];
|
let mut ecdh_data = [0u8; 60];
|
||||||
buf.read_exact(&mut ecdh_data)?;
|
buf.read_exact(&mut ecdh_data).map_err(TicketError::IOError)?;
|
||||||
let ticket_version = buf.read_u8()?;
|
let ticket_version = buf.read_u8().map_err(TicketError::IOError)?;
|
||||||
|
// v1 Tickets are NOT supported (just like in libWiiPy).
|
||||||
|
if ticket_version != 0 {
|
||||||
|
return Err(TicketError::UnsupportedVersion);
|
||||||
|
}
|
||||||
let mut reserved1 = [0u8; 2];
|
let mut reserved1 = [0u8; 2];
|
||||||
buf.read_exact(&mut reserved1)?;
|
buf.read_exact(&mut reserved1).map_err(TicketError::IOError)?;
|
||||||
let mut title_key = [0u8; 16];
|
let mut title_key = [0u8; 16];
|
||||||
buf.read_exact(&mut title_key)?;
|
buf.read_exact(&mut title_key).map_err(TicketError::IOError)?;
|
||||||
let mut unknown1 = [0u8; 1];
|
let mut unknown1 = [0u8; 1];
|
||||||
buf.read_exact(&mut unknown1)?;
|
buf.read_exact(&mut unknown1).map_err(TicketError::IOError)?;
|
||||||
let mut ticket_id = [0u8; 8];
|
let mut ticket_id = [0u8; 8];
|
||||||
buf.read_exact(&mut ticket_id)?;
|
buf.read_exact(&mut ticket_id).map_err(TicketError::IOError)?;
|
||||||
let mut console_id = [0u8; 4];
|
let mut console_id = [0u8; 4];
|
||||||
buf.read_exact(&mut console_id)?;
|
buf.read_exact(&mut console_id).map_err(TicketError::IOError)?;
|
||||||
let mut title_id = [0u8; 8];
|
let mut title_id = [0u8; 8];
|
||||||
buf.read_exact(&mut title_id)?;
|
buf.read_exact(&mut title_id).map_err(TicketError::IOError)?;
|
||||||
let mut unknown2 = [0u8; 2];
|
let mut unknown2 = [0u8; 2];
|
||||||
buf.read_exact(&mut unknown2)?;
|
buf.read_exact(&mut unknown2).map_err(TicketError::IOError)?;
|
||||||
let title_version = buf.read_u16::<BigEndian>()?;
|
let title_version = buf.read_u16::<BigEndian>().map_err(TicketError::IOError)?;
|
||||||
let mut permitted_titles_mask = [0u8; 4];
|
let mut permitted_titles_mask = [0u8; 4];
|
||||||
buf.read_exact(&mut permitted_titles_mask)?;
|
buf.read_exact(&mut permitted_titles_mask).map_err(TicketError::IOError)?;
|
||||||
let mut permit_mask = [0u8; 4];
|
let mut permit_mask = [0u8; 4];
|
||||||
buf.read_exact(&mut permit_mask)?;
|
buf.read_exact(&mut permit_mask).map_err(TicketError::IOError)?;
|
||||||
let title_export_allowed = buf.read_u8()?;
|
let title_export_allowed = buf.read_u8().map_err(TicketError::IOError)?;
|
||||||
let common_key_index = buf.read_u8()?;
|
let common_key_index = buf.read_u8().map_err(TicketError::IOError)?;
|
||||||
let mut unknown3 = [0u8; 48];
|
let mut unknown3 = [0u8; 48];
|
||||||
buf.read_exact(&mut unknown3)?;
|
buf.read_exact(&mut unknown3).map_err(TicketError::IOError)?;
|
||||||
let mut content_access_permission = [0u8; 64];
|
let mut content_access_permission = [0u8; 64];
|
||||||
buf.read_exact(&mut content_access_permission)?;
|
buf.read_exact(&mut content_access_permission).map_err(TicketError::IOError)?;
|
||||||
let mut padding2 = [0u8; 2];
|
let mut padding2 = [0u8; 2];
|
||||||
buf.read_exact(&mut padding2)?;
|
buf.read_exact(&mut padding2).map_err(TicketError::IOError)?;
|
||||||
// Build the array of title limits.
|
// Build the array of title limits.
|
||||||
let mut title_limits: Vec<TitleLimit> = Vec::new();
|
let mut title_limits: Vec<TitleLimit> = Vec::new();
|
||||||
for _ in 0..8 {
|
for _ in 0..8 {
|
||||||
let limit_type = buf.read_u32::<BigEndian>()?;
|
let limit_type = buf.read_u32::<BigEndian>().map_err(TicketError::IOError)?;
|
||||||
let limit_max = buf.read_u32::<BigEndian>()?;
|
let limit_max = buf.read_u32::<BigEndian>().map_err(TicketError::IOError)?;
|
||||||
title_limits.push(TitleLimit { limit_type, limit_max });
|
title_limits.push(TitleLimit { limit_type, limit_max });
|
||||||
}
|
}
|
||||||
let title_limits = title_limits.try_into().unwrap();
|
let title_limits = title_limits.try_into().unwrap();
|
||||||
@ -150,6 +174,14 @@ impl Ticket {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn dec_title_key(&self) -> [u8; 16] {
|
pub fn dec_title_key(&self) -> [u8; 16] {
|
||||||
decrypt_title_key(self.title_key, self.common_key_index, self.title_id)
|
// Get the dev status of this Ticket so decrypt_title_key knows the right common key.
|
||||||
|
let is_dev = self.is_dev();
|
||||||
|
decrypt_title_key(self.title_key, self.common_key_index, self.title_id, Some(is_dev))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_dev(&self) -> bool {
|
||||||
|
// Parse the signature issuer to determine if this is a dev Ticket or not.
|
||||||
|
let issuer_str = String::from_utf8(Vec::from(&self.signature_issuer)).unwrap_or_default();
|
||||||
|
issuer_str.contains("Root-CA00000002-XS00000004") || issuer_str.contains("Root-CA00000002-XS00000006")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user