Merge pull request #5 from G2-Games/upload

Add track upload
This commit is contained in:
G2 2024-03-09 23:57:52 -06:00 committed by GitHub
commit 54129892bc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 179 additions and 51 deletions

1
Cargo.lock generated
View file

@ -760,6 +760,7 @@ dependencies = [
"once_cell", "once_cell",
"rand", "rand",
"regex", "regex",
"tokio",
"unicode-jp", "unicode-jp",
"unicode-normalization", "unicode-normalization",
"web-sys", "web-sys",

View file

@ -28,6 +28,7 @@ getrandom = { version = "0.2.12", features = ["js"] }
des = "0.8.1" des = "0.8.1"
cbc = "0.1.2" cbc = "0.1.2"
ecb = "0.1.2" ecb = "0.1.2"
tokio = { version = "1.35.1", features = ["sync"] }
[dependencies.unicode-jp] [dependencies.unicode-jp]
# Relying on this fork for now as it has up-to-date deps # Relying on this fork for now as it has up-to-date deps

View file

@ -254,7 +254,7 @@ impl NetMD {
// Back off while trying again // Back off while trying again
let sleep_time = Self::READ_REPLY_RETRY_INTERVAL let sleep_time = Self::READ_REPLY_RETRY_INTERVAL
* (u32::pow(2, attempt / 10) - 1); * (u32::pow(2, attempt) - 1);
cross_sleep(sleep_time).await; cross_sleep(sleep_time).await;
} }

View file

@ -3,7 +3,7 @@ use std::error::Error;
use num_derive::FromPrimitive; use num_derive::FromPrimitive;
use num_traits::FromPrimitive; use num_traits::FromPrimitive;
use super::interface::{NetMDInterface, MDTrack}; use super::interface::{NetMDInterface, MDTrack, MDSession};
use super::utils::cross_sleep; use super::utils::cross_sleep;
#[derive(FromPrimitive)] #[derive(FromPrimitive)]
@ -72,8 +72,13 @@ pub async fn prepare_download(interface: &mut NetMDInterface) -> Result<(), Box<
Ok(()) Ok(())
} }
pub async fn download(interface: &mut NetMDInterface, _track: MDTrack) -> Result<(), Box<dyn Error>>{ pub async fn download<F>(interface: &mut NetMDInterface, track: MDTrack, progress_callback: F) -> Result<(u16, Vec<u8>, Vec<u8>), Box<dyn Error>> where F: Fn(usize, usize){
prepare_download(interface).await?; prepare_download(interface).await?;
let mut session = MDSession::new(interface);
session.init().await?;
let result = session.download_track(track, progress_callback, None).await?;
session.close().await?;
interface.release().await?;
Ok(()) Ok(result)
} }

View file

@ -0,0 +1,67 @@
use std::thread;
use cbc::cipher::block_padding::NoPadding;
use cbc::cipher::{KeyInit, BlockDecryptMut, KeyIvInit, BlockEncryptMut};
use tokio::sync::mpsc::{UnboundedReceiver, unbounded_channel};
use rand::RngCore;
use super::interface::DataEncryptorInput;
type DesEcbEnc = ecb::Decryptor<des::Des>;
type DesCbcEnc = cbc::Encryptor<des::Des>;
pub fn new_thread_encryptor(_input: DataEncryptorInput) -> UnboundedReceiver<(Vec<u8>, Vec<u8>, Vec<u8>)> {
let (tx, rx) = unbounded_channel::<(Vec<u8>, Vec<u8>, Vec<u8>)>();
let input = Box::from(_input);
thread::spawn(move || {
let mut iv = [0u8; 8];
// Create the random key
let mut random_key = [0u8; 8];
rand::thread_rng().fill_bytes(&mut random_key);
// Encrypt it with the kek
let mut encrypted_random_key = random_key.clone();
match DesEcbEnc::new(&input.kek.into()).decrypt_padded_mut::<NoPadding>(&mut encrypted_random_key){
Err(x) => panic!("Cannot create main key {:?}", x),
Ok(_) => {}
};
let default_chunk_size = match input.chunk_size{
0 => 0x00100000,
e => e
};
let mut packet_count = 0u32;
let mut current_chunk_size;
let mut input_data = input.data.clone();
if (input_data.len() % input.frame_size) != 0 {
let padding_remaining = input.frame_size - (input_data.len() % input.frame_size);
input_data.extend(std::iter::repeat(0).take(padding_remaining));
}
let input_data_length = input_data.len();
let mut offset: usize = 0;
while offset < input_data_length {
if packet_count > 0 {
current_chunk_size = default_chunk_size;
} else {
current_chunk_size = default_chunk_size - 24;
}
current_chunk_size = std::cmp::min(current_chunk_size, input_data_length - offset);
let this_data_chunk = &mut input_data[offset..offset+current_chunk_size];
DesCbcEnc::new(&random_key.into(), &iv.into()).encrypt_padded_mut::<NoPadding>(this_data_chunk, current_chunk_size).unwrap();
tx.send((encrypted_random_key.to_vec(), iv.to_vec(), this_data_chunk.to_vec())).unwrap();
iv.copy_from_slice(&this_data_chunk[this_data_chunk.len()-8..]);
packet_count += 1;
offset += current_chunk_size;
}
});
rx
}

