-
-
Save Popax21/a85f4dce14bd486b924be7c1e097e238 to your computer and use it in GitHub Desktop.
A script to reencrypt / fix 3DS ROM title keys
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python3 | |
import sys, secrets | |
from Crypto.Cipher import AES | |
if len(sys.argv) < 2: | |
print(f"Usage: {sys.argv[0]} <3ds ROM file>") | |
sys.exit(1) | |
#Load the ROM file into memory | |
rom = None | |
with open(sys.argv[1], "rb") as f: rom = f.read() | |
#Get the current crypto type, key seed, encrypted title key, MAC and nonce | |
crypto_type = (rom[0x207] >> 6) & 0b11 #CardInfo.Flag.CryptoType | |
key_seed = rom[0x1000:0x1010] #InitialData.Seed | |
enc_title_key = rom[0x1010:0x1020] #InitialData.TitleKey | |
title_key_mac = rom[0x1020:0x1030] #InitialData.Mac | |
title_key_nonce = rom[0x1030:0x103c] #InitialData.Nonce | |
print("ROM Info:") | |
print(f" - Crypto Type: {crypto_type:x} [%s]" % ("PROD" if crypto_type != 3 else "DEV")) | |
print(f" - Key Seed: {key_seed.hex()}") | |
print(f" - Encrypted Title Key: {enc_title_key.hex()}") | |
print(f" - Title Key MAC: {title_key_mac.hex()}") | |
print(f" - Title Key Nonce: {title_key_nonce.hex()}") | |
print() | |
#Derive production title key encryption key | |
#Algorithm taken from libnintendo-n3ds / CTRtool | |
def gen_key(keyX, keyY): | |
KEYGEN_CONST = int.from_bytes(bytes.fromhex("1FF9E9AAC5FE0408024591DC5D52768A"), "big") | |
def lrot(v, b): return ((v << b) | (v >> (128 - b))) & ((1 << 128) - 1) | |
def rrot(v, b): return ((v >> b) | (v << (128 - b))) & ((1 << 128) - 1) | |
assert len(keyX) == 16 and len(keyY) == 16 | |
keyX, keyY = int.from_bytes(keyX, "big"), int.from_bytes(keyY, "big") | |
key = rrot((lrot(keyX, 2) ^ keyY) + KEYGEN_CONST, 41) | |
return key.to_bytes(16, "big") | |
BOOTROM_INITIAL_DATA_KEY = bytes.fromhex("B529221CDDB5DB5A1BF26EFF2041E875") | |
prod_title_key_enc_key = gen_key(BOOTROM_INITIAL_DATA_KEY, key_seed) | |
#Determine the key used for the encrypted title key | |
title_key_enc_key = prod_title_key_enc_key if crypto_type != 3 else bytes(16) | |
print("Determined Title Key Encryption Keys:") | |
print(f" - Derived Production Title Key Encryption Key: {prod_title_key_enc_key.hex()}") | |
print(f" - Used Title Key Encryption Key for decryption: {title_key_enc_key.hex()} [%s]" % ("PROD" if crypto_type != 3 else "DEV")) | |
print() | |
#Attempt to decrypt the title key | |
title_key = None | |
print("Attempting decryption of title key...") | |
try: | |
title_key = AES.new(title_key_enc_key, AES.MODE_CCM, title_key_nonce).decrypt_and_verify(enc_title_key, title_key_mac) | |
print(f" - Decrypted Title Key: {title_key.hex()}") | |
if crypto_type != 3: | |
print("Title Key already is a valid production one - exiting...") | |
sys.exit(0) | |
else: | |
crypto_type = 3 | |
print("Tile Key is valid, but a development one - reencrypting as production key...") | |
except ValueError: | |
print(" - MAC check failed - reencryping key as production key...") | |
#Check for development card title key | |
title_key = rom[0x1400:0x1410] #TitleKeyData | |
if all(b == 0xff for b in title_key): | |
print(" - utilizing randomly generated title key") | |
title_key = secrets.token_bytes(16) | |
else: | |
print(" - utilizing development card title key") | |
print() | |
#Reencrypt the title key | |
print(f"Reencrypting Title Key: {title_key.hex()}...") | |
enc_title_key, title_key_mac = AES.new(prod_title_key_enc_key, AES.MODE_CCM, title_key_nonce).encrypt_and_digest(title_key) | |
print(f" - Encrypted Title Key: {enc_title_key.hex()}") | |
print(f" - Title Key MAC: {title_key_mac.hex()}") | |
#Patch the ROM keys and save the patched ROM | |
rom = rom[:0x1010] + enc_title_key + title_key_mac + rom[0x1030:] | |
print("Patched ROM keys") | |
with open(sys.argv[1], "wb") as f: f.write(rom) | |
print("Saved patched ROM") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment