ctr_cart 0.4.0

3DS file header library and utilities.
Documentation
// SPDX-License-Identifier: LGPL-2.1-or-later OR GPL-2.0-or-later OR MPL-2.0
// SPDX-FileCopyrightText: 2024 Gabriel Marcano <gabemarcano@yahoo.com>

use crate::ncch::NCCH;
use ctr::Ctr128BE;
use ctr::cipher::KeyIvInit;

/// Scrambles the given key X and key Y into a normal key.
///
/// This function returns the key as a u128 in the current system's preferred endianness. For use
/// in AES, it needs to be converted to Big Endian.
const fn keyscrambler(key_x: u128, key_y: u128) -> u128 {
    // Based on a post by Myria, the math operates with Big Endian values, so the final result
    // should be whatever the result of the math is, in Big Endian
    const CONSTANT: u128 = 0x1FF9_E9AA_C5FE_0408_0245_91DC_5D52_768A_u128;
    ((key_x.rotate_left(2) ^ key_y).wrapping_add(CONSTANT)).rotate_left(87)
}

pub type AESCtr = Ctr128BE<aes::Aes128>;

/// This key is used to decrypt `ExeFs` info regions. Since we just want to read the icon, that's all
/// we need.
const INFO_KEYX: u128 = 0xB98E_95CE_CA3E_4D17_1F76_A94D_E934_C053_u128;

/// Computes the IV for the `ExeFs` partition.
///
/// This function returns the key as a u128 in the current system's preferred endianness. For use
/// in AES, it needs to be converted to Big Endian.
fn compute_exefs_iv(partition_id: u64, offset: u32) -> u128 {
    ((u128::from(partition_id) << 64) | (2u128 << 56)) + u128::from(offset)
}

impl NCCH {
    #[must_use]
    #[allow(clippy::missing_panics_doc)]
    pub fn exefs_ctr(&self) -> AESCtr {
        // by types this cannot panic-- this signature has 0x100 elements.
        let key_y = u128::from_be_bytes(self.signature[0..0x10].try_into().unwrap());
        let key = keyscrambler(INFO_KEYX, key_y);
        let iv = compute_exefs_iv(self.partition_title_id, 0);
        AESCtr::new((&key.to_be_bytes()).into(), (&iv.to_be_bytes()).into())
    }
}

#[cfg(test)]
mod tests {
    use crate::crypto::keyscrambler;

    #[test]
    fn test() {
        assert_eq!(
            0xEE2EA93B480FFCF4D562FF02040122C8u128,
            keyscrambler(
                0x00000000000000000000000000000001u128,
                0x00000000000000000000000000000002u128
            )
        );
    }
}