View file

@ -8,10 +8,11 @@ use crate::netmd::utils::{
use cbc::cipher::block_padding::NoPadding; use cbc::cipher::block_padding::NoPadding;
use cbc::cipher::{KeyIvInit, BlockEncryptMut, BlockDecryptMut, KeyInit}; use cbc::cipher::{KeyIvInit, BlockEncryptMut, BlockDecryptMut, KeyInit};
use encoding_rs::SHIFT_JIS; use encoding_rs::SHIFT_JIS;
use num_derive::FromPrimitive;
use rand::RngCore; use rand::RngCore;
use tokio::sync::mpsc::UnboundedReceiver;
use std::collections::HashMap; use std::collections::HashMap;
use std::error::Error; use std::error::Error;
use std::str::FromStr;
use lazy_static::lazy_static; use lazy_static::lazy_static;
@ -31,7 +32,7 @@ enum Track {
Restart = 0x0001, Restart = 0x0001,
} }
#[derive(Debug)] #[derive(Debug, Clone, Copy, FromPrimitive)]
pub enum DiscFormat { pub enum DiscFormat {
LP4 = 0, LP4 = 0,
LP2 = 2, LP2 = 2,
@ -39,7 +40,7 @@ pub enum DiscFormat {
SPStereo = 6, SPStereo = 6,
} }
#[derive(Clone, Hash, Eq, PartialEq)] #[derive(Clone, Hash, Eq, PartialEq, FromPrimitive)]
pub enum WireFormat { pub enum WireFormat {
Pcm = 0x00, Pcm = 0x00,
L105kbps = 0x90, L105kbps = 0x90,
@ -391,6 +392,7 @@ impl NetMDInterface {
let sleep_time = Self::INTERIM_RESPONSE_RETRY_INTERVAL let sleep_time = Self::INTERIM_RESPONSE_RETRY_INTERVAL
* (u32::pow(2, current_attempt as u32) - 1); * (u32::pow(2, current_attempt as u32) - 1);
cross_sleep(sleep_time).await; cross_sleep(sleep_time).await;
current_attempt += 1; current_attempt += 1;
@ -459,7 +461,7 @@ impl NetMDInterface {
Ok(()) Ok(())
} }
async fn release(&mut self) -> Result<(), Box<dyn Error>> { pub async fn release(&mut self) -> Result<(), Box<dyn Error>> {
let mut query = format_query("ff 0100 ffff ffff ffff ffff ffff ffff".to_string(), vec![])?; let mut query = format_query("ff 0100 ffff ffff ffff ffff ffff ffff".to_string(), vec![])?;
let reply = self.send_query(&mut query, false, false).await?; let reply = self.send_query(&mut query, false, false).await?;
@ -1431,8 +1433,8 @@ impl NetMDInterface {
pub async fn setup_download( pub async fn setup_download(
&mut self, &mut self,
contentid: Vec<u8>, contentid: &[u8],
keyenckey: Vec<u8>, keyenckey: &[u8],
hex_session_key: &[u8], hex_session_key: &[u8],
) -> Result<(), Box<dyn Error>> { ) -> Result<(), Box<dyn Error>> {
if contentid.len() != 20 { if contentid.len() != 20 {
@ -1441,11 +1443,11 @@ impl NetMDInterface {
if keyenckey.len() != 8 { if keyenckey.len() != 8 {
return Err("Supplied Key Encryption Key length wrong".into()); return Err("Supplied Key Encryption Key length wrong".into());
} }
if hex_session_key.len() != 16 { if hex_session_key.len() != 8 {
return Err("Supplied Session Key length wrong".into()); return Err("Supplied Session Key length wrong".into());
} }
let mut message = [vec![1, 1, 1, 1], contentid, keyenckey].concat(); let mut message = [vec![1, 1, 1, 1], contentid.to_vec(), keyenckey.to_vec()].concat();
DesCbcEnc::new(hex_session_key.into(), &[0u8; 8].into()).encrypt_padded_mut::<NoPadding>(message.as_mut_slice(), 32).unwrap(); DesCbcEnc::new(hex_session_key.into(), &[0u8; 8].into()).encrypt_padded_mut::<NoPadding>(message.as_mut_slice(), 32).unwrap();
let mut query = format_query( let mut query = format_query(
@ -1465,7 +1467,7 @@ impl NetMDInterface {
track_number: u16, track_number: u16,
hex_session_key: &[u8], hex_session_key: &[u8],
) -> Result<(), Box<dyn Error>> { ) -> Result<(), Box<dyn Error>> {
if hex_session_key.len() != 16 { if hex_session_key.len() != 8 {
return Err("Supplied Session Key length wrong".into()); return Err("Supplied Session Key length wrong".into());
} }
@ -1473,7 +1475,7 @@ impl NetMDInterface {
DesEcbEnc::new(hex_session_key.into()).encrypt_padded_mut::<NoPadding>(&mut message, 8).unwrap(); DesEcbEnc::new(hex_session_key.into()).encrypt_padded_mut::<NoPadding>(&mut message, 8).unwrap();
let mut query = format_query( let mut query = format_query(
"1800 080046 f0030103 22 ff 0000 %*".to_string(), "1800 080046 f0030103 48 ff 00 1001 %w %*".to_string(),
vec![ vec![
QueryValue::Number(track_number as i64), QueryValue::Number(track_number as i64),
QueryValue::Array(Vec::from(message)), QueryValue::Array(Vec::from(message)),
@ -1482,29 +1484,30 @@ impl NetMDInterface {
let reply = self.send_query(&mut query, false, false).await?; let reply = self.send_query(&mut query, false, false).await?;
scan_query(reply, "1800 080046 f0030103 22 00 0000".to_string())?; scan_query(reply, "1800 080046 f0030103 48 00 00 1001 %?%?".to_string())?;
Ok(()) Ok(())
} }
pub async fn send_track( pub async fn send_track<F>(
&mut self, &mut self,
wireformat: u8, wireformat: u8,
discformat: u8, discformat: u8,
frames: i32, frames: u32,
pkt_size: u32, pkt_size: u32,
// key // iv // data // key // iv // data
packets: Vec<(Vec<u8>, Vec<u8>, Vec<u8>)>, mut packets: UnboundedReceiver<(Vec<u8>, Vec<u8>, Vec<u8>)>,
hex_session_key: &[u8], hex_session_key: &[u8],
) -> Result<(i64, String, String), Box<dyn Error>> { progress_callback: F
if hex_session_key.len() != 16 { ) -> Result<(u16, Vec<u8>, Vec<u8>), Box<dyn Error>> where F: Fn(usize, usize) {
if hex_session_key.len() != 8 {
return Err("Supplied Session Key length wrong".into()); return Err("Supplied Session Key length wrong".into());
} }
// Sharps are slow // Sharps are slow
cross_sleep(200).await; cross_sleep(200).await;
let total_bytes = pkt_size + 24; //framesizedict[wireformat] * frames + pktcount * 24; let total_bytes: usize = (pkt_size + 24) as usize; //framesizedict[wireformat] * frames + pktcount * 24;
let mut query = format_query( let mut query = format_query(
"1800 080046 f0030103 28 ff 000100 1001 ffff 00 %b %b %d %d".to_string(), "1800 080046 f0030103 28 ff 000100 1001 ffff 00 %b %b %d %d".to_string(),
@ -1520,20 +1523,29 @@ impl NetMDInterface {
reply, reply,
"1800 080046 f0030103 28 00 000100 1001 %?%? 00 %*".to_string(), "1800 080046 f0030103 28 00 000100 1001 %?%? 00 %*".to_string(),
)?; )?;
self.net_md_device.poll().await?;
// Sharps are slow // Sharps are slow
cross_sleep(200).await; cross_sleep(200).await;
let mut _written_bytes = 0; let mut _written_bytes = 0;
for (packet_count, (key, iv, data)) in packets.into_iter().enumerate() { let mut packet_count = 0;
while let Some((key, iv, data)) = packets.recv().await {
let binpack = if packet_count == 0 { let binpack = if packet_count == 0 {
let packed_length: Vec<u8> = pkt_size.to_le_bytes().to_vec(); let packed_length: Vec<u8> = pkt_size.to_be_bytes().to_vec();
[vec![0, 0, 0, 0], packed_length, key, iv, data.clone()].concat() [vec![0, 0, 0, 0], packed_length, key, iv, data].concat()
} else { } else {
data.clone() data
}; };
self.net_md_device.write_bulk(&binpack).await?; self.net_md_device.write_bulk(&binpack).await?;
_written_bytes += data.len(); _written_bytes += binpack.len();
packet_count += 1;
(progress_callback)(total_bytes, _written_bytes);
if total_bytes == _written_bytes.try_into().unwrap() {
packets.close();
break;
}
} }
reply = self.read_reply(false).await?; reply = self.read_reply(false).await?;
@ -1546,12 +1558,10 @@ impl NetMDInterface {
let mut encrypted_data = res[1].to_vec().unwrap(); let mut encrypted_data = res[1].to_vec().unwrap();
DesCbcDec::new(hex_session_key.into(), &[0u8; 8].into()).decrypt_padded_mut::<NoPadding>(&mut encrypted_data).unwrap(); DesCbcDec::new(hex_session_key.into(), &[0u8; 8].into()).decrypt_padded_mut::<NoPadding>(&mut encrypted_data).unwrap();
let reply_data = String::from_utf8(encrypted_data)?; let part1 = encrypted_data[0..8].to_vec();
let part2 = encrypted_data[12..32].to_vec();
let part1 = String::from_str(&reply_data[0..8]).unwrap(); Ok((res[0].to_i64().unwrap() as u16, part1, part2))
let part2 = String::from_str(&reply_data[12..32]).unwrap();
Ok((res[0].to_i64().unwrap(), part1, part2))
} }
pub async fn track_uuid(&mut self, track: u16) -> Result<String, Box<dyn Error>> { pub async fn track_uuid(&mut self, track: u16) -> Result<String, Box<dyn Error>> {
@ -1644,22 +1654,20 @@ impl EKBOpenSource {
} }
} }
#[derive(Clone)]
pub struct MDTrack { pub struct MDTrack {
title: String, pub title: String,
format: WireFormat, pub format: WireFormat,
data: Vec<u8>, pub data: Vec<u8>,
chunk_size: i32, pub chunk_size: usize,
full_width_title: Option<String>, pub full_width_title: Option<String>,
encrypt_packets_iterator: EncryptPacketsIterator, pub encrypt_packets_iterator: Box<dyn Fn(DataEncryptorInput) -> UnboundedReceiver<(Vec<u8>, Vec<u8>, Vec<u8>)>>,
} }
#[derive(Clone)] pub struct DataEncryptorInput {
pub struct EncryptPacketsIterator { pub kek: [u8; 8],
kek: Vec<u8>, pub frame_size: usize,
frame_size: i32, pub data: Vec<u8>,
data: Vec<u8>, pub chunk_size: usize,
chunk_size: i32,
} }
impl MDTrack { impl MDTrack {
@ -1683,7 +1691,7 @@ impl MDTrack {
*FRAME_SIZE.get(&self.format).unwrap() *FRAME_SIZE.get(&self.format).unwrap()
} }
pub fn chunk_size(self) -> i32 { pub fn chunk_size(&self) -> usize {
self.chunk_size self.chunk_size
} }
@ -1696,25 +1704,34 @@ impl MDTrack {
len len
} }
pub fn content_id() -> [u8; 20] { pub fn content_id(&self) -> [u8; 20] {
[ [
0x01, 0x0f, 0x50, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0xa2, 0x8d, 0x3e, 0x1a, 0x01, 0x0f, 0x50, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0xa2, 0x8d, 0x3e, 0x1a,
0x3b, 0x0c, 0x44, 0xaf, 0x2f, 0xa0, 0x3b, 0x0c, 0x44, 0xaf, 0x2f, 0xa0,
] ]
} }
pub fn get_kek() -> [u8; 8] { pub fn get_kek(&self) -> [u8; 8] {
[0x14, 0xe3, 0x83, 0x4e, 0xe2, 0xd3, 0xcc, 0xa5] [0x14, 0xe3, 0x83, 0x4e, 0xe2, 0xd3, 0xcc, 0xa5]
} }
pub fn get_encrypting_iterator(&mut self) -> UnboundedReceiver<(Vec<u8>, Vec<u8>, Vec<u8>)>{
(self.encrypt_packets_iterator)(DataEncryptorInput {
kek: self.get_kek().clone(),
frame_size: self.frame_size(),
chunk_size: self.chunk_size(),
data: std::mem::take(&mut self.data)
})
}
} }
pub struct MDSession { pub struct MDSession<'a> {
pub md: NetMDInterface, pub md: &'a mut NetMDInterface,
pub ekb_object: EKBOpenSource, pub ekb_object: EKBOpenSource,
pub hex_session_key: Option<Vec<u8>>, pub hex_session_key: Option<Vec<u8>>,
} }
impl MDSession { impl<'a> MDSession<'a> {
pub async fn init(&mut self) -> Result<(), Box<dyn Error>>{ pub async fn init(&mut self) -> Result<(), Box<dyn Error>>{
self.md.enter_secure_session().await?; self.md.enter_secure_session().await?;
self.md.leaf_id().await?; self.md.leaf_id().await?;
@ -1739,4 +1756,40 @@ impl MDSession {
Ok(()) Ok(())
} }
pub async fn download_track<F>(&mut self, mut track: MDTrack, progress_callback: F, disc_format: Option<DiscFormat>) -> Result<(u16, Vec<u8>, Vec<u8>), Box<dyn Error>> where F: Fn(usize, usize) {
if let None = self.hex_session_key{
return Err("Cannot download a track using a non-init()'ed session!".into());
}
self.md.setup_download(&track.content_id(), &track.get_kek(), &self.hex_session_key.as_ref().unwrap()).await?;
let data_format = track.data_format();
let final_disc_format = disc_format.unwrap_or(*DISC_FOR_WIRE.get(&data_format).unwrap());
let (track_index, uuid, ccid) = self.md.send_track(
data_format as u8,
final_disc_format as u8,
track.frame_count() as u32,
track.total_size() as u32,
track.get_encrypting_iterator(),
self.hex_session_key.as_ref().unwrap().as_slice(),
progress_callback
).await?;
self.md.set_track_title(track_index, &track.title, false).await?;
if let Some(full_width) = track.full_width_title {
self.md.set_track_title(track_index, &full_width, true).await?;
}
self.md.commit_track(track_index, &self.hex_session_key.as_ref().unwrap()).await?;
Ok((track_index, uuid, ccid))
}
pub fn new(md: &'a mut NetMDInterface) -> Self {
MDSession {
md,
ekb_object: EKBOpenSource {},
hex_session_key: None,
}
}
} }

View file

@ -9,4 +9,5 @@ pub mod interface;
mod mappings; mod mappings;
mod query_utils; mod query_utils;
mod utils; mod utils;
mod commands; pub mod commands;
pub mod encryption;