|
|
@ -1,13 +1,9 @@ |
|
|
|
//! Process Services (PS) module. This is used for miscellaneous utility tasks, but
|
|
|
|
//! Process Services (PS) module. This is used for miscellaneous utility tasks, but
|
|
|
|
//! is particularly important because it is used to generate random data, which
|
|
|
|
//! is particularly important because it is used to generate random data, which
|
|
|
|
//! is required for common things like [`HashMap`](std::collections::HashMap).
|
|
|
|
//! is required for common things like [`HashMap`](std::collections::HashMap).
|
|
|
|
|
|
|
|
//! As such, it is initialized by default in `ctru::init` instead of having a safety handler
|
|
|
|
//! See also <https://www.3dbrew.org/wiki/Process_Services>
|
|
|
|
//! See also <https://www.3dbrew.org/wiki/Process_Services>
|
|
|
|
|
|
|
|
|
|
|
|
/// PS handle. This must not be dropped in order for random generation
|
|
|
|
|
|
|
|
/// to work (in most cases, the lifetime of an application).
|
|
|
|
|
|
|
|
#[non_exhaustive] |
|
|
|
|
|
|
|
pub struct Ps; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[repr(u32)] |
|
|
|
#[repr(u32)] |
|
|
|
pub enum AESAlgorithm { |
|
|
|
pub enum AESAlgorithm { |
|
|
|
CbcEnc, |
|
|
|
CbcEnc, |
|
|
@ -32,55 +28,34 @@ pub enum AESKeyType { |
|
|
|
Keyslot39Nfc, |
|
|
|
Keyslot39Nfc, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
impl Ps { |
|
|
|
pub fn local_friend_code_seed() -> crate::Result<u64> { |
|
|
|
/// Initialize the PS module.
|
|
|
|
let mut seed: u64 = 0; |
|
|
|
pub fn init() -> crate::Result<Self> { |
|
|
|
|
|
|
|
let r = unsafe { ctru_sys::psInit() }; |
|
|
|
|
|
|
|
if r < 0 { |
|
|
|
|
|
|
|
Err(r.into()) |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
Ok(Self) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub fn local_friend_code_seed(&self) -> crate::Result<u64> { |
|
|
|
|
|
|
|
let mut seed: u64 = 0; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let r = unsafe { ctru_sys::PS_GetLocalFriendCodeSeed(&mut seed) }; |
|
|
|
let r = unsafe { ctru_sys::PS_GetLocalFriendCodeSeed(&mut seed) }; |
|
|
|
if r < 0 { |
|
|
|
if r < 0 { |
|
|
|
Err(r.into()) |
|
|
|
Err(r.into()) |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
Ok(seed) |
|
|
|
Ok(seed) |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pub fn device_id(&self) -> crate::Result<u32> { |
|
|
|
pub fn device_id() -> crate::Result<u32> { |
|
|
|
let mut id: u32 = 0; |
|
|
|
let mut id: u32 = 0; |
|
|
|
|
|
|
|
|
|
|
|
let r = unsafe { ctru_sys::PS_GetDeviceId(&mut id) }; |
|
|
|
|
|
|
|
if r < 0 { |
|
|
|
|
|
|
|
Err(r.into()) |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
Ok(id) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub fn generate_random_bytes(&self, out: &mut [u8]) -> crate::Result<()> { |
|
|
|
let r = unsafe { ctru_sys::PS_GetDeviceId(&mut id) }; |
|
|
|
let r = |
|
|
|
if r < 0 { |
|
|
|
unsafe { ctru_sys::PS_GenerateRandomBytes(out as *mut _ as *mut _, out.len() as u32) }; |
|
|
|
Err(r.into()) |
|
|
|
if r < 0 { |
|
|
|
} else { |
|
|
|
Err(r.into()) |
|
|
|
Ok(id) |
|
|
|
} else { |
|
|
|
|
|
|
|
Ok(()) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
impl Drop for Ps { |
|
|
|
pub fn generate_random_bytes(out: &mut [u8]) -> crate::Result<()> { |
|
|
|
fn drop(&mut self) { |
|
|
|
let r = unsafe { ctru_sys::PS_GenerateRandomBytes(out as *mut _ as *mut _, out.len() as u32) }; |
|
|
|
unsafe { |
|
|
|
if r < 0 { |
|
|
|
ctru_sys::psExit(); |
|
|
|
Err(r.into()) |
|
|
|
} |
|
|
|
} else { |
|
|
|
|
|
|
|
Ok(()) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -92,8 +67,6 @@ mod tests { |
|
|
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
#[test] |
|
|
|
fn construct_hash_map() { |
|
|
|
fn construct_hash_map() { |
|
|
|
let _ps = Ps::init().unwrap(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let mut input = vec![ |
|
|
|
let mut input = vec![ |
|
|
|
(1_i32, String::from("123")), |
|
|
|
(1_i32, String::from("123")), |
|
|
|
(2, String::from("2")), |
|
|
|
(2, String::from("2")), |
|
|
@ -108,34 +81,4 @@ mod tests { |
|
|
|
|
|
|
|
|
|
|
|
assert_eq!(input, actual); |
|
|
|
assert_eq!(input, actual); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
|
|
|
fn construct_hash_map_no_rand() { |
|
|
|
|
|
|
|
// Without initializing PS, we can't use `libc::getrandom` and constructing
|
|
|
|
|
|
|
|
// a HashMap panics at runtime.
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// If any test case successfully creates a HashMap before this test,
|
|
|
|
|
|
|
|
// the thread-local RandomState in std will be initialized. We spawn
|
|
|
|
|
|
|
|
// a new thread to actually create the hash map, since even in multi-threaded
|
|
|
|
|
|
|
|
// test environment there's a chance this test wouldn't panic because
|
|
|
|
|
|
|
|
// some other test case ran before it.
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// One downside of this approach is that the panic handler for the panicking
|
|
|
|
|
|
|
|
// thread prints to the console, which is not captured by the default test
|
|
|
|
|
|
|
|
// harness and prints even when the test passes.
|
|
|
|
|
|
|
|
crate::thread::Builder::new() |
|
|
|
|
|
|
|
.stack_size(0x20_0000) |
|
|
|
|
|
|
|
.spawn(|| { |
|
|
|
|
|
|
|
let map: HashMap<i32, String> = HashMap::from_iter([ |
|
|
|
|
|
|
|
(1_i32, String::from("123")), |
|
|
|
|
|
|
|
(2, String::from("2")), |
|
|
|
|
|
|
|
(6, String::from("six")), |
|
|
|
|
|
|
|
]); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
dbg!(map); |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
.unwrap() |
|
|
|
|
|
|
|
.join() |
|
|
|
|
|
|
|
.expect_err("should have panicked"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|