1use std::collections::HashMap;
2use std::fmt;
3use std::marker::PhantomData;
4use std::mem;
5use std::num;
6use std::str::FromStr;
7use std::sync::OnceLock;
8
9use crate::string::SizedCString;
10use crate::{CytrynaError, CytrynaResult, FromBytes};
11
12use sha2::{Digest, Sha256};
13use thiserror::Error;
14
15pub mod aes128_ctr {
16 pub use aes::cipher::block_padding::NoPadding;
17 pub use aes::cipher::BlockDecryptMut;
18 pub use aes::cipher::KeyIvInit;
19 pub use aes::cipher::StreamCipher;
20 pub type Aes128CbcDec = cbc::Decryptor<aes::Aes128>;
21 pub type Aes128CtrDec = ctr::Ctr128BE<aes::Aes128>;
22}
23
24static KEY_BAG: OnceLock<KeyBag> = OnceLock::new();
25
26#[derive(Clone, Debug)]
28pub struct KeyBag {
29 keys: HashMap<KeyIndex, [u8; 0x10]>,
30}
31
32impl KeyBag {
33 #[must_use]
35 pub fn new() -> Self {
36 Self {
37 keys: HashMap::new(),
38 }
39 }
40 #[must_use]
45 pub fn from_string(string: &str) -> CytrynaResult<Self> {
46 let mut this = Self::new();
47 for line in string.lines() {
48 if line.starts_with('#') {
49 continue;
50 }
51 let line = line.to_lowercase();
52 let Some((left, right)) = line.split_once('=') else {
53 continue;
54 };
55 let idx: KeyIndex = left.parse()?;
56 let keyvec = hex::decode(right)?;
57 if keyvec.len() != 0x10 {
58 return Err(CytrynaError::InvalidLength {
59 what: "key",
60 actual: keyvec.len(),
61 expected: 0x10,
62 });
63 }
64 let key: [u8; 0x10] = keyvec.try_into().unwrap();
65
66 this.set_key(idx, key);
67 }
68 Ok(this)
69 }
70 pub fn set_key(&mut self, idx: KeyIndex, key: [u8; 0x10]) {
72 self.keys.insert(idx, key);
73 }
74 pub fn finalize(self) {
76 let _ = KEY_BAG.set(self);
77 }
78 pub fn get_key(&self, idx: KeyIndex) -> CytrynaResult<&[u8; 0x10]> {
80 self.keys.get(&idx).ok_or(CytrynaError::MissingKey(idx))
81 }
82 pub fn global() -> CytrynaResult<&'static Self> {
84 KEY_BAG.get().ok_or(CytrynaError::NoKeyBag)
85 }
86}
87
88#[must_use]
90pub fn keygen(x: [u8; 0x10], y: [u8; 0x10]) -> CytrynaResult<[u8; 0x10]> {
91 let x = u128::from_be_bytes(x);
92 let y = u128::from_be_bytes(y);
93 let gen = u128::from_be_bytes(*KeyBag::global()?.get_key(KeyIndex::Generator)?);
94
95 Ok(((x.rotate_left(2) ^ y).wrapping_add(gen))
96 .rotate_right(41)
97 .to_be_bytes())
98}
99
100#[derive(Debug, Clone, Eq, Hash, PartialEq)]
102pub enum KeyIndex {
103 Generator,
105 Slot(u8, KeyType),
107 Common(u8),
109 CommonN(u8),
111}
112
113impl fmt::Display for KeyIndex {
114 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
115 let string = match self {
116 Self::Generator => "generator".to_string(),
117 Self::Slot(num, ty) => format!("slot0x{num:X}Key{ty}"),
118 Self::Common(num) => format!("common{num}"),
119 Self::CommonN(num) => format!("common{num}N"),
120 };
121 f.write_str(&string)
122 }
123}
124
125#[derive(Error, Debug)]
127pub enum KeyIndexParseError {
128 #[error("Failed to parse a hex number")]
129 NumberParseError(#[from] num::ParseIntError),
130 #[error("Invalid key type \"{0}\"")]
131 InvalidKeyType(String),
132 #[error("Invalid X/Y/Z key type \"{0}\"")]
133 InvalidKeyXYNType(String),
134}
135
136impl FromStr for KeyIndex {
137 type Err = KeyIndexParseError;
138
139 fn from_str(from: &str) -> Result<Self, KeyIndexParseError> {
140 if from == "generator" {
141 Ok(Self::Generator)
142 } else if from.starts_with("slot") {
143 let from = from.trim_start_matches("slot").trim_start_matches("0x");
144 let num = u8::from_str_radix(&from[..2], 16)?;
145 let keytype = match &from[2..] {
146 "keyx" => KeyType::X,
147 "keyy" => KeyType::Y,
148 "keyn" => KeyType::N,
149 _ => return Err(KeyIndexParseError::InvalidKeyXYNType(from[2..].to_string())),
150 };
151 Ok(Self::Slot(num, keytype))
152 } else if from.starts_with("common") {
153 let from = from.trim_start_matches("common");
154 let num = u8::from_str_radix(from.get(0..1).unwrap(), 16)?;
155 let has_n = from.ends_with('n');
156 if has_n {
157 Ok(Self::Common(num))
158 } else {
159 Ok(Self::CommonN(num))
160 }
161 } else {
162 Err(KeyIndexParseError::InvalidKeyType(from.to_string()))
163 }
164 }
165}
166
167#[derive(Debug, Clone, Eq, Hash, PartialEq)]
169pub enum KeyType {
170 X,
172 Y,
174 N,
176}
177
178impl fmt::Display for KeyType {
179 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
180 let string = match self {
181 Self::X => "X",
182 Self::Y => "Y",
183 Self::N => "N",
184 };
185 f.write_str(string)
186 }
187}
188
189pub fn sha256(data: &[u8]) -> [u8; 0x20] {
191 let mut hasher = Sha256::new();
192 hasher.update(data);
193 hasher.finalize().into()
194}
195
196#[repr(C)]
198pub struct SignedDataInner<T: ?Sized + FromBytes + fmt::Debug, S: Signature> {
199 _unused: PhantomData<T>,
200 sig_type: SignatureType,
201 signature: S,
202 sig_issuer: SizedCString<0x40>,
203 data: [u8],
204}
205
206impl<T: ?Sized + FromBytes + fmt::Debug, S: Signature> SignedDataInner<T, S> {
207 #[must_use]
209 pub fn data(&self) -> &T {
210 T::cast(&self.data)
211 }
212}
213
214impl<T, S> fmt::Debug for SignedDataInner<T, S>
215where
216 T: ?Sized + FromBytes + fmt::Debug,
217 S: Signature,
218{
219 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
220 f.debug_struct("SignedDataInner")
221 .field("sig_type", &self.sig_type)
222 .field("signature", &"skipped")
223 .field("sig_issuer", &self.sig_issuer)
224 .field("data", &T::cast(&self.data))
225 .finish()
226 }
227}
228
229pub enum SignedData<'a, T: ?Sized + FromBytes + fmt::Debug> {
231 Rsa4096Sha256(&'a SignedDataInner<T, Rsa4096Sha256>),
232 Rsa2048Sha256(&'a SignedDataInner<T, Rsa2048Sha256>),
233 EcdsaSha256(&'a SignedDataInner<T, EcdsaSha256>),
234}
235
236impl<T> fmt::Debug for SignedData<'_, T>
237where
238 T: ?Sized + FromBytes + fmt::Debug,
239{
240 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
241 match self {
242 Self::Rsa4096Sha256(inner) => f.debug_tuple("Rsa4096Sha256").field(inner).finish(),
243 Self::Rsa2048Sha256(inner) => f.debug_tuple("Rsa2048Sha256").field(inner).finish(),
244 Self::EcdsaSha256(inner) => f.debug_tuple("EcdsaSha256").field(inner).finish(),
245 }
246 }
247}
248
249impl<T: ?Sized + FromBytes + fmt::Debug> SignedData<'_, T> {
250 #[must_use]
251 pub fn from_bytes(bytes: &[u8]) -> CytrynaResult<SignedData<T>> {
252 unsafe {
253 if bytes[0] != 0x0
254 || bytes[1] != 0x1
255 || bytes[2] != 0x0
256 || bytes[3] >= 0x06
257 || bytes[3] <= 0x02
258 {
259 return Err(CytrynaError::InvalidMagic);
260 }
261
262 let sig_size = match bytes[3] {
263 0x03 => mem::size_of::<Rsa4096Sha256>(),
264 0x04 => mem::size_of::<Rsa2048Sha256>(),
265 0x05 => mem::size_of::<EcdsaSha256>(),
266 _ => unreachable!("Already checked if it's in range"),
267 };
268 let offset = sig_size + mem::size_of::<SignatureType>() + 0x40;
269
270 T::bytes_ok(&bytes[offset..])?;
271
272 match bytes[3] {
273 0x03 => Ok(SignedData::Rsa4096Sha256(mem::transmute(bytes))),
274 0x04 => Ok(SignedData::Rsa2048Sha256(mem::transmute(bytes))),
275 0x05 => Ok(SignedData::EcdsaSha256(mem::transmute(bytes))),
276 _ => unreachable!("Already checked if it's in range"),
277 }
278 }
279 }
280 #[must_use]
282 pub fn data(&self) -> &T {
283 match self {
284 Self::Rsa4096Sha256(inner) => T::cast(&inner.data),
285 Self::Rsa2048Sha256(inner) => T::cast(&inner.data),
286 Self::EcdsaSha256(inner) => T::cast(&inner.data),
287 }
288 }
289}
290
291#[derive(Copy, Clone, Debug)]
293#[repr(u32)]
294pub enum SignatureType {
295 Rsa4096Sha256 = 0x03000100,
296 Rsa2048Sha256 = 0x04000100,
297 EcdsaSha256 = 0x05000100,
298}
299
300pub trait Signature: sealed_impl::Sealed {}
301
302#[repr(C, packed)]
304pub struct Rsa4096Sha256 {
305 sig: [u8; 0x200],
306 pad: [u8; 0x3c],
307}
308impl Signature for Rsa4096Sha256 {}
309
310#[repr(C, packed)]
312pub struct Rsa2048Sha256 {
313 sig: [u8; 0x100],
314 pad: [u8; 0x3c],
315}
316impl Signature for Rsa2048Sha256 {}
317
318#[repr(C, packed)]
320pub struct EcdsaSha256 {
321 sig: [u8; 0x3c],
322 pad: [u8; 0x40],
323}
324impl Signature for EcdsaSha256 {}
325
326mod sealed_impl {
327 pub trait Sealed {}
328 impl Sealed for super::Rsa4096Sha256 {}
329 impl Sealed for super::Rsa2048Sha256 {}
330 impl Sealed for super::EcdsaSha256 {}
331}
332
333#[cfg(test)]
334mod tests {
335 use super::{KeyBag, KeyIndex};
336 #[test]
337 fn test_keygen() {
338 const RANDOM_GENERATOR: [u8; 0x10] = [
340 0x12, 0x59, 0x9a, 0x14, 0xff, 0x66, 0xda, 0x9f, 0x65, 0xc1, 0x3e, 0xad, 0x30, 0x50,
341 0x15, 0xc7,
342 ];
343 const RANDOM_X: [u8; 0x10] = [
344 0xfa, 0xfe, 0x20, 0x7b, 0xb2, 0x3c, 0xa4, 0x30, 0x16, 0x2a, 0x65, 0xf6, 0xd3, 0xff,
345 0x50, 0x40,
346 ];
347 const RANDOM_Y: [u8; 0x10] = [
348 0x82, 0x48, 0x62, 0xde, 0xd5, 0xc6, 0xd5, 0x99, 0x23, 0x05, 0x19, 0xf5, 0x2d, 0x27,
349 0x56, 0xa8,
350 ];
351 const REFERENCE_KEY: [u8; 0x10] = [
352 0x6d, 0xc9, 0x95, 0x16, 0xb9, 0x3e, 0x05, 0x3e, 0xa2, 0x8e, 0x4d, 0x8f, 0xfc, 0x70,
353 0xb6, 0xe6,
354 ];
355
356 let mut bag = KeyBag::new();
357 bag.set_key(KeyIndex::Generator, RANDOM_GENERATOR);
358 bag.finalize();
359
360 assert_eq!(super::keygen(RANDOM_X, RANDOM_Y).unwrap(), REFERENCE_KEY);
361 }
362}