diff --git a/dist/dumpkeys/DumpKeys.gm9 b/dist/dumpkeys/DumpKeys.gm9 new file mode 100644 index 000000000..b2991a27e --- /dev/null +++ b/dist/dumpkeys/DumpKeys.gm9 @@ -0,0 +1,291 @@ +set PREVIEW_MODE "Key Dumper\n \nWorking..." + +# Boot9 + +set BOOT9_BIN "M:/boot9.bin" + +fget $[BOOT9_BIN]@D9D0:10 KEYX_2C +set KEYX_2D $[KEYX_2C] +set KEYX_2E $[KEYX_2C] +set KEYX_2F $[KEYX_2C] +fget $[BOOT9_BIN]@D9E0:10 KEYX_30 +set KEYX_31 $[KEYX_30] +set KEYX_32 $[KEYX_30] +set KEYX_33 $[KEYX_30] +fget $[BOOT9_BIN]@D9F0:10 KEYX_34 +set KEYX_35 $[KEYX_34] +set KEYX_36 $[KEYX_34] +set KEYX_37 $[KEYX_34] +fget $[BOOT9_BIN]@DA00:10 KEYX_38 +set KEYX_39 $[KEYX_38] +set KEYX_3A $[KEYX_38] +set KEYX_3B $[KEYX_38] +fget $[BOOT9_BIN]@DA10:10 KEYX_3C +fget $[BOOT9_BIN]@DA20:10 KEYX_3D +fget $[BOOT9_BIN]@DA30:10 KEYX_3E +fget $[BOOT9_BIN]@DA40:10 KEYX_3F + +fget $[BOOT9_BIN]@DA50:10 KEYY_04 +fget $[BOOT9_BIN]@DA60:10 KEYY_05 +fget $[BOOT9_BIN]@DA70:10 KEYY_06 +fget $[BOOT9_BIN]@DA80:10 KEYY_07 +fget $[BOOT9_BIN]@DA90:10 KEYY_08 +fget $[BOOT9_BIN]@DAA0:10 KEYY_09 +fget $[BOOT9_BIN]@DAB0:10 KEYY_0A +fget $[BOOT9_BIN]@DAC0:10 KEYY_0B + +fget $[BOOT9_BIN]@DAD0:10 KEYN_0D +fget $[BOOT9_BIN]@DBA0:10 KEYN_2D +fget $[BOOT9_BIN]@DBB0:10 KEYN_32 +fget $[BOOT9_BIN]@DBC0:10 KEYN_36 +fget $[BOOT9_BIN]@DBD0:10 KEYN_38 + +# NATIVE_FIRM + +if chk $[ONTYPE] "N3DS" + if not find 1:/title/00040138/20000002/content/????????.app NATIVE_FIRM_APP + echo "New 3DS NATIVE_FIRM not found." + goto Exit + end + set EXPECTED_NATIVE_FIRM_VER "1F00" + set KEYY_2E_OFFSET "66504" + set KEYY_39_NFC_OFFSET "66524" + set COMMON_0_OFFSET "6CE51" + set COMMON_1_OFFSET "6CE65" + set COMMON_2_OFFSET "6CE79" + set COMMON_3_OFFSET "6CE8D" + set COMMON_4_OFFSET "6CEA1" + set COMMON_5_OFFSET "6CEB5" +else + if not find 1:/title/00040138/00000002/content/????????.app NATIVE_FIRM_APP + echo "Old 3DS NATIVE_FIRM not found." + goto Exit + end + set EXPECTED_NATIVE_FIRM_VER "1F00" + set KEYY_2E_OFFSET "66488" + set KEYY_39_NFC_OFFSET "664A8" + set COMMON_0_OFFSET "6CDC1" + set COMMON_1_OFFSET "6CDD5" + set COMMON_2_OFFSET "6CDE9" + set COMMON_3_OFFSET "6CDFD" + set COMMON_4_OFFSET "6CE11" + set COMMON_5_OFFSET "6CE25" +end +set NATIVE_FIRM_EXTHEADER "G:/extheader.bin" +set NATIVE_FIRM_ENCRYPTED "G:/exefs/.firm" +set NATIVE_FIRM_DECRYPTED "$[GM9OUT]/native.firm" +set PROCESS9_CODE "G:/0004013000003000.Process9/exefs/.code" + +imgmount $[NATIVE_FIRM_APP] + +fget $[NATIVE_FIRM_EXTHEADER]@E:2 NATIVE_FIRM_VER +if not chk $[NATIVE_FIRM_VER] $[EXPECTED_NATIVE_FIRM_VER] + echo "Unsupported NATIVE_FIRM version.\nThis script requires the latest 3DS firmware.\n\nExpected $[EXPECTED_NATIVE_FIRM_VER], got $[NATIVE_FIRM_VER]" + goto Exit +end + +cp -w $[NATIVE_FIRM_ENCRYPTED] $[NATIVE_FIRM_DECRYPTED] +decrypt $[NATIVE_FIRM_DECRYPTED] + +imgumount + +imgmount $[NATIVE_FIRM_DECRYPTED] + +fget $[PROCESS9_CODE]@$[KEYY_2E_OFFSET]:10 KEYY_2E +set KEYY_31 $[KEYY_2E] +set KEYY_39_DLP $[KEYY_2E] +fget $[PROCESS9_CODE]@$[KEYY_39_NFC_OFFSET]:10 KEYY_39_NFC +fget $[PROCESS9_CODE]@$[COMMON_0_OFFSET]:10 COMMON_0 +fget $[PROCESS9_CODE]@$[COMMON_1_OFFSET]:10 COMMON_1 +fget $[PROCESS9_CODE]@$[COMMON_2_OFFSET]:10 COMMON_2 +fget $[PROCESS9_CODE]@$[COMMON_3_OFFSET]:10 COMMON_3 +fget $[PROCESS9_CODE]@$[COMMON_4_OFFSET]:10 COMMON_4 +fget $[PROCESS9_CODE]@$[COMMON_5_OFFSET]:10 COMMON_5 + +imgumount +rm -o -s $[NATIVE_FIRM_DECRYPTED] + +# NFC + +if chk $[ONTYPE] "N3DS" + if not find 1:/title/00040130/20004002/content/????????.app NFC_APP + echo "New 3DS NFC not found." + goto Exit + end + set EXPECTED_NFC_VER "0700" + set NFC_PHRASE_0_OFFSET "355EE" + set NFC_SEED_0_OFFSET "355FC" + set NFC_HMAC_KEY_0_OFFSET "3560A" + set NFC_PHRASE_1_OFFSET "3561A" + set NFC_SEED_1_OFFSET "35628" + set NFC_HMAC_KEY_1_OFFSET "35638" + set NFC_IV_OFFSET "35648" +else + if not find 1:/title/00040130/00004002/content/????????.app NFC_APP + echo "Old 3DS NFC not found." + goto Exit + end + set EXPECTED_NFC_VER "0800" + set NFC_PHRASE_0_OFFSET "17382" + set NFC_SEED_0_OFFSET "17390" + set NFC_HMAC_KEY_0_OFFSET "1739E" + set NFC_PHRASE_1_OFFSET "173AE" + set NFC_SEED_1_OFFSET "173BC" + set NFC_HMAC_KEY_1_OFFSET "173CC" + set NFC_IV_OFFSET "173DC" +end +set NFC_EXTHEADER "G:/extheader.bin" +set NFC_CODE "$[GM9OUT]/nfc_code.bin" + +imgmount $[NFC_APP] + +fget $[NFC_EXTHEADER]@E:2 NFC_VER +if not chk $[NFC_VER] $[EXPECTED_NFC_VER] + echo "Unsupported NFC module version.\nThis script requires the latest 3DS firmware.\n\nExpected $[EXPECTED_NFC_VER], got $[NFC_VER]" + goto Exit +end + +imgumount + +extrcode $[NFC_APP] $[NFC_CODE] + +fget $[NFC_CODE]@$[NFC_PHRASE_0_OFFSET]:E NFC_PHRASE_0 +fget $[NFC_CODE]@$[NFC_SEED_0_OFFSET]:E NFC_SEED_0 +fget $[NFC_CODE]@$[NFC_HMAC_KEY_0_OFFSET]:10 NFC_HMAC_KEY_0 + +fget $[NFC_CODE]@$[NFC_PHRASE_1_OFFSET]:E NFC_PHRASE_1 +fget $[NFC_CODE]@$[NFC_SEED_1_OFFSET]:10 NFC_SEED_1 +fget $[NFC_CODE]@$[NFC_HMAC_KEY_1_OFFSET]:10 NFC_HMAC_KEY_1 + +fget $[NFC_CODE]@$[NFC_IV_OFFSET]:10 NFC_IV + +rm -o -s $[NFC_CODE] + +# GodMode9 Key Database + +set KEY_DB "V:/aeskeydb.bin" +set KEY_DB_18X "K:/slot0x18KeyX.ret.bin" +set KEY_DB_19X "K:/slot0x19KeyX.ret.bin" +set KEY_DB_1AX "K:/slot0x1AKeyX.ret.bin" +set KEY_DB_1BX "K:/slot0x1BKeyX.ret.bin" +set KEY_DB_1CX "K:/slot0x1CKeyX.ret.bin" +set KEY_DB_1DX "K:/slot0x1DKeyX.ret.bin" +set KEY_DB_1EX "K:/slot0x1EKeyX.ret.bin" +set KEY_DB_1FX "K:/slot0x1FKeyX.ret.bin" +set KEY_DB_25X "K:/slot0x25KeyX.ret.bin" +set KEY_DB_24Y "K:/slot0x24KeyY.bin" +set KEY_DB_2FY "K:/slot0x2FKeyY.ret.bin" + +imgmount $[KEY_DB] + +fget $[KEY_DB_18X]@0:10 KEYX_18 +fget $[KEY_DB_19X]@0:10 KEYX_19 +fget $[KEY_DB_1AX]@0:10 KEYX_1A +fget $[KEY_DB_1BX]@0:10 KEYX_1B +fget $[KEY_DB_1CX]@0:10 KEYX_1C +fget $[KEY_DB_1DX]@0:10 KEYX_1D +fget $[KEY_DB_1EX]@0:10 KEYX_1E +fget $[KEY_DB_1FX]@0:10 KEYX_1F +fget $[KEY_DB_25X]@0:10 KEYX_25 + +fget $[KEY_DB_24Y]@0:10 KEYY_24 +fget $[KEY_DB_2FY]@0:10 KEYY_2F + +imgumount + +# Write Keys To File + +set OUT "0:/gm9/aes_keys.txt" + +dumptxt $[OUT] "# KeyX" +dumptxt -p $[OUT] "" + +dumptxt -p $[OUT] "slot0x18KeyX=$[KEYX_18]" +dumptxt -p $[OUT] "slot0x19KeyX=$[KEYX_19]" +dumptxt -p $[OUT] "slot0x1AKeyX=$[KEYX_1A]" +dumptxt -p $[OUT] "slot0x1BKeyX=$[KEYX_1B]" +dumptxt -p $[OUT] "slot0x1CKeyX=$[KEYX_1C]" +dumptxt -p $[OUT] "slot0x1DKeyX=$[KEYX_1D]" +dumptxt -p $[OUT] "slot0x1EKeyX=$[KEYX_1E]" +dumptxt -p $[OUT] "slot0x1FKeyX=$[KEYX_1F]" +dumptxt -p $[OUT] "slot0x25KeyX=$[KEYX_25]" +dumptxt -p $[OUT] "slot0x2CKeyX=$[KEYX_2C]" +dumptxt -p $[OUT] "slot0x2DKeyX=$[KEYX_2D]" +dumptxt -p $[OUT] "slot0x2EKeyX=$[KEYX_2E]" +dumptxt -p $[OUT] "slot0x2FKeyX=$[KEYX_2F]" +dumptxt -p $[OUT] "slot0x30KeyX=$[KEYX_30]" +dumptxt -p $[OUT] "slot0x31KeyX=$[KEYX_31]" +dumptxt -p $[OUT] "slot0x32KeyX=$[KEYX_32]" +dumptxt -p $[OUT] "slot0x33KeyX=$[KEYX_33]" +dumptxt -p $[OUT] "slot0x34KeyX=$[KEYX_34]" +dumptxt -p $[OUT] "slot0x35KeyX=$[KEYX_35]" +dumptxt -p $[OUT] "slot0x36KeyX=$[KEYX_36]" +dumptxt -p $[OUT] "slot0x37KeyX=$[KEYX_37]" +dumptxt -p $[OUT] "slot0x38KeyX=$[KEYX_38]" +dumptxt -p $[OUT] "slot0x39KeyX=$[KEYX_39]" +dumptxt -p $[OUT] "slot0x3AKeyX=$[KEYX_3A]" +dumptxt -p $[OUT] "slot0x3BKeyX=$[KEYX_3B]" +dumptxt -p $[OUT] "slot0x3CKeyX=$[KEYX_3C]" +dumptxt -p $[OUT] "slot0x3DKeyX=$[KEYX_3D]" +dumptxt -p $[OUT] "slot0x3EKeyX=$[KEYX_3E]" +dumptxt -p $[OUT] "slot0x3FKeyX=$[KEYX_3F]" + +dumptxt -p $[OUT] "" +dumptxt -p $[OUT] "# KeyY" +dumptxt -p $[OUT] "" + +dumptxt -p $[OUT] "slot0x04KeyY=$[KEYY_04]" +dumptxt -p $[OUT] "slot0x05KeyY=$[KEYY_05]" +dumptxt -p $[OUT] "slot0x06KeyY=$[KEYY_06]" +dumptxt -p $[OUT] "slot0x07KeyY=$[KEYY_07]" +dumptxt -p $[OUT] "slot0x08KeyY=$[KEYY_08]" +dumptxt -p $[OUT] "slot0x09KeyY=$[KEYY_09]" +dumptxt -p $[OUT] "slot0x0AKeyY=$[KEYY_0A]" +dumptxt -p $[OUT] "slot0x0BKeyY=$[KEYY_0B]" +dumptxt -p $[OUT] "slot0x24KeyY=$[KEYY_24]" +dumptxt -p $[OUT] "slot0x2EKeyY=$[KEYY_2E]" +dumptxt -p $[OUT] "slot0x2FKeyY=$[KEYY_2F]" +dumptxt -p $[OUT] "slot0x31KeyY=$[KEYY_31]" + +dumptxt -p $[OUT] "" +dumptxt -p $[OUT] "# DLP/NFC KeyY (slot 0x39)" +dumptxt -p $[OUT] "" + +dumptxt -p $[OUT] "dlpKeyY=$[KEYY_39_DLP]" +dumptxt -p $[OUT] "nfcKeyY=$[KEYY_39_NFC]" + +dumptxt -p $[OUT] "" +dumptxt -p $[OUT] "# Ticket Common KeyY (slot 0x3D)" +dumptxt -p $[OUT] "" + +dumptxt -p $[OUT] "common0=$[COMMON_0]" +dumptxt -p $[OUT] "common1=$[COMMON_1]" +dumptxt -p $[OUT] "common2=$[COMMON_2]" +dumptxt -p $[OUT] "common3=$[COMMON_3]" +dumptxt -p $[OUT] "common4=$[COMMON_4]" +dumptxt -p $[OUT] "common5=$[COMMON_5]" + +dumptxt -p $[OUT] "" +dumptxt -p $[OUT] "# KeyN" +dumptxt -p $[OUT] "" + +dumptxt -p $[OUT] "slot0x0DKeyN=$[KEYN_0D]" +dumptxt -p $[OUT] "slot0x2DKeyN=$[KEYN_2D]" +dumptxt -p $[OUT] "slot0x32KeyN=$[KEYN_32]" +dumptxt -p $[OUT] "slot0x36KeyN=$[KEYN_36]" +dumptxt -p $[OUT] "slot0x38KeyN=$[KEYN_38]" + +dumptxt -p $[OUT] "" +dumptxt -p $[OUT] "# NFC Secrets" +dumptxt -p $[OUT] "" + +dumptxt -p $[OUT] "nfcSecret0Phrase=$[NFC_PHRASE_0]" +dumptxt -p $[OUT] "nfcSecret0Seed=$[NFC_SEED_0]" +dumptxt -p $[OUT] "nfcSecret0HmacKey=$[NFC_HMAC_KEY_0]" +dumptxt -p $[OUT] "nfcSecret1Phrase=$[NFC_PHRASE_1]" +dumptxt -p $[OUT] "nfcSecret1Seed=$[NFC_SEED_1]" +dumptxt -p $[OUT] "nfcSecret1HmacKey=$[NFC_HMAC_KEY_1]" +dumptxt -p $[OUT] "nfcIv=$[NFC_IV]" + +@Exit + diff --git a/dist/dumpkeys/README.md b/dist/dumpkeys/README.md new file mode 100644 index 000000000..4416b178b --- /dev/null +++ b/dist/dumpkeys/README.md @@ -0,0 +1,10 @@ +# DumpKeys + +This is a GodMode9 script that dumps all the keys and other related secrets that Citra needs from a real 3DS. + +Usage: +1. Copy "DumpKeys.gm9" into the "gm9/scripts/" directory on your SD card. +2. Launch GodMode9, press the HOME button, select Scripts, and select "DumpKeys" from the list of scripts that appears. +3. Wait for the script to complete and return you to the GodMode9 main menu. +4. Power off your system and copy the "gm9/aes_keys.txt" file off of your SD card into "(Citra directory)/sysdata/". + diff --git a/src/core/hle/service/ps/ps_ps.cpp b/src/core/hle/service/ps/ps_ps.cpp index db61fdcd1..524fdb022 100644 --- a/src/core/hle/service/ps/ps_ps.cpp +++ b/src/core/hle/service/ps/ps_ps.cpp @@ -31,13 +31,12 @@ constexpr std::array KeyTypes{{ HW::AES::APTWrap, HW::AES::BOSSDataKey, 0x32, // unknown - HW::AES::DLPDataKey, + HW::AES::DLPNFCDataKey, HW::AES::CECDDataKey, 0, // invalid HW::AES::FRDKey, // Note: According to 3dbrew the KeyY is overridden by Process9 when using this key type. - // TODO: implement this behaviour? - HW::AES::NFCKey, + HW::AES::DLPNFCDataKey, }}; void PS_PS::EncryptDecryptAes(Kernel::HLERequestContext& ctx) { @@ -60,6 +59,12 @@ void PS_PS::EncryptDecryptAes(Kernel::HLERequestContext& ctx) { // and encrypted data is actually returned, but the key used is unknown. ASSERT_MSG(key_type != 7 && key_type < 10, "Key type is invalid"); + if (key_type == 0x5) { + HW::AES::SelectDlpNfcKeyYIndex(HW::AES::DlpNfcKeyY::Dlp); + } else if (key_type == 0x9) { + HW::AES::SelectDlpNfcKeyYIndex(HW::AES::DlpNfcKeyY::Nfc); + } + if (!HW::AES::IsNormalKeyAvailable(KeyTypes[key_type])) { LOG_ERROR(Service_PS, "Key 0x{:2X} is not available, encryption/decryption will not be correct", diff --git a/src/core/hw/aes/key.cpp b/src/core/hw/aes/key.cpp index c8dc0adcf..95e09ac86 100644 --- a/src/core/hw/aes/key.cpp +++ b/src/core/hw/aes/key.cpp @@ -55,6 +55,15 @@ AESKey HexToKey(const std::string& hex) { return key; } +std::vector HexToVector(const std::string& hex) { + std::vector vector(hex.size() / 2); + for (std::size_t i = 0; i < vector.size(); ++i) { + vector[i] = static_cast(std::stoi(hex.substr(i * 2, 2), nullptr, 16)); + } + + return vector; +} + struct KeySlot { std::optional x; std::optional y; @@ -91,6 +100,9 @@ struct KeySlot { std::array key_slots; std::array, MaxCommonKeySlot> common_key_y_slots; +std::array, NumDlpNfcKeyYs> dlp_nfc_key_y_slots; +std::array nfc_secrets; +AESIV nfc_iv; enum class FirmwareType : u32 { ARM9 = 0, // uses NDMA @@ -440,6 +452,12 @@ void LoadPresetKeys() { while (!file.eof()) { std::string line; std::getline(file, line); + + // Ignore empty or commented lines. + if (line.empty() || line.starts_with("#")) { + continue; + } + std::vector parts; Common::SplitString(line, '=', parts); if (parts.size() != 2) { @@ -448,6 +466,24 @@ void LoadPresetKeys() { } const std::string& name = parts[0]; + + std::size_t nfc_secret_index; + if (std::sscanf(name.c_str(), "nfcSecret%zd", &nfc_secret_index) == 1) { + auto value = HexToVector(parts[1]); + if (nfc_secret_index >= nfc_secrets.size()) { + LOG_ERROR(HW_AES, "Invalid NFC secret index {}", nfc_secret_index); + } else if (name.ends_with("Phrase")) { + nfc_secrets[nfc_secret_index].phrase = value; + } else if (name.ends_with("Seed")) { + nfc_secrets[nfc_secret_index].seed = value; + } else if (name.ends_with("HmacKey")) { + nfc_secrets[nfc_secret_index].hmac_key = value; + } else { + LOG_ERROR(HW_AES, "Invalid NFC secret {}", name); + } + continue; + } + AESKey key; try { key = HexToKey(parts[1]); @@ -466,6 +502,21 @@ void LoadPresetKeys() { continue; } + if (name == "dlpKeyY") { + dlp_nfc_key_y_slots[DlpNfcKeyY::Dlp] = key; + continue; + } + + if (name == "nfcKeyY") { + dlp_nfc_key_y_slots[DlpNfcKeyY::Nfc] = key; + continue; + } + + if (name == "nfcIv") { + nfc_iv = key; + continue; + } + std::size_t slot_id; char key_type; if (std::sscanf(name.c_str(), "slot0x%zXKey%c", &slot_id, &key_type) != 2) { @@ -534,4 +585,16 @@ void SelectCommonKeyIndex(u8 index) { key_slots[KeySlotID::TicketCommonKey].SetKeyY(common_key_y_slots.at(index)); } +void SelectDlpNfcKeyYIndex(u8 index) { + key_slots[KeySlotID::DLPNFCDataKey].SetKeyY(dlp_nfc_key_y_slots.at(index)); +} + +const NfcSecret& GetNfcSecret(u8 index) { + return nfc_secrets[index]; +} + +const AESIV& GetNfcIv() { + return nfc_iv; +} + } // namespace HW::AES diff --git a/src/core/hw/aes/key.h b/src/core/hw/aes/key.h index fa21ea783..33232dceb 100644 --- a/src/core/hw/aes/key.h +++ b/src/core/hw/aes/key.h @@ -6,6 +6,7 @@ #include #include +#include #include "common/common_types.h" namespace HW::AES { @@ -27,8 +28,8 @@ enum KeySlotID : std::size_t { // AES Keyslot used to encrypt the BOSS container data. BOSSDataKey = 0x38, - // AES Keyslot used to calculate DLP data frame checksum. - DLPDataKey = 0x39, + // AES Keyslot used to calculate DLP data frame checksum and encrypt Amiibo key data. + DLPNFCDataKey = 0x39, // AES Keyslot used to generate the StreetPass CCMP key. CECDDataKey = 0x2E, @@ -36,9 +37,6 @@ enum KeySlotID : std::size_t { // AES Keyslot used by the friends module. FRDKey = 0x36, - // AES Keyslot used by the NFC module. - NFCKey = 0x39, - // AES keyslot used for APT:Wrap/Unwrap functions APTWrap = 0x31, @@ -48,11 +46,28 @@ enum KeySlotID : std::size_t { MaxKeySlotID = 0x40, }; +enum DlpNfcKeyY : std::size_t { + // Download Play KeyY + Dlp = 0, + + // NFC (Amiibo) KeyY + Nfc = 1 +}; + +struct NfcSecret { + std::vector phrase; + std::vector seed; + std::vector hmac_key; +}; + constexpr std::size_t MaxCommonKeySlot = 6; +constexpr std::size_t NumDlpNfcKeyYs = 2; +constexpr std::size_t NumNfcSecrets = 2; constexpr std::size_t AES_BLOCK_SIZE = 16; using AESKey = std::array; +using AESIV = std::array; void InitKeys(bool force = false); @@ -65,5 +80,9 @@ bool IsNormalKeyAvailable(std::size_t slot_id); AESKey GetNormalKey(std::size_t slot_id); void SelectCommonKeyIndex(u8 index); +void SelectDlpNfcKeyYIndex(u8 index); + +const NfcSecret& GetNfcSecret(u8 index); +const AESIV& GetNfcIv(); } // namespace HW::AES