diff --git a/README.md b/README.md index 34d69b6..8867bee 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,18 @@ ## Usage -- **For the example commands, those in brackets are optional** +- For the example commands, those **in brackets are optional** - Use **python** instead of **python3** if using Windows -- "CCI" is equivalent to a .3ds file + +### Notes +- "SRL" is equivalent to a .nds/.dsi file; "CCI" is equivalent to a .3ds file +- A ROM resigned for dev needs to match your devkit's region for it to load successfully +- IS-NITRO/TWL does not emulate Slot-1 save memory. Most games will show an error message if the save memory is not present. To work around this, you will need to insert a Slot-1 card with a matching save memory chip before loading the ROM image. + +### Re-sign and re-encrypt NTR/TWL-enhanced/TWL-exclusive SRL for dev: +```py +python3 ntool.py srl_retail2dev (--out ) +``` ### Re-sign and re-encrypt CIA/CCI for retail/dev: ```py @@ -34,7 +43,17 @@ python3 ntool.py cci2cia (--out ) (--cci_dev) ``` ### Convert CDN contents to CIA +Example folder structure required: +``` +cdn_folder/ +|__ 00000001 +|__ 00000002 +|__ 00000003 +|__ tmd.0 +|__ tmd.16 +``` - If `--title-ver` is not provided and there are multiple TMD versions in the CDN folder, the latest TMD will be used +- If a `cetk` exists in the CDN folder, it will be used as the ticket; if not, a ticket with a fake signature will be generated and used - Pass `--cdn-dev` if the CDN contents are dev-crypted/signed, pass `--cia-dev` if you want to build a dev-signed CIA ```py python3 ntool.py cdn2cia (--out ) (--title-ver ) (--cdn-dev) (--cia-dev) diff --git a/lib/common.py b/lib/common.py index a96dd29..f3010c6 100644 --- a/lib/common.py +++ b/lib/common.py @@ -1,4 +1,4 @@ -import os, sys, platform, struct, shutil, subprocess, string, warnings, hashlib, secrets, math +import os, sys, platform, struct, shutil, subprocess, string, warnings, hashlib, hmac, secrets, math from ctypes import * from Crypto.Cipher import AES @@ -51,7 +51,17 @@ def roundup(size, alignment): else: return size +def rol(val, r_bits, max_bits): + return (val << r_bits % max_bits) & (2 ** max_bits - 1) | ((val & (2 ** max_bits - 1)) >> (max_bits - (r_bits % max_bits))) + class Crypto: + def sha1(f, size, chunk_size=0x10000): + h = hashlib.sha1() + for _ in range(size // chunk_size): + h.update(f.read(chunk_size)) + h.update(f.read(size % chunk_size)) + return h.digest() + def sha256(f, size, chunk_size=0x10000): h = hashlib.sha256() for _ in range(size // chunk_size): @@ -73,3 +83,42 @@ class Crypto: return True except (ValueError, TypeError): return False + +crc16tab = [0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, + 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, + 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, + 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841, + 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40, + 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41, + 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641, + 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040, + 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240, + 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, + 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41, + 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, + 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41, + 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40, + 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640, + 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041, + 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240, + 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441, + 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41, + 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, + 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41, + 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, + 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640, + 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041, + 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241, + 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440, + 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40, + 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, + 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40, + 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41, + 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, + 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040] + +def crc16(data): # polynomial: x**16 + x**15 + x**2 + 1, 0xA001 + crc = 0xFFFF + for i in range(0, len(data)): + crc = (crc >> 8) ^ crc16tab[(crc ^ data[i]) & 0xFF] + return crc \ No newline at end of file diff --git a/lib/keys.py b/lib/keys.py index fc63f3a..ac9fd23 100644 --- a/lib/keys.py +++ b/lib/keys.py @@ -50,11 +50,8 @@ class CTR: # For tuples: index 0 for retail, index 1 for dev crr_body_mod = bytes.fromhex('E2ADA6EACAA3E8CCA9701D2E234BC655CE13D9A758B4C773961DE8C3094D9BC3EB69A237835DD83704727A4FEA53989D0E0134709A8206E76AC9F80E495AA4E70EFAD4AB3BC5D7F1A4FC927FD0F3CDD5B92A1A4162B37B3E1E3546418EB2531A60F8C2D194B39D769F1D98ACF0CFE3A90585F2BF55761B891CC3192EEE94BE750FA3768B242497FAC05324F58502199DC5112E6BA326FEF755D4230A733F3753EAC2B7C1C9D8F32F78764AA0696091C27D1174EF96D97453B11CB0C43216823BAF61B2DE38873E374BA395888EE0279A1F7DB823C363E86851D98C4CC2598649F7469E3CD79F8923B473352F182376A39F400B769085C889DA65E76EEF2EF567') crr_body_priv = bytes.fromhex('8D27296BC7A7EDCD942D365E86A826E7439E64C8AA9A582107F7B3FBCF8D3E53F002257B80182E0D847D6CE0DAC017A6A513E6FDBF98FC879A9E0E138766248DA56C588610809089EEFD4094CB1F26ABD1D3FFE97B76DC65C015D89BF629E149E9DCBE2417FF092CD6C46D5033DCA09D9DCCDD6E7BDF42224D80C7EBCBB1602F04EE040E4C76495592A5C113600A80153D1CC646572E7CB03D870687FD31F8E714972A5740AC9461CDCFDE8C404695A0D6F92C089B12BFF1889C5D40326D9D99A480A6C2455AD322FEFA175411EA41B4BD681EDD3FE592CB9EF8C00A8BF589A40368F8F8997CFEAD32DD5CB40629DA968BBACB15DE380DCAF70165F62D366E71') - def rol(val, r_bits, max_bits): - return (val << r_bits % max_bits) & (2 ** max_bits - 1) | ((val & (2 ** max_bits - 1)) >> (max_bits - (r_bits % max_bits))) - def key_scrambler(keyX, keyY): - return CTR.rol((CTR.rol(keyX, 2, 128) ^ keyY) + 0x1FF9E9AAC5FE0408024591DC5D52768A, 87, 128).to_bytes(0x10, 'big') + return rol((rol(keyX, 2, 128) ^ keyY) + 0x1FF9E9AAC5FE0408024591DC5D52768A, 87, 128).to_bytes(0x10, 'big') def titlekey_gen(titleID: str, password: str): secret = hextobytes('fd040105060b111c2d49') @@ -62,3 +59,60 @@ class CTR: # For tuples: index 0 for retail, index 1 for dev salt = hashlib.md5(secret + tid).digest() titlekey = hashlib.pbkdf2_hmac('sha1', password.encode(), salt, 20, 16) return hex(readbe(titlekey))[2:].zfill(32) + +class NTR: + blowfish_key = [0x99D5205F, 0x5744F5B9, 0x6E19A4D9, 0x9E6A5A94, 0xD8AEF1EB, 0x4175E23A, 0x9382D032, 0x33EE31D5, 0xCC57619A, 0x3706A21B, 0x793972F5, 0x55AEF6BE, 0x5F1B69FB, 0xE59DF1E9, 0xCE2CD9A1, 0x5E3205E6, 0xFED3FECF, 0xD462040D, 0x8BF5ECB7, 0x2B6079BB, 0x1295310D, 0x6E3FDA2B, 0x8884F0F1, 0x3D127E25, 0x4522F1BB, 0x24061A06, 0x11ADDF28, 0x8B648134, 0x2BEB3329, 0x99AAF2BD, 0x9C14959D, 0x9FF7F58C, 0x7297A129, 0x9DD15FCF, 0x664D071A, 0xDED34A4B, 0x85C9A7A3, 0x1795053A, 0x3D490ABF, 0x0A898BA2, 0x4A8249DD, 0x2790F10B, 0xE9EB1C6A, 0x83764505, 0xBA817061, 0x173F4BDE, 0xAECFAB39, 0x57F23A56, 0x4811AD8A, 0x40E1453F, 0xFA9B0254, 0xCAA693FB, 0xEF4DFE6F, 0xA3D8879C, 0x08BAD548, 0x6A8D2DFD, 0x6E15F874, 0xBDBE528B, 0x18228A9E, 0xFB743707, 0x1B366C4A, 0x19BA4262, 0xB9799110, 0x7B676596, 0xFE0223E8, 0xEE998C77, 0x3E5C8664, 0x4D6D7886, 0xA54F65E2, 0x1EB2DF5A, 0x0AD07E08, 0x14B071AC, 0xBDDB831C, 0xB9D7A162, 0xCDC6637C, 0x5269C3E6, 0xBF75CE12, 0x445D2104, 0xFAFBD33C, 0x381163D4, 0x95854149, 0x4609F208, 0x4311DC1F, 0x76C0156D, 0x1F3C6370, 0xEA87806C, 0xC3BD638B, 0xC2372137, 0xDCEE0923, 0x2E376A4D, 0x7390F750, 0x30AC1C92, 0x04102391, 0x4FD207AA, 0x683E4F9A, 0xC964606A, 0xC81421F3, 0xD6224112, 0x4424CFE6, 0x8A56DD0D, 0x534DE185, 0x1E8C525A, 0x9C1984C2, 0x0357F16F, 0xE300BE58, 0xF64CEDD5, 0x21649C1F, 0xBE55033C, 0x4ADCFFAA, 0xC9DAE05D, 0x5EBFE6DE, 0xF5D8B1F8, 0xFF36B3B9, 0x626795DB, 0x315F37ED, 0x4C706799, 0x90B51831, 0x6C3D9999, 0xE442DAD3, 0x254213A0, 0xAED7706C, 0xB155CFC7, 0xD746D543, 0x61173D44, 0x28E93385, 0xD5D0A293, 0xAA25121F, 0xFBC50B46, 0xF5977656, 0x45A6BE87, 0xB1946BE8, 0xB1FE3399, 0xAE1F3E6C, 0x39711D09, 0x009037E4, 0x103E7574, 0xFF8C833B, 0xB0F1B0F9, 0x01054742, 0x95F1D6AC, 0x7E38E69E, 0x9574263F, 0xB4685018, 0xD04330B4, 0x4C4BE368, 0xBFE54DB6, 0x958B0AA0, 0x74253277, 0xCFA1F72C, 0xD871135A, 0xABEAC951, 0xE80DEEEF, 0xE9937E19, 0xA71E4338, 0x81162CA1, 0x48E373CC, 0x29216CD3, 0x5DCEA0D9, 0x617143A0, 0x1513B564, 0x92CF2A19, 0xDCADB7A5, 0x9F8665F8, 0x1A9FE7FB, 0xF7FDB813, 0x6C27DB6F, 0xDF351CF7, 0x8D2C5B9B, 0x12AB3864, 0x06CCDE31, 0xE84E7511, 0x64E3FAEA, 0xEB3454C2, 0xAD3F34EB, 0x932C7D26, 0x369D56F3, 0x5AE1F6B3, 0x98634A9E, 0x3283E49A, 0x84607D90, 0x2E130EEE, 0x934B36A2, 0x85EC1638, 0xE8880602, 0xBFF0A03A, 0xEDD76A9A, 0x73E157CF, 0xF844B8DC, 0x2E2359D1, 0xDF955271, 0x9961A04B, 0xD57F6E78, 0xBAA9C530, 0xD3408632, 0x9D320C9C, 0x37B7022F, 0xBA5498A9, 0xC41304C9, 0x8DBEC8E7, 0x5D97502E, 0x93D62259, 0x0C27BC22, 0x92E0A720, 0x0F936F7F, 0x4C9FD3B5, 0xA62A0B74, 0x67497D10, 0x26CBD1C5, 0x8671E78C, 0xA09CE95B, 0xB21AF601, 0xEE8C9E5E, 0x83F21ADB, 0xE6E5EA84, 0x5976D27C, 0xF68DA549, 0x3648C216, 0x52BB83A3, 0x74B9070C, 0x3BFF6128, 0xE161E9E4, 0xEF6E15AA, 0x4EBAE85D, 0x0596BB32, 0x56B0FB72, 0x520F0EC8, 0x42256576, 0x89AFF2DE, 0x1027F001, 0x4B74A797, 0x07D52654, 0x54091F82, 0x0A867D30, 0x390EB326, 0x9B0B57BB, 0x360631AF, 0xFD79FCD9, 0x30102B0C, 0xB3E19BD7, 0x7BDC5FEF, 0xD2F81345, 0x4D4775BD, 0x46963C7E, 0x75F33EB5, 0x67C59A3B, 0xB05B296B, 0xDE805BC8, 0x1505B131, 0xB6CE49DD, 0xAD84B5AE, 0x60DC6731, 0x3430FE4E, 0xBD802FA6, 0xBF633921, 0x86D9357F, 0x16682205, 0x54E99026, 0x8C076C51, 0xA43155D7, 0x0907A83E, 0x2E5366C1, 0xF8F27BC4, 0xF258CFF1, 0x87C5A2E7, 0x278F3087, 0x58A06462, 0x2318B988, 0x7CFACEC4, 0x98AEAD17, 0xCC4A5BF3, 0xE948D556, 0xD30DF2C8, 0x92738CDB, 0xD72F56AC, 0x81F99269, 0x4DC632F6, 0xE6C08D21, 0xE2768061, 0x11BCDC6C, 0x93AF1969, 0x9BD0BFB9, 0x319F0267, 0xA351EE83, 0x06227B0C, 0xAB494240, 0xB8D5017D, 0xCE5EF755, 0x5339C599, 0x46D8879F, 0xBAF764B4, 0xE39AFAA1, 0x6D906810, 0x30CA8A54, 0xA79F60C3, 0x19F56B0D, 0x7A5198E6, 0x984351B4, 0xD635E94F, 0xC3DF0F7B, 0xD62F5CBD, 0x3A156119, 0xF14BCBAA, 0xDC6D64C9, 0xD3C61E56, 0xEF384C50, 0x718675CC, 0x0D0D4EE9, 0x28F6065D, 0x701BAAD3, 0x45CFA839, 0xAC95A62E, 0xB4E422D4, 0x74A8375F, 0x487A04CC, 0xA54C40D8, 0x28B42808, 0x0D1C7252, 0x41F07D47, 0x193A534E, 0x5884626B, 0x93B58A81, 0x214E0DDC, 0xB43FA2C6, 0xFCC92B40, 0xDA3804E9, 0x5E5A866B, 0x0C222585, 0x68118D7C, 0x921D9555, 0x4DAB8EBB, 0xDAA6E6B7, 0x51B6325A, 0x0541DD05, 0x2A0A5650, 0x911747CC, 0xC9E67EB5, 0x614ADB73, 0x6751C833, 0xF5DA6E74, 0x2E54C337, 0x0D6DAF08, 0xE8158A5F, 0xE25921CD, 0xA8DE0C06, 0x5A776B5F, 0xDB18653E, 0xC850DE78, 0xE0B882B3, 0x5D4E7232, 0x074FC134, 0x23BA96B7, 0x674EA428, 0x1E3462EB, 0x2D6A70E9, 0x2F42C470, 0x4E5A319C, 0xF95B4728, 0xAADA716F, 0x381FB378, 0xC4926B1C, 0x9EF6359A, 0xB74D0EBF, 0xCC182941, 0x0348355D, 0x55D02BC6, 0x29AF5C60, 0x74698E5E, 0x9B7CD4BD, 0x7B44647D, 0x3F925D69, 0xB61F004B, 0xD48335CF, 0x7E644E17, 0xAE8DD52E, 0x9A28124E, 0x2E2B4908, 0x5CAEC646, 0x85AE4161, 0x1E6F82D2, 0x5137161F, 0x0BF659A4, 0x9ACA5AAF, 0x0DD4338B, 0x2063F184, 0x805CCBCF, 0x08B4B9D3, 0x1605BD62, 0x83319B56, 0x51989FBA, 0xB25BAAB2, 0x226B2CB5, 0xD448FA63, 0x2B5F58FA, 0x61FA6409, 0xBB38E0B8, 0x9D9260A8, 0x0D676F0E, 0x37F50D01, 0x9FC277D4, 0xFEECF173, 0x3039E07D, 0xF56198E4, 0x2C285504, 0x5655DB2F, 0x6BECE558, 0x06B66480, 0x6A2A1A4E, 0x5B0FD8C4, 0x0A2E5219, 0xD962F530, 0x48BE8C7B, 0x4F389BA2, 0xC3AFC9D3, 0xC7C16241, 0x86B96121, 0x576F994F, 0xC1BACE7B, 0xB53B4D5E, 0x8A8B4457, 0x5F135F70, 0x6D5B2947, 0xDC38E2EC, 0x04556512, 0x2AE81743, 0xE18EDD2A, 0xB3E294F7, 0x096E5CE6, 0xEB8AF86D, 0x89495448, 0xF52FADBF, 0xEA944BCA, 0xFC398782, 0x5F8A01F2, 0x75F2E671, 0xD6D842DE, 0xF12D1D28, 0xA6887EA3, 0xA0471D30, 0xD9A371DF, 0x491CCB01, 0xF836B1F2, 0xF022585D, 0x456BBDA0, 0xBBB28842, 0xC78C28CE, 0x93E89063, 0x08907C89, 0x3CF57DB7, 0x042D4F55, 0x5116FD7E, 0x79E8BEC1, 0xF212D4F8, 0xB4840523, 0xA0CCD22B, 0xFDE1ABAD, 0x0DD1556C, 0x2341944D, 0x77374F05, 0x280CBF17, 0xB312676C, 0x8CC35AF7, 0x41842A6D, 0xD0941227, 0x2CB4ED9C, 0x4DEC4782, 0x97D567B9, 0x1B9DC055, 0x077EE58E, 0xE2A8E73E, 0x12E40E3A, 0x2A455534, 0xA2F92D5A, 0x1BAB527C, 0x83105F55, 0xD2F15A43, 0x2BC6A7A4, 0x891595E8, 0xB44B9DF8, 0x75E39F60, 0x785BD6E6, 0x0D44E621, 0x06BD4722, 0x53A400AD, 0x8D431385, 0x39F7AAFC, 0x38AF7BED, 0xFCE42B54, 0x50984CFC, 0x8580F7DF, 0x3C8022E1, 0x94DADE24, 0xC6B07A39, 0x38DC0FA1, 0xA7F4F96F, 0x6318578B, 0x84412A2E, 0xD453F2D9, 0x000FD0DD, 0x996E19A6, 0x0AD0EC5B, 0x5824ABC0, 0xCB0665EC, 0x1A133894, 0x0A67032F, 0x3FF7E377, 0x447733C6, 0x1439D0E3, 0xC0A20879, 0xBB409957, 0x410B0190, 0xCDE1CC48, 0x67DBB3AF, 0x8874F34C, 0x828F72B1, 0xB52329C4, 0x126C19FC, 0x8E46A49C, 0xC4256587, 0xD36DBE8A, 0x93110338, 0xED832BF3, 0x46A493EA, 0x3B53851D, 0xCED4F108, 0x8327EDFC, 0x9B1A18BC, 0xF98BAEDC, 0x24AB5038, 0xE9724B10, 0x22177B46, 0x5DAB5964, 0xF340AEF8, 0xBBE5C8F9, 0x26034E55, 0x7DEBEBFE, 0xF739E6E0, 0x0A11BE2E, 0x28FF98ED, 0xC0C94256, 0x42C3FD00, 0xF6AF87A2, 0x5B013F32, 0x9247959A, 0x72A5323D, 0xAE6BD09B, 0x07D24992, 0xE3784AFA, 0xA1067DF2, 0x41CF7774, 0x0414B20C, 0x86846416, 0xD5BB51A1, 0xE56FF1D1, 0xF2E2F75F, 0x58204DB8, 0x57C7CFDD, 0xC5D8BE76, 0x3DF65F7E, 0xE72A8B88, 0x241B383F, 0x0E412377, 0xF5F04BD4, 0x0C1FFAA4, 0x0B805FCF, 0x45F6E0DA, 0x2F345953, 0xFB203C52, 0x625E35B5, 0x62FE8B60, 0x63E3865A, 0x151A6ED1, 0x4745BC32, 0xB4EB6738, 0xABE46E33, 0x3AB5EDA3, 0xAD67E04E, 0x4195EE62, 0x6271261D, 0x31EF6230, 0xAFD782AC, 0xC2DC0504, 0xF59707BF, 0x11592307, 0xC06402E8, 0x97E53EAF, 0x18AC59A6, 0x8B4A3390, 0x1C6E7C9C, 0x207E4C3C, 0x3E6164BB, 0xC56B7C7E, 0x3E9FC54C, 0x9FEA73F5, 0xD789C04C, 0xF4FBF42D, 0xEC141B51, 0xD5C112C8, 0x10DF0B4A, 0x8B9CBC93, 0x456A3E3E, 0x7DC1A9BA, 0xCDC1B407, 0xE4E16886, 0x43B26D38, 0xF3FB0C5C, 0x663771DE, 0x56EF6EA0, 0x104065A7, 0x98F7D0BE, 0x0EC83736, 0xEC10CA7C, 0x9CAB841E, 0x05177602, 0x1C4F52AA, 0x5FC1C6A0, 0x56B9D804, 0x84444DA7, 0x59D8DE60, 0xE6380E05, 0x8F03E13B, 0x6D810433, 0x6F300BCE, 0x69052133, 0xFB26BB89, 0x7DB6AE87, 0x7E5107E0, 0xACF7960A, 0x6BF9C45C, 0x1DE44447, 0xB85EFAE3, 0x78845542, 0x4B485EF7, 0x7D473586, 0x1D2B4305, 0x03EC8AB8, 0x1E063C76, 0x0C481A43, 0xA7B78AED, 0x1E13C643, 0xEE10EFDB, 0xECFB3C83, 0xB29544EF, 0xD854514E, 0x2D11441D, 0xFB36591E, 0x7A34C1C3, 0xCA570061, 0xEA67A516, 0x9B55D055, 0xE17FD936, 0xD24076AE, 0xDC01CEB0, 0x7A83D5CB, 0x2098EC6B, 0xC1729234, 0xF3825737, 0x628A3236, 0x0C9043AE, 0xAE5C9B78, 0x8E136502, 0xFD6871C1, 0xFEB031A0, 0x2482B0C3, 0xB17969A7, 0xF5D2EBD0, 0x82C032DC, 0x9EC7263C, 0x6D8D98C1, 0xBB22D4D0, 0x0F33EC3E, 0xB9CCE1DC, 0x6A4C7736, 0x141CF9BF, 0x819F285F, 0x71853229, 0x907548C4, 0xB34ACED8, 0x448F142F, 0xFD4057EF, 0xAA0875D9, 0x46D1D66E, 0x32551FC3, 0x18FE841F, 0xFC84D5FF, 0x715E1B48, 0xC386950E, 0x280827D3, 0x3883717B, 0x4C806354, 0x9A56B0AC, 0xCF80CA31, 0x09EFFEF3, 0xBEAF247E, 0xA6FE533F, 0xC28D4A33, 0x68D122A6, 0x66AD7BEA, 0xDEB643B0, 0xA1259500, 0xA33F7546, 0x141144EC, 0xD795BC92, 0xF04FA916, 0x53629760, 0x2A0F41F1, 0x7124BEEE, 0x947F08CD, 0x6093B385, 0x5B07003F, 0xD80F2883, 0x9AD1699F, 0xD1DA2EC3, 0x9001A2B9, 0x6B4E2A66, 0x9DDAAEA6, 0xEA2AD368, 0x2F0C0C9C, 0xD28C4AED, 0xE29E5765, 0x9D0987A3, 0xB4C4325D, 0xC9D4322B, 0xB1E0711E, 0x644DE690, 0x71E31E40, 0xED7DF384, 0x0EEDC878, 0x76AEC071, 0x2772BB05, 0xEA0264FB, 0xF3486BB5, 0x42933FED, 0x9F1353D2, 0xF7FE2AEC, 0x1D4725DB, 0x3C9186C6, 0x8EF011FD, 0x237436F7, 0xA4F59E7A, 0x7E535044, 0xD447CAD3, 0xEB386DE6, 0xD971947F, 0x4AC6694B, 0x11F452EA, 0x22FE8AB0, 0x36678B59, 0xE8E6802A, 0xEB650413, 0xEEECDC9E, 0x5FB1EC05, 0x6A59E69F, 0x5E596B89, 0xBFF71ACA, 0x44F95B6A, 0x718503E4, 0x2962E070, 0x6F41C4CF, 0xB2B1CCE3, 0x7EA607A8, 0x87E77F84, 0x93DB524B, 0x6CEC7EDD, 0xD4244810, 0x699F0460, 0x74E64818, 0xF3E42CB9, 0x4F2E507A, 0xDFD45469, 0x2B8BA7F3, 0xCEFF1FF3, 0x3E260139, 0x17958489, 0xB0F04C4B, 0x82919FC4, 0x4BAC9DA5, 0x74AF1725, 0xC9CA32D3, 0xBC898A84, 0x89CC0DAE, 0x7CA2DB9C, 0x6A7891EE, 0xEA765D4E, 0x8760F569, 0x1567D402, 0xCFAF4836, 0x07EABF6F, 0x662D068F, 0xC49AFEF9, 0xF6908775, 0xB8F7AD0F, 0x76105A3D, 0x59B02EB3, 0xC7352CCC, 0x70562BCB, 0xE33796C5, 0x2F461B8A, 0x2246C788, 0xA7263298, 0x61DF8622, 0x8AF41C2F, 0x87A109AA, 0xCCA9AED3, 0xBD00451C, 0x9A548786, 0x5287EFFF, 0x1E8FA18F, 0xC1895C35, 0x1BDA2D3A, 0x2C16B2C2, 0xF156E278, 0xC16B6397, 0xC5568FC9, 0x327F2CAA, 0xAFA6A8AC, 0x20912288, 0xDEE4608B, 0xF94B4225, 0x1AE37F9C, 0x2C19893A, 0x7E05D436, 0xCC6958C2, 0xC1328B2F, 0x9085EB7A, 0x3950A5A1, 0x2792C566, 0xB0204F58, 0x7E558343, 0x2B45E29C, 0xE4D81290, 0x2C168356, 0x167903B3, 0xAD2D6118, 0x1A131F37, 0xE2E19C73, 0x7B80D5FD, 0x2D5187FC, 0x7BAAD71F, 0x2C7A8EAF, 0xF48DBBCD, 0x95117C72, 0x0BEE6FE2, 0xB9AFDE37, 0x83DE8C8D, 0x620567B7, 0x96C68D56, 0xB60DD762, 0xBAD64636, 0xBD8EC8E6, 0xEA2A6C10, 0x14FF6B5B, 0xFA823C46, 0xB1304346, 0x518A7D9B, 0x923E8379, 0x5B555DB2, 0x6C5ECE90, 0x628E5398, 0xC90D6DE5, 0x2D57CDC5, 0x8157BAE1, 0xE8B88F72, 0xE54F13DC, 0xEA9D7115, 0x10B21188, 0xD509D47F, 0x5B657F2C, 0x3B384C11, 0x68508DFB, 0x9EB059BF, 0x9480894A, 0xC51A1812, 0x8953D14A, 0x1029E88C, 0x1CECB6EA, 0x46C7178B, 0x251531A8, 0xA26B43B1, 0x9DE2DB0B, 0x879BB011, 0x040E71D2, 0x29778982, 0x0A66417F, 0x1D0B48FF, 0x72BB24FD, 0xC248A19B, 0xFE7B7FCE, 0x88DB86D9, 0x853B1CB0, 0xDCA83307, 0xBF512EE3, 0x0E9A0097, 0x1E06C097, 0x439DD8B6, 0x45C48667, 0x5F00F888, 0x9AA4529E, 0xC7AA8A83, 0x75ECC518, 0xAECEC32F, 0x1A2BF918, 0xFFAE1AF5, 0x530BB533, 0x51A7FDE8, 0xA8E1A264, 0xB6221743, 0x80CC0AD8, 0xAE3BBA40, 0xD7D9924A, 0x89DF0410, 0xEE9B182B, 0x6A77698A, 0x68F4F9B9, 0xA221156E, 0xE61E3B03, 0x62309B60, 0x417E259B, 0x9E8FC552, 0x1008F8C2, 0x69A12111, 0x88375E79, 0x3566FF10, 0x42186EED, 0x97B66B1C, 0x4E36E56D, 0x7DB4E4BF, 0x20B9E005, 0x3A69D5B8, 0xE3D5DCE0, 0xB9AC533E, 0x07A457AD, 0x77FF4818, 0x762AAC49, 0x2A8E4775, 0x6D9F6763, 0x30358C39, 0x0539D56F, 0x643A5BAD, 0xCA0BBB82, 0x529945B1, 0x93363699, 0xAF132044, 0x36D80244, 0x09399285, 0xFF4A4A97, 0x87A663D7, 0xC7B5B524, 0xED0FB46F, 0x0C585214, 0xD9A67BD3, 0x79BC3858, 0xA1BD3B84, 0x06D81A06, 0xFD6BA8EA, 0x4B692804, 0x37AD8299, 0xFB0E1B85, 0xBDA85D73, 0xCDDC5875, 0x0ABE636C, 0x48E74CE4, 0x302B0460, 0xB915D8DA, 0x8681758F, 0x96D48D1C, 0x5D70857C, 0x1C677BD5, 0x0867A6CE, 0x4B0A6670, 0xB7E563D4, 0x5B8A82EA, 0x1067CAE2, 0xF4EF1785, 0x2F2A5F8A, 0x9782F86A, 0xD63410EA, 0xEBC95C3C, 0xE149F846, 0xEBDEBDF6, 0xA992F1AA, 0xA6A018B0, 0x3AD30F1F, 0xF36FFF31, 0x454344D3, 0x509AF788, 0x0996C1CE, 0x76CCF22C, 0x2CBAAD82, 0x778F1884, 0xC0D2079C, 0x3690834E, 0x0BA54F43, 0x3E04AB78, 0x4FD6FB09, 0x012490DA, 0x6F3C3A61, 0x0D7F694A, 0xEB2B3002, 0xB4DBE084, 0xA9ECD735, 0xBF377D85, 0x58CEA94E, 0xE480C7A8, 0xD3306748, 0xEB29AF2F, 0x746AB4A7, 0x3F0F3F92, 0xAFF3CAAC, 0xAF4BD994, 0xC043CA81, 0x0D2F48A1, 0xB027D5D2, 0xEF4B0585, 0xA3DE4D93, 0x303CF0BB, 0x4A8F3027, 0x4CEBE33E, 0x64ED9A2F, 0x3BF182F0, 0xBAF4CF7F, 0x40CBB0E1, 0x7FBCAA57, 0xD3C974F2, 0xFA430D22, 0xD0F4774E, 0x93D78570, 0x1F99BFB6, 0xDE35F130, 0xA75E71F0, 0x6B012D7B, 0x64F03353, 0x0A3988F3, 0x6B3AA66B, 0x35D22F43, 0xCD02FDB5, 0xE9BC5BAA, 0xD8A4197E, 0x0E5D9481, 0x9E6F77AD, 0xD60E7493, 0x96E7C418, 0x5FADF519] + +class TWL: # For tuples: index 0 for retail, index 1 for dev + # RSA signing keys + rsa_key_mod = [ + (bytes.fromhex('C30293DEC05271302D4D22BADAA27379D4D9DE9EFD301DF50E7188D95F9CEE49B09D19AF0C76381C1C7D489AE02128B8B174074CCE83102B777C915B89CBD3450E799C15158E474E8C389E3786780EFD1E373AE103ED03F9084DE6DB4B005D27392C73E5B9B833054D2E2BFA4747F54A73A3F5B969FDD5F20EFAA3CF1DFE3359'), bytes.fromhex('bcfda1ff1f66dfecb469f8f7430c5d0f00d7204942060329850b99596198706efff6b47066f0dd8fdce9f20dd0211d77b89c5187c0b133ab13960b47b8424a0dc377e187b16b2431108a47f232f4c9782513d480100552c3e7507b2949ce93d98f2ab54dd1c191d807161015ffd6848f543d915b374548e46b62d2119b0d7169')), + (bytes.fromhex('B618D86128CB5C6F05FCD709183FB2D06B7DEED998DC4FDDC1A85918FBB065BD65809CC768A14EDC18AA7BCBB9A07CFC1FAB865DED9C2C5C6D07D9FCC29B7A9D7C3A7333B7E8048681C85C7DB3957DC9EC66072F8BB26D13C46CF0BA27823318D4316AB2ADBC37066A2EE9735F3A57C7D7F88EC1B93D3FD4E5276FB4008BB719'), bytes.fromhex('e99ea79f594df4a76004bd47f2b364cd1679c14739f6a9f8ee1ad072cf43970c93a1384e13406c105943e2712954142cc5da594db46aef85616f7f1c59342cc624f37bc3b740d146f890b7c29850af955242dbacd67ea9c33d1b51560706d00b01bb5893eaa02cc77d6a317ec9e2dafe1f2e9da75484dc28b918ea16f295556d')), + (bytes.fromhex('DA940901D5B7D3C511460C4119BC8ABAD1562888440710ABC5B8E306CEA65BE40752E28985566DE4CD5CBDFA4575FE581CE3509E3C67405E0F46396A3C48066ECEDD308341B5804DD9B864A3F048C9ECACC9CFAFF9B6460449617BE2A3A72588D3EDA5F3768492002C96BCB4389C0256564FEDC2A34E4B8861000316A58F5783'), bytes.fromhex('a79f54a0c745aef663a753b70acc0bcb65e111c60515b56ebdac0ccaf47c687af90e5d985bc84d223ba3be8b5b7f26449fc44844b132b7be63bad6c110cef6ed478fe1ff7f5ad55d94382fa1d4ef82b10dc443ecbe77b6829cfa178784822546fbd605c89a7ead44400d359c45446436614bf7e6315c7d9673e8acb4e35ed19d')), + (bytes.fromhex('956F790DF08BB85A76AAEFA27FE874758BED9EDF9E9A670CD818BEB9B2885203B3FA11AEAA186513B5D6BB85A384D0D0EFB366CBC6051AAA86827AB74311F59C9BFC6C7079D5F17BD0819F522056738C721F40CF2361932590A3C5DC94CFD17A8CBC954A918AA858F4D804BAF7D3C1C4D7B8F077012FA170260B2C049056F3A5'), bytes.fromhex('ac93bb3c155c5f25b04c37a42d85291d7a9d2dd579b55db108209cf04c562797f87e3ecb9406059400929bb05b06f6afaa9ca5f011a78acb0c11d60c3d30ac51795ab57f119274488281bf3bfa93bf6b5b3f86964fcc9012b2398d68167bc687f1f5606239fb107e487fdd82383876b5ce214bc96f318d23573db66ceec20d11')) + ] + rsa_key_priv = [ + (None, bytes.fromhex('1665dcc81699a39a32fb88350dae2e6d33c54b1f4382e0e70fcc831d13b2f448436fa28cdbb65a1bcdabb03330e26bd29f7f6f2e2e8911bd89b1b39e8958d9dbb99100da17a914c383ffff460f5f811b5b02d229ef48b7df8c34eda2ecc07950c6ad0b9828c2178096673a22d7606198f6ab101ee623064b61affed5afb83d81')), + (None, bytes.fromhex('cf67aa3b1f26c5687f2755fff5296cf962d5cf50f3d5d84b06e0c964d43b967374f171b267070fa968068b9799706ac4e1169ba80fdfcc2be737fc6a678b999ed1dacc68fceb8dfe4284728805af2332301ffacc6752936cc10acfd6d35f75e94d32c0311bd4b54bb44eb03ed56c3cdc57bb3224419c79befcc03b630c5dc6e5')), + (None, bytes.fromhex('48ca9c49a5bfd11716779797298d2aa20d800be3de596b0cc8396ae4c7ff2eb0186ff09d355e6a80af3d90859c1a3f5815b97b58b94ab91868debe5be11cbf85df5f30fdd2ecc077336258ecc147f741e82b2183e08ee0f4035d078491fa5bd8d347906e7d819c1ab3032fbc1a2801bb783360d2323409ee0835507a0277b901')), + (None, bytes.fromhex('95dcc81819f8980b73780c4d7ed606e93983eabc59af7c87f225d802f8574c94b4ce749b7694701da10daf334b28d5e9a03fddfd866736a8b2777ea82c2eeb437be4a3667c3a0090c5f5f859f995882aa8f494410502a047c0c1b9801f49bb09bf42bae6ee8d211dfb8fc7b77100fb3513d8bca33f3acf96b00b8a619c70f891')) + ] + + # HMAC keys + hmac_key = bytes.fromhex('2106C0DEBA98CE3FA692E39D46F2ED0176E3CC08562363FACAD4ECDF9A6278348F6D633CFE22CA9220889723D2CFAEC232678DFECA836498ACFD3E3787465824') + hmac_key_whitelist12 = bytes.fromhex('61BDDD727E72BEDEAD3ADF7F3D2DF7A5167EB4C97C6C007C57BB948A64CD4E1C516BBDDB1DEB54E93427F931515E894E7FD97CE992440FEF6BB612216888D8EE') + hmac_key_whitelist34 = bytes.fromhex('852948F3A1BB1330935DB8C9A59AE830C4D04ADDA49281FD4FA132FA4605DE687BA7D75BC93AC88DCD253A173CC2D6E0D2E5B9FB49F94D05701029517AC58949') + + def key_scrambler(keyX, keyY): + return rol((keyX ^ keyY) + 0xFFFEFB4E295902582A680F5F1A4F3E79, 42, 128).to_bytes(0x10, 'big') + + def aes_ctr_block(key, counter, inp): + cipher = AES.new(key, AES.MODE_ECB) + stream = cipher.encrypt(counter) + out = bytearray(16) + for i in range(16): + out[i] = stream[15 - i] ^ inp[i] + + # Increment counter: (semi) byte reverse ctr to u32[4], add with carry, then reverse back + n = [] + for i in range(4): + n.append((counter[i * 4 + 0] << 24) | (counter[i * 4 + 1] << 16) | (counter[i * 4 + 2] << 8) | (counter[i * 4 + 3] << 0)) + + carry = 1 + for i in range(3, -1, -1): + sm = (n[i] + carry) & 0xFFFFFFFF + if sm < n[i]: + carry = 1 + else: + carry = 0 + n[i] = sm + + for i in range(4): + counter[i * 4 + 0] = (n[i] >> 24) & 0xFF + counter[i * 4 + 1] = (n[i] >> 16) & 0xFF + counter[i * 4 + 2] = (n[i] >> 8) & 0xFF + counter[i * 4 + 3] = (n[i] >> 0) & 0xFF + + return bytes(out), counter + + blowfish_key = ([0x59AA568E, 0x90D71155, 0x4DEABFFE, 0xBD0D7591, 0xF7853998, 0xD09CC358, 0xC4156FF1, 0x90F9E4C3, 0x8EC09B0E, 0x5DE18794, 0xB9072CBA, 0xA64F7574, 0xC1E31C86, 0xE6EDF809, 0x3BBB377A, 0x4EF0F092, 0xF655FA47, 0xFB1BC516, 0x06744E56, 0x20DDB6D1, 0x42CFCFF1, 0x557E1718, 0xA193FF09, 0xDA36A69A, 0x433DF465, 0xED40976C, 0xD5A6DD6D, 0x6C23BF94, 0xE751A668, 0x3CE8E665, 0xD6BC9E92, 0x782646A1, 0x73DCE536, 0x8ECDECA1, 0xF1EE8B68, 0xF4ACC1DC, 0xC8849531, 0xE8EDC75E, 0xE45A37CA, 0xEC55BE2A, 0xFCF64567, 0xA9B47D7D, 0x9B6EE92C, 0xFF3FEB69, 0xB72E68A8, 0x94EF7BBD, 0x88931566, 0x3AB77FFE, 0x1DC38908, 0xD77459FA, 0xAF91419E, 0x57D56784, 0xBA00E963, 0x58074DEC, 0xDFC6DA1E, 0x6252D914, 0xBC03C3B0, 0xA5FDB727, 0xDEB16F1B, 0x7C724ACD, 0x09E58270, 0xD39FB6D6, 0xA46A2FC2, 0x32BDB539, 0xE4EAB971, 0x1C706721, 0x9221ACF4, 0x9E63E85E, 0x8302CC0C, 0xF8F89E87, 0x89FC0385, 0xFACC7707, 0x445F4DE5, 0x19D312EE, 0xCAE1E0BF, 0x1EBEE712, 0x1F6A931E, 0x384BA79F, 0x81A97785, 0x0CC63902, 0x55D26256, 0x1985A638, 0x85E12D3C, 0x383B5BA0, 0x2418E929, 0x6C9FE44D, 0x4E235FB1, 0xE2A06F97, 0xB241D1EA, 0xDBA73781, 0xEB068D77, 0xC268FC5A, 0x65973358, 0xA1B8350F, 0xF425BC3B, 0x4F180F0E, 0x60253DD8, 0x771AD08A, 0xB0615716, 0x0B55F258, 0xB9915230, 0x33AB299B, 0x0362E5CC, 0xDF6E6286, 0x9D76E5DD, 0x6FCA3E75, 0xD8885806, 0x8DA458F5, 0xAA7CCE17, 0xDDDECA0A, 0x72876F29, 0x6C0CE9C0, 0x3D322E55, 0xF3A727DA, 0xFC860C9E, 0x3383B547, 0xEBE8F6C9, 0xF42472EE, 0xAFF8B559, 0x70068571, 0xBB3CBEBB, 0x2C24AD67, 0xBA42B9EE, 0x68EC0BE6, 0x5B260F2B, 0xB63A934F, 0x9FE69FB9, 0x1AA0B951, 0x1C8D6637, 0xD250CCAE, 0x10301660, 0x563B990E, 0x907B28DE, 0x93F41687, 0x1FD09BC2, 0x33422C2C, 0xF136C339, 0xF84FA41E, 0x0043B1AC, 0xDF08BBFE, 0x5E2ADC2A, 0x10F37BC5, 0x2F96C91D, 0x514FC0DE, 0x6E939A35, 0x19B858B5, 0x19BAAF2A, 0xB1B5B2FF, 0xC189BC3F, 0xD88F3407, 0x6360A5ED, 0xDBFF9EF5, 0x5B23C01B, 0x1396D42F, 0x07511BAC, 0x90727128, 0x6598E1FF, 0x6A9DE730, 0x6DB12C21, 0xFACBBC6A, 0x3C25E850, 0x5C53D8D5, 0xCBA25353, 0xA5643F78, 0x61771D8D, 0x16E4E4A1, 0x329C0052, 0x5F2AD7F5, 0x3CFD09D7, 0x1B3B9901, 0x4DDC91E4, 0x6DE89EA3, 0x18AD4327, 0xBAC15F37, 0xA61261F5, 0x1C630C25, 0x2D90F852, 0xCB2C374B, 0xDE1E6C36, 0x1D47F5DF, 0x87CA7998, 0x800959D7, 0x14FDF7F9, 0xF4CE6923, 0xD2F8C4EE, 0xA07EF836, 0x8E359345, 0x82AE0DFC, 0x65BCAAF5, 0x58A965BA, 0xC6084B63, 0xC33FA68A, 0xF4C19B8F, 0x02451B13, 0x78289FD6, 0x53B1C27E, 0x4E7117E7, 0x550962C7, 0xADD5911A, 0xC0FA494A, 0xEF00D6F6, 0xF1D0C940, 0x1BB1FD0E, 0xD395F1CD, 0x95600873, 0xD2E056FA, 0xD06551FD, 0xC448D1AA, 0x5ABACB8F, 0x7622E360, 0x6F4A3C86, 0x35EEE988, 0x9A4A3634, 0x74E36D3F, 0xE42A930B, 0xE2C6474D, 0xF2B68E78, 0x149161CF, 0xFAB61B39, 0xCA880C04, 0x65D33BD1, 0xC6DAE5F4, 0xE91A380F, 0xA5CA3229, 0x786C919D, 0xD8C18C3D, 0x6E824910, 0x384C95E3, 0xF169302E, 0x3EBFAF7D, 0x5E513C6A, 0x1504BD8F, 0xCFEB3FE0, 0xE0A7B33E, 0xF3F7D81D, 0x1574EF4E, 0x5BA01E3A, 0x45EC988B, 0xE40CFB77, 0xFDCFDE88, 0x4D421881, 0x140DE220, 0x4ECF0D3B, 0xC841369D, 0x99AB47CB, 0x55F07977, 0x3285A4E4, 0x1114428D, 0x038C76BA, 0x05CFE840, 0x47CFBDE7, 0x22E672CE, 0xA013E459, 0x5E68C253, 0x7A4D4B4C, 0xCDBFE2B0, 0xA36377F2, 0x1EC321CA, 0xD2B67B01, 0x790243EC, 0x6D989786, 0x274167E7, 0x04CF710E, 0xFCC83D32, 0x99354D2C, 0x94D782B5, 0x2E2073D8, 0xA4F9AE6C, 0xD61257E9, 0x44866A9E, 0xE0728497, 0xB38D5628, 0x66DBEC25, 0xBF011176, 0x9BE1438D, 0x6D0BFA3D, 0x45154AB4, 0xAC762A4A, 0xFB8DA503, 0xE436E6D9, 0xFDC12063, 0xE35C9A0E, 0x0F9949C6, 0x109A0847, 0xFF3DAA0C, 0x9F46575A, 0xE5C524C5, 0xF1CA1AA2, 0xB02978DD, 0x7A724954, 0xACC42204, 0x97A2A11A, 0x2F57FD9B, 0xAFC93010, 0x4AF45E52, 0xF8253248, 0xCB026C3B, 0xA7E3BDE9, 0x54D5BE46, 0x6BEA0B43, 0x131D6F9C, 0xF5EB0EBA, 0x284F9884, 0xB2199CFE, 0xA04AF607, 0xCC0C8F75, 0x6A16A11C, 0x4E4251DC, 0x17B0A42C, 0x868755F5, 0x7A5AD00D, 0x4B9FB9CB, 0xF3235BAA, 0x810E7456, 0x96BB6514, 0x3EB21753, 0x7E71F19B, 0xFD1C5CFE, 0xEE6B58C7, 0xB582ED14, 0x47B062E8, 0xAD349CE6, 0x12293B91, 0x2B83E65C, 0xD4F15B7F, 0xE058C829, 0xA41776A0, 0x959DB1AD, 0xA101A2CE, 0xD0A3141E, 0xB722989D, 0xCD7F8CB8, 0x0F5B5B36, 0x3FCECACE, 0x5B548BBD, 0xDE827EF1, 0xF9A030FE, 0xBDE73584, 0x291E418E, 0x553FF740, 0x23AA2D5A, 0xE5C4329E, 0xBF22B0C1, 0xE78C7D5D, 0x0B28B457, 0x8EE7563D, 0x1F351E98, 0xA90DD7B7, 0x20E28990, 0x04A756EA, 0x84166FFF, 0xA9385EA0, 0xAF2DC1B6, 0xC17772E1, 0x21C72F3F, 0x85514B83, 0xCA3350B1, 0x4C580C54, 0x7C70FE23, 0xEFC7C7AF, 0xAFBFE57B, 0x05906C7A, 0x9F66B9C6, 0x44D5996C, 0xD5AC74CE, 0x00494BCF, 0x5101DA24, 0xC542BA6F, 0x8A732011, 0xBC4A4FDB, 0xA64027BC, 0x93A330B2, 0xCC6E78A0, 0x287DE734, 0x114C008B, 0x043D937F, 0x2A3C6756, 0xADC5DD2A, 0x75E19602, 0x8D660ED8, 0xC183DF27, 0x42C44718, 0x24AC998B, 0x22286874, 0xB27E5819, 0x19DAD496, 0x3626C753, 0x37DB53A5, 0xD398B465, 0x80DE73CB, 0x977E5980, 0xF625606F, 0x77204CC7, 0x35C680E3, 0x562CBA62, 0xF756F963, 0x3EF9917B, 0x9C350204, 0xD83D35FD, 0xB785BA04, 0x197FB9E6, 0x6A65519E, 0xDE21ECF0, 0x6BFD4190, 0xDC32084D, 0x9B432A61, 0x5B3561C1, 0xFDA2DE30, 0xD393C60D, 0xAD76ACFB, 0xB0EE855F, 0xDE4E2BE8, 0x8F67A012, 0x003FCF04, 0xE4B12BA0, 0xDABB335A, 0x589B7C05, 0xEA2B7B40, 0x9CC3E099, 0x9EE09167, 0xA5636B9F, 0x15B63CDA, 0x17908F05, 0x7E617CC7, 0x25DFBBD6, 0x96BA45A8, 0x84A07D0F, 0x41DDBAE5, 0x5A093DE7, 0x2022C68E, 0x0DD5C575, 0x388C6E4F, 0xA042F75E, 0xB135E5FC, 0x9313582B, 0xA7E0FEFF, 0x1ADD3027, 0x9E69DD05, 0x18F7235D, 0x9C64BE47, 0xF0A8E1F5, 0xDE678ACC, 0x18ED4A76, 0xA0239655, 0xD08422CE, 0xE1E21180, 0x95610D75, 0x1286B93C, 0x109D4D39, 0x93427D83, 0xA5F4E4AA, 0x9B59225E, 0xD3FDADF9, 0xA0F2B270, 0x8629CD71, 0x6198B821, 0x155DF5DE, 0x4D652709, 0x8CEDD0C8, 0xE7ED0B0C, 0x139E78EA, 0xF83C10DA, 0xCDFCAF33, 0x9662319C, 0xB69DC87A, 0x35E6FF75, 0xA83098D4, 0xAACF9CEF, 0xDAB964E8, 0x3BA62FC1, 0xBD7E6BFC, 0x1AEF62AD, 0x905E7D29, 0x124D7686, 0x5C297C61, 0x1D1E6397, 0x21CD77BD, 0xC23245CA, 0x7ADC0B16, 0xA410AC37, 0xBAF5F6BC, 0x2666672B, 0xB82E22C0, 0xEA9078F0, 0x0D0F8069, 0x60D289A5, 0x1AB0CF5E, 0x576F79DC, 0xD82C5192, 0xD66241F9, 0xF726F059, 0x93E27682, 0x21F6AB7A, 0xD27B81CB, 0x8CE88777, 0x76CEF2AA, 0x00DCECD1, 0xC18DF842, 0x418C35D1, 0x7097F482, 0x2F3A2F4A, 0x188FAC41, 0xFA29C29D, 0x0AFA0C44, 0xDDEAC62B, 0xD32E28EE, 0xCA6E8490, 0xECAFF48F, 0xBDC7D12D, 0xF69AD200, 0xAA5C38C5, 0x11437CF4, 0x0DBD576D, 0x4262A5D8, 0x05A7E930, 0xC0819BFC, 0x30DA162F, 0x546108AA, 0xF7C01E4D, 0xF2D4ED5C, 0x9630AD9F, 0xC5E3F091, 0xFFF0B1E4, 0x937B6711, 0xBAEFB7F4, 0x29936D32, 0x1F88D16C, 0x7C5A7E0A, 0xEF6AE923, 0x2CDE4C68, 0x36CBAA1F, 0xD371CE31, 0x8B2B5116, 0xE665D130, 0xAFB8BE02, 0x216136BC, 0x197C0E9D, 0x9CD6A9C7, 0x5C2FB623, 0x4B643B99, 0x748351DA, 0x3EF8CF0F, 0xA37AFBAA, 0xD1E20905, 0x3AF5A861, 0x5159F6B3, 0x3DE9A3C7, 0x3AE6FF2D, 0x96AFE441, 0xB87DCADF, 0x42165CEE, 0xD09DA374, 0xA9AEFD6D, 0x3B15B989, 0x19A8F848, 0xFE3AF6D7, 0x444B9607, 0x374BF933, 0x624F0838, 0xFC02FC8D, 0x3D658302, 0xEDD74840, 0x51990A20, 0xB2DA9DCA, 0xBFB7CFA8, 0x32672F31, 0xA300E3CB, 0x097E0AB0, 0x7A347BFC, 0x1D978CA6, 0x17CB62C7, 0x28F4B821, 0xDB51C9EF, 0x69B6AC36, 0x907490B7, 0xDBCBFDDB, 0x1781ED94, 0x4DE54EE5, 0xF6014A99, 0x9F5EE045, 0x704145A2, 0x2B4ED6AB, 0xDC06152D, 0x48881743, 0x3994B43A, 0x23CEBBDA, 0x0EB05C1E, 0x0D0B318E, 0x9B048078, 0x751C9B97, 0xACC7ADDE, 0x2B7F48B2, 0x29AE7659, 0x27EE79B8, 0x8E30E7F2, 0x84444079, 0x25CE1387, 0x8EFA0818, 0x8D71ACEB, 0xF27CA669, 0x291BD802, 0xEA64407D, 0xA1B205D3, 0x2B9D98A4, 0x2CEEC92C, 0x525AD93E, 0xD4CC6BF5, 0x114A0A84, 0x4B6EA4AB, 0x164631B2, 0x84324343, 0xE3210933, 0x539D9360, 0xD418EF71, 0xC8D1972B, 0x2DA0E3C3, 0xB7545BA2, 0xBF92A048, 0x15EF8E25, 0x02493520, 0x9E1B52FA, 0xF9339931, 0x2C1B0492, 0x8D19DB7D, 0xAD6129E3, 0x5CB794A6, 0x8B2EC52E, 0xBDE060AE, 0xEA930864, 0x989E8EA1, 0x2EF1E031, 0x5787D477, 0x816DF5A6, 0x4C9B898D, 0x0896C596, 0xBE59CCBD, 0x587B2108, 0x19C05533, 0x80440D8E, 0x59F9E800, 0x5098A230, 0xA6FDA846, 0xC5056559, 0xE7251A17, 0x328AC02A, 0x157E6911, 0xE96DFF96, 0x5298A3FA, 0x437B3379, 0x56C4E327, 0x40D633EA, 0xAC874E74, 0xBBE052AB, 0x568AED3E, 0xD225B2BE, 0x584CDF0E, 0x8FCA57DC, 0x00FAB0C0, 0xF37B6E41, 0x1707CB08, 0xE3D8A504, 0xDB429967, 0x73D5D71F, 0x229EEA66, 0x5F447DF4, 0xBF50B23E, 0x2F9F7ACA, 0x80955983, 0x6905EC70, 0x711297AF, 0xDBFDE811, 0x448A6E09, 0x90D5598C, 0x6A65F9A9, 0x3D3C0CF3, 0x2FA0B18E, 0xF03F1663, 0xF6E88027, 0x64569994, 0x93C83600, 0x21EB7C41, 0x86AEB24B, 0x7DACAC90, 0x8B991825, 0xA40D9C96, 0x530CFA7E, 0x61BA7FCA, 0x617BBA2F, 0x962F7529, 0x84B732CA, 0x3B1FE657, 0x34F3F158, 0x61C904A2, 0x20EA77A7, 0x83ED3E14, 0x878F8286, 0x88C3F910, 0x7E03A433, 0xE04E97EF, 0x66919FCE, 0x85C8CA04, 0x3A8BF6C7, 0xDFEB7531, 0xF41A9A67, 0xC7B1D033, 0x97EAD252, 0xC381DB63, 0x64310F9E, 0x755FDEE7, 0x46011903, 0xE50BF89F, 0xAB4F1A1F, 0xE0B07596, 0xF2154963, 0xA7AE26E5, 0x41821B1E, 0xD08B2ECC, 0xF730B3B5, 0x1BD9E765, 0x1E603B74, 0xFA5203E9, 0x0F45878C, 0x1A4D0DB9, 0x90ECA359, 0xADA233FB, 0xD3ACF058, 0x0C6F27A9, 0x1B18CB5D, 0x70CD14D9, 0xE39F42F7, 0x823E4739, 0xE4A29E03, 0x7C2F5D18, 0x55206368, 0x38C80B3D, 0xF3C79172, 0x704C8A0D, 0xC0807DDE, 0xA37F0A54, 0xB5D04942, 0xC3276919, 0x1CBCF95E, 0x1619ABDB, 0x991E908C, 0x6EB91795, 0xADDE7093, 0x7F5F26B0, 0x9F86B382, 0x9D302419, 0x837D2070, 0x29E1B322, 0x99B5EF8F, 0x7A66BC86, 0x18853956, 0x9B5FF663, 0xC824BC54, 0xC3C3A327, 0xAD80A98F, 0x3B4BA008, 0xCCF9DE0B, 0x92A32679, 0x42A2D07C, 0xD42E8F63, 0x4BAF6B7A, 0x5899B2B4, 0x0155754A, 0xCA9DD785, 0xA828973D, 0xA63CD1F6, 0x97D41545, 0xC42548D5, 0xA61000A4, 0x3EC4B41D, 0x08F0F76D, 0x91C9C44E, 0xB5D54507, 0x3E053444, 0x14E1111E, 0xA2BA3718, 0xEA167858, 0x83FD9842, 0xD383AC77, 0x65C95B36, 0x8B9D7625, 0x5DFC8021, 0x0ED8CBC4, 0xCC594E85, 0x513BC0A8, 0x0B5D60A2, 0xC32F2E8A, 0x183C9D18, 0x0DD5A732, 0x72F0EFDF, 0xA4350975, 0x2FB0E0EC, 0xB22D54EB, 0x22016773, 0x612D008E, 0x2A59C5A0, 0xF1B42015, 0xEBE50B7D, 0x6B704564, 0x3AC6BF34, 0x6A3335F2, 0x88474D95, 0x3D767C56, 0x7A6A7248, 0xA92832F6, 0x25F52A52, 0x40704693, 0x4E8658DE, 0x11662FA6, 0x75BD2405, 0x3C5EF4BC, 0x88DA69D0, 0x9D7FFA6B, 0xF4505103, 0x26F6AA11, 0xA63D2AA3, 0x180EB10B, 0x8C5A3AC4, 0x14D39BEA, 0x2FF95FBC, 0x9A94922B, 0xAAF7620C, 0xF0F9CC20, 0x1B5B56ED, 0xE45FEFA0, 0x5DE2E750, 0x0D13927F, 0x7068813C, 0x5D711214, 0xBAE9F124, 0x70C3EA3A, 0x8E19AB4F, 0x5F2038CB, 0xD8913D47, 0x8AB8E081, 0x735719C4, 0xB6D76E01, 0xADB780B2, 0x44E7773A, 0x559F3677, 0x4C88696B, 0xC6672FBD, 0x370CF29F, 0x886EA6A0, 0xD75A53F3, 0xC0AA7BCA, 0xC307F408, 0xC9840C36, 0x4915718C, 0x606FD591, 0xC53A333B, 0xDE8CE0F4, 0x08426DF7, 0x3BAED006, 0xE51B6024, 0xC4AACF54, 0x0E78A6F9, 0x403BCAED, 0x5FA4D313, 0x6E0A593C, 0xDADBC66F, 0x8F89318C, 0x871EFE01, 0x165BAC1A, 0x62853961, 0xE14CAEAE, 0x85D95D9C, 0x2A6E6BEC, 0xEF2BC632, 0xD6627E46, 0x67E86F4A, 0x500575DA, 0xE06B47B6, 0x2A48563E, 0x2218FBF5, 0x66F142D6, 0xF33D26C8, 0x44EAA79F, 0x90D88FEB, 0x455099F8, 0x865F7003, 0x89060892, 0x026A9390, 0xCEF2B506, 0x781E961D, 0xA2202790, 0xFC075084, 0xF819D0B1, 0x0C75B63B, 0xB2AA7334, 0x49C9B2C2, 0x587E4019, 0xA6086B9E, 0x87CE7A27, 0x3D7EE3E4, 0x127C3924, 0x168097BB, 0x949EA60F, 0x5F42A1CA, 0x37A0BE1C, 0x4F62686A, 0x501E77E2, 0xB6DFA889, 0xDF989B80, 0xC200216A, 0xF682B35F, 0x8B9868E1, 0x76EF068C, 0x2407D2E5, 0x86C1C026, 0x942D96B1, 0xA47711CB, 0xA9F946E8, 0xFD91619D, 0xCED12427, 0xBBAE6804, 0x44C94469, 0x5205104D, 0x96569869, 0x4649C275, 0xB9D81C46, 0xDC718671, 0x07EA185D, 0x7C3F8197, 0xFCC888E4, 0xE75EB2F8, 0x041714CF, 0x244C351E, 0x0DBAF60D, 0x291C3C5A, 0x0FD0F8F8, 0xDBA6A5F2, 0x833DA016, 0x5DF4B7C0, 0xC5F10F09, 0x7D6A24F0, 0x94037DC0, 0x9661A744, 0xE2A340A5, 0x00061345, 0xBA60C315, 0x756AE6D9, 0x21371580, 0xFD68DFAB, 0x9FDECF4A, 0x98F8A927, 0xD32D3D5C, 0xDCFF8C61, 0x4D25C8DF, 0x66F6DEF0, 0x215A2736, 0x381FFE89, 0x33524E99, 0x559BD04F, 0x15BA91DD, 0x95EB5718, 0xC4218624, 0x6738F02F, 0xD2660B92, 0xB9C3CB8E, 0x7F6C44BC, 0xCB457151, 0x6135F29F, 0xBFF28F5A, 0xDBAA5420, 0x1D52CE89, 0xD4E77975, 0xA7FF1F6A, 0xFDB956EE, 0xCA2C9FF3, 0x6DDFCE96, 0x0CAF2C2C, 0x7D35795D, 0x43BB27AF, 0x07179A67, 0xDE3B35F1, 0xD99892F8, 0x3D35F125, 0xF815CC6F, 0x499EBA52, 0xD876718D, 0x902C733F, 0xEFA2EF43, 0x192D83B9, 0xDEC63300, 0xD99D5058, 0xA7AA07A6, 0x818C1BE7, 0x9A3F114C, 0xEF9607B3, 0x483E952E, 0xB1272DF9, 0x5049917E, 0x91D2CCF0, 0x36E8CD69, 0x056850E6, 0x4DBED755, 0xD5D2E496, 0xD0A81B4B, 0x2854CB95, 0x4CB5E71C, 0x952E11DA, 0x300E87FE, 0x5E348059, 0x9D700948, 0x9EE83112, 0x3FE20735, 0x74794368, 0x42850985, 0x42995A92, 0x8CE9146C, 0xF306502A, 0x6DD8D506, 0xDC8C4D60, 0x84986AF6, 0x30B906E3, 0xCD75D7AA, 0xBC568D2F, 0x6B0D2E26, 0x408608F5, 0xC6EE12D6, 0x06597FA6, 0xAC3FEF15, 0xD6205521, 0xA929DBF4, 0x1FA6793C, 0x13F0327F, 0x25277A64, 0xE1643B86, 0x144EFD29, 0xBC6E9F1B, 0xAADFE377, 0xD7B87F61, 0x2FBCEAFE, 0x18C65491, 0x85AA55A7, 0xCA008F56, 0xAFA84902, 0xCBBE205A], [0x69635205, 0x1F420B04, 0x6BE1DA04, 0xFA2E2203, 0x5735EB05, 0xE1EB8900, 0x753CE103, 0x6D8BBD02, 0x69601E07, 0xC5B00604, 0x4E813707, 0x207C3504, 0x4305E706, 0x25B3D602, 0xDC2EF405, 0x08A45F07, 0x1FB68C05, 0x15B10500, 0xD784FB04, 0x37598300, 0xB0694D07, 0xF82E0903, 0xAA77C402, 0x663C6302, 0xEB791402, 0x9072BE02, 0xE34F2504, 0xDE181A04, 0x9E121100, 0xF3099201, 0xA3513705, 0x106DB402, 0xB49F8102, 0xA52F8B00, 0xF8D12007, 0xA2C71403, 0x7BAEB904, 0x28801604, 0x2F74AD01, 0x7495AA02, 0x0058A905, 0xA50D7A04, 0x93F07902, 0x90A8FA06, 0xBDA9A306, 0xAB421106, 0xB56BD302, 0xCBB8D507, 0x7F14D501, 0xF2B48501, 0xCE9C3204, 0xD8B5B104, 0x403AE106, 0x8C89F004, 0x07579B05, 0xD89F2D07, 0x63A9A604, 0x44C07105, 0x951D0B05, 0x98DEB806, 0xFFCA5302, 0xD5930F04, 0xA5E94D02, 0xBD596101, 0x1E4C1604, 0x0F9C0306, 0x3ABB6201, 0x5F6F7A03, 0x801F3E02, 0x47DCBE01, 0xC42CE402, 0xD31ABA02, 0x4ACD9F00, 0x649AA001, 0x2FF7D503, 0xF7162F00, 0xC2231506, 0x98DE9E01, 0x214EFB02, 0x82BA4A02, 0x4B325F03, 0x6D183B04, 0xBAD06F04, 0x7D4FD803, 0x5F9C5101, 0x37C64F05, 0x65314406, 0x65145900, 0x6A85D407, 0xF147C805, 0xA2E74907, 0xEC807D01, 0x2FBF1101, 0xBE20C902, 0x2356D806, 0x93DF4A06, 0xCC0C2603, 0x8DF2C002, 0x0CBE5B00, 0x5EF37103, 0x19714000, 0x7B5C0706, 0xC26DCB06, 0x12CB3100, 0xB50D2205, 0xD935D203, 0x35EB3607, 0x8C441607, 0xE9E4B902, 0xD808C406, 0x02D98202, 0xA3862505, 0x7FA47507, 0x0BA16C05, 0x4F168F02, 0x48593603, 0x4DDBBB07, 0xC7174203, 0xDBF4FD00, 0x82BE7802, 0xE32E6B05, 0xF4573A00, 0xDA446E02, 0xDFD68705, 0x81B39906, 0xDD58DD05, 0x4BC39900, 0x4F33FE07, 0xA1821606, 0xDC485F07, 0x1059A905, 0xF2D2C807, 0x3EF10107, 0x1421F901, 0xEB551F05, 0x958B1503, 0xEBCBA406, 0xD5C78806, 0x1E633904, 0xFE486400, 0x3F7BAB01, 0xB0D3DC03, 0x5DB13507, 0x145A0401, 0xD0F47C04, 0x19E1BF01, 0xD4765906, 0x8742BF06, 0xBFD25400, 0x1A287E03, 0x3EDC7404, 0x33F5DD01, 0x9E24FD07, 0x5CFF9606, 0xEF83FC06, 0x86598900, 0x53AD7F07, 0x75D4A002, 0x47CF8905, 0x0D066304, 0x538CF504, 0x84018C01, 0x7FEF1604, 0xE7B84801, 0x465F6600, 0x0E5FB106, 0xB376DD07, 0xBE2B5907, 0xF952C702, 0x9A635B03, 0xAB61B507, 0xC1025A05, 0xF4872C03, 0xC9F84D07, 0x087EDE04, 0xD09D7701, 0x83A1B004, 0xF173CD02, 0x6E105C01, 0xBCEB0E07, 0x03BB5500, 0xA21F6502, 0xD36DCE06, 0xAC3B5B06, 0x618AB105, 0x59358401, 0x0A60F100, 0x93285B03, 0x0FBA1901, 0x4CE21103, 0xDCA17600, 0x86545E07, 0x671C6502, 0xB2FAB002, 0x3AED9C02, 0xABDDE903, 0x3754B303, 0x6D0DD303, 0x08996203, 0x3C250B07, 0x6EFD0304, 0x1BAE3106, 0x5BE5C604, 0x46EACF05, 0x3982F700, 0x561EC304, 0xEA48AC02, 0x9357DA02, 0x80EED707, 0xD08DA501, 0x8EBC4004, 0x58A97F04, 0xAE20A104, 0x2F8CA407, 0x31D69004, 0x1B5C2004, 0xE1914407, 0x3787DC03, 0x917BAA05, 0xA0498102, 0x6853D102, 0xB257B802, 0x71203503, 0x3F2B3F01, 0x3422A302, 0x5F5AAD00, 0x07BBC102, 0xA8B14607, 0xC51B6F01, 0x53EC8E00, 0x74631F01, 0xF8DE3602, 0x36F1F501, 0x06125E01, 0xF615E807, 0xC6792804, 0x7DD53A03, 0x2A445805, 0xF1F94302, 0x9107B500, 0xED2B6304, 0x46E78C00, 0xB947BA02, 0x3D95DC04, 0x20C53002, 0x448A8B02, 0xAD4A4E03, 0x4A40F405, 0x9A2FC005, 0x98209C04, 0x118F7305, 0xBB1F3407, 0xDA5F8401, 0x1FD03E04, 0x07A39704, 0x98F95F05, 0x28D4DD04, 0xA4FDDD05, 0x722F4407, 0x9D220706, 0x16D12F05, 0xD328E500, 0xAAD7E301, 0x09BAEB02, 0x52C2E306, 0x6770F104, 0xBB220607, 0x0B860803, 0x5B049D03, 0x4888DD04, 0x5D10D901, 0xAAC98903, 0xAE6AB000, 0xACCA4D03, 0xE5E8C501, 0x63468302, 0xDB04BC07, 0x430F6C04, 0x2C5C7905, 0xD0A9C300, 0x6D649806, 0xDAE74104, 0x07EC0401, 0xC409CF04, 0x1F3BFA01, 0x18A8A307, 0x2F49C300, 0xC5419E05, 0xCC910C02, 0x155DF404, 0x2F536204, 0x6BB0B005, 0x777FB703, 0xFD081207, 0x1CB0BD00, 0x86938A06, 0x104C7F04, 0xF0A72800, 0x1075A206, 0x432B6F05, 0x4C760500, 0x95916E05, 0x7F996702, 0x98880104, 0x95EE3505, 0x59750207, 0xB6917406, 0x0942F302, 0xBB474E03, 0xE692E402, 0x031C3A01, 0xC4A55302, 0x4AE86406, 0xE6A56900, 0x8F2E0D00, 0xA8DAC406, 0x7487FA06, 0xC8A4D304, 0x17C3AB02, 0x60401405, 0xE9201102, 0x714B2E07, 0xAF253C07, 0x7E409105, 0x5063D900, 0x1D7BDF02, 0x1F232500, 0x60E6D603, 0xBF62DC03, 0x618BF206, 0x3D87AF02, 0x4CBF7D07, 0x0E32BC06, 0x5BB2D907, 0x00F19F07, 0xBC045303, 0x256CFB06, 0xF0047E01, 0xDA79B804, 0x68274702, 0x7854B006, 0x6D29F907, 0x41A2D306, 0xEED0E900, 0x27FFC406, 0xD9655F05, 0x93105502, 0x17790604, 0x4D528B03, 0x235D4007, 0xBEDC3F05, 0x3DA7A604, 0x189F5203, 0x50CA1D04, 0x3E929A06, 0x36445606, 0xE0A43C00, 0xB622E407, 0xDA379D05, 0x6481E406, 0x88BA3701, 0x55938B03, 0x461D8E00, 0xEA265400, 0x2474B606, 0x1985C502, 0xE52FAF04, 0x6FFC3200, 0x04D96E00, 0xC2BD9605, 0xF2291A06, 0x039D3A02, 0x069E4300, 0xD28D3400, 0xCEF8DC01, 0xE9885607, 0x0A9ED204, 0x8C44BD03, 0xF397AB05, 0xBBE30B05, 0x7473C005, 0x6CB4BE01, 0x6C665D03, 0xFFA7A705, 0x8AE32703, 0x3D2A8005, 0x43D04501, 0x08FF4404, 0x90E0C203, 0xABF12E04, 0x7BD52006, 0x13E0F607, 0x30A5FB06, 0xA8140C03, 0x385A2101, 0x4A91BE03, 0xD2B3FF05, 0xDC788402, 0x4B562607, 0x657D5300, 0xDE616100, 0x2BB0AE07, 0x8E6D0D02, 0x66FAC401, 0x0025F105, 0xEF686804, 0x3B4F1702, 0x33552502, 0x36E41A07, 0xF7544A01, 0x3433DB07, 0x53DA1B04, 0x46D98302, 0xDA303105, 0x60A6AB01, 0x560D3607, 0x5C855600, 0xBD94CE00, 0xD9CD0604, 0x08E0E606, 0xE0364D00, 0x86621C05, 0x6E347002, 0x6909AD05, 0x74E2D007, 0x1E99E500, 0x21B28602, 0xCA196305, 0x0BE18400, 0x59BD0C05, 0x39D58306, 0xAA034B03, 0xB5DB9A06, 0xD2163F01, 0x2A11EA01, 0xB80D7C03, 0x66920F07, 0xC1E56B02, 0x7CE4E404, 0x74C11D02, 0x68439F04, 0x2BA50B03, 0x25E8A704, 0x22918602, 0x50689802, 0x55175002, 0x069CB903, 0xB80E9600, 0x1FFEDA07, 0x44F6C103, 0x28E82304, 0x384A2C03, 0x48F70206, 0x5E62AA06, 0x50795807, 0xF04B9602, 0x33B6A603, 0x347E6203, 0xE474BF03, 0x2F2ADA03, 0x40B4AA04, 0x13810201, 0x5C0A8603, 0x76458704, 0xED318C07, 0x9CBC4800, 0x42485401, 0xEA229901, 0x3234D103, 0x02462E07, 0x387F9405, 0xD9595004, 0xB1B4BC03, 0x6E19D104, 0x7FA33E00, 0x79B61A03, 0x41499F04, 0x999DB203, 0x56538F04, 0x4BCD7607, 0x85BA1303, 0xA3BB0002, 0x81ABB800, 0x64C35A00, 0x4FD95404, 0x8AFA9700, 0xF26FCF06, 0x50605902, 0x64E34205, 0x46F0CB00, 0x4A62D903, 0x0785B605, 0xA161FC04, 0x735ABE05, 0xF7CF3A02, 0xE3CFCF06, 0xBA9F6302, 0x8F3C7B05, 0x6CFE7C01, 0xD394CB01, 0x9BD62803, 0xE7FEEC02, 0xF0580207, 0xB6991702, 0xCBB4FE02, 0x62A31105, 0xABC68900, 0xD405A306, 0x52A8B805, 0x8C133102, 0xB50AC303, 0x0D513E04, 0x49525606, 0xF4DC2104, 0x5788D504, 0x5806D105, 0x69A8EE05, 0x1D4D4004, 0x8F0C0E01, 0x114FB604, 0x1E885E00, 0x90A88306, 0x17071B03, 0x6184A302, 0x9B6CEB01, 0xB8E35C05, 0x3D731404, 0x2F600C06, 0xFF33F000, 0x337B1605, 0x78DCA904, 0x24F37700, 0x27157903, 0x09632A06, 0x11162903, 0x18982F02, 0xDE2B4507, 0x6827F906, 0xA65D0C01, 0x65506707, 0x9AB03203, 0x09608500, 0x88379902, 0x491EF806, 0x92CA3600, 0xEA2EAA07, 0x2FBD5804, 0xACD84D00, 0x7EEC6B02, 0x64862703, 0xCF815507, 0x2163E901, 0x6CEE1301, 0xCB3F3004, 0xCAF34202, 0xDD8BB000, 0xF85C0F05, 0x97A10405, 0xFB5BFE07, 0x0BF9BD07, 0xB15BC601, 0x36149200, 0xF5027200, 0x4942CA04, 0x9EB64F03, 0x7BDB8302, 0x1AC6C703, 0x14EAB306, 0x5A887B07, 0x312D7507, 0xC8D17E03, 0x3DCEA504, 0x95F9D306, 0x4BC2DC00, 0xD0727004, 0xDB66C101, 0x47068601, 0x65934807, 0x885FEE00, 0x82883F06, 0x9871D706, 0x90A54501, 0x7A189102, 0x06466E00, 0xA6509506, 0x8C4C2206, 0xCEB18B06, 0x5D9F5101, 0x83A8F101, 0x85933006, 0xE253C606, 0x476EAB01, 0x1ED04105, 0x6918A200, 0xFA428F03, 0xE5BCF804, 0x0734AC07, 0xB5C39F04, 0x2B90F407, 0x7592CF03, 0x3D6F5205, 0x238BE205, 0x3AEAEE00, 0x332C9803, 0x82421204, 0x50F29603, 0x4CA19501, 0xAE5E2E07, 0x17E7E600, 0x5EC83D00, 0xF86F8B03, 0xFEB6C704, 0x3B856F07, 0x78A9BA05, 0x53DF0E03, 0xABAEF104, 0xB0D1A303, 0x9DBA0702, 0x89819203, 0xFD3D6E00, 0x63DB2E01, 0xC3162106, 0x5B308607, 0xAD3F5C06, 0x9BE44A04, 0x8D133104, 0xC6638906, 0x8CC0CD07, 0xEECB7901, 0xB223E804, 0xC619F903, 0xF160B304, 0x3CACE506, 0x34C34005, 0x2C10A906, 0x09B51C06, 0x36853B01, 0xD6B3D500, 0x808C8304, 0xA8086306, 0xEAE76A07, 0x8A961306, 0x4EC14E05, 0x5408BC05, 0xA9024603, 0x5CB58F06, 0x5BA5D504, 0xB018FC02, 0x709B4B04, 0xAF28BF05, 0xF289FE07, 0x7A205204, 0x4FAAAF03, 0xA8FE8802, 0x0CC62604, 0x1D6F4D03, 0x74A23E05, 0x1E63F506, 0x3EAF4903, 0x15BC8A04, 0x37096E02, 0x8B9BC603, 0xDE94CA03, 0x846DF302, 0x6AC9AB01, 0xE1C9C501, 0xA6C1B305, 0x4BE12500, 0xD80F3E03, 0xEFBDBA07, 0x80C7FF06, 0x951EED02, 0x6425B904, 0x38425B07, 0x49EF8F07, 0x4B02A104, 0x246CAA00, 0x320C3101, 0x0D8D7B06, 0xEC389D05, 0x03164106, 0x504F9D03, 0x1B470300, 0x451F2F00, 0xF8F17A02, 0x50681E06, 0x34B75A05, 0xE3996E03, 0x75440107, 0xC3109B06, 0x1AE58B06, 0xC506C002, 0xB8E78802, 0xDB455B07, 0x651C6F01, 0x485E4603, 0xCA683C04, 0x8D7EE705, 0x31F75100, 0xFFC22D01, 0x1419C101, 0xA1A6AD02, 0x106B4502, 0x9F046503, 0xA6803F01, 0x28ADB403, 0xE280C401, 0xDBF6C206, 0x84205106, 0x11DCE500, 0x1519DC02, 0x96068401, 0xA0874003, 0x200CC407, 0x033E9603, 0xE1AE2B06, 0x5695BC07, 0x4420DF02, 0xEE239800, 0xB0717602, 0x06081C05, 0x63E47904, 0x4B414102, 0x2B582405, 0x707E0706, 0x05999602, 0x78054801, 0x0F23AF02, 0x24D8A907, 0x395F9503, 0x64614104, 0x8D765E03, 0x41EBDB02, 0x5EC21A03, 0xA13DB401, 0xCCF30F04, 0x423BB902, 0x63CDD000, 0x3740C305, 0x9D7FE105, 0x6B00D406, 0x74622106, 0xF26C5300, 0x02943C00, 0xCA6B6B00, 0x5A97C402, 0xA4102B02, 0x25197105, 0x98CE5501, 0x676B8C03, 0x0C6AA002, 0x1CAC0F06, 0x255BAB02, 0xA3B98F03, 0xEC611201, 0xB591A300, 0x7186DB07, 0x1C63FA04, 0xBEEF3907, 0x2AC1ED06, 0x70C7F404, 0x14D13D00, 0x038C8800, 0x5CF9FF00, 0x4EBDEF02, 0xCC2C9C02, 0xC3F3D302, 0x3C859B02, 0x103C6002, 0x1D43F800, 0xE40B2503, 0xF8FE6004, 0xD5E25002, 0x2E723904, 0x6E3A5102, 0xCD2FD805, 0xC2944806, 0x69274905, 0x80662603, 0x75047E04, 0xB60EFA02, 0x613CBA06, 0xA9A97F04, 0xFEA9C102, 0xB7CF5A02, 0xD25E9802, 0xAC1B4404, 0x186E0D06, 0xD7A4D504, 0x2E7F2A04, 0xAB2D9006, 0x207BE800, 0xCE6E7F01, 0x2A313401, 0xCE7C4D05, 0x489FC404, 0xA03BE303, 0xF10C9900, 0xBD21BD00, 0x0D6B4601, 0x78E20207, 0x6CD08E03, 0xB97EBC04, 0xF79BD507, 0x39205704, 0x14EFC002, 0xDCDEB300, 0x1BB76005, 0x973DEE04, 0xE1168801, 0x9B5D1806, 0x3E1ED901, 0x2EA03303, 0x2F198702, 0xC52C0502, 0xF502E706, 0xD7854304, 0xECD8C003, 0x72A7EA03, 0x84D69504, 0xADBC9C02, 0xF4B41D05, 0x8ABD1304, 0xE4067B05, 0x620B6502, 0x94316804, 0x28CECD07, 0x815B8105, 0x0C63BF02, 0xCDC80804, 0x9CD9F107, 0x4DDBD904, 0xA1FCE207, 0xBE157F06, 0xD2324C03, 0x6FE0A507, 0x42D64903, 0x23534706, 0xAB4E1005, 0x6244FD04, 0x61047503, 0x43D9B102, 0xC7A91E06, 0x7DC97B06, 0x6B9A6102, 0xA7F15406, 0x70E65301, 0xC097E500, 0xAB34F205, 0x59C4F704, 0x57D56D02, 0x01FCA006, 0x13E30507, 0xFE6D0505, 0x24D58D01, 0xBE7E3F03, 0x2E5A0702, 0xF299BF05, 0x7810AA00, 0x2EB8C600, 0x59A59401, 0xBB62AC06, 0x80741803, 0xBE164302, 0x4C675B03, 0xDA789C06, 0x5BBE9806, 0xF3539307, 0xD43CAE02, 0xD4C9CA00, 0x93BF6D00, 0x6FCE1B06, 0x884CB704, 0x3E11E007, 0x4CC73E01, 0xBED65A03, 0x5D216003, 0xE0B0F307, 0x6A7BB301, 0xCCA0B205, 0x5D2FD303, 0x4DB96B01, 0xD174A301, 0x73F9A806, 0x19AC8101, 0x016B1704, 0x8EC98507, 0x53DB1300, 0xCE475207, 0x49FF6707, 0x3494B200, 0xB3DE1F06, 0xF07CC505, 0xA2FDD107, 0xE5680903, 0xA41BC001, 0x74C3BA03, 0xE1CD4407, 0xBACDE907, 0x1AE79901, 0xEA812401, 0x2E71D107, 0xCB4CCD01, 0x6D7B3701, 0x0E5B6005, 0xD2EA2F00, 0x7B33B202, 0x302D6207, 0xD94AA000, 0x46BED302, 0x4929F007, 0x61A22404, 0xB5856507, 0xBE168807, 0x17411507, 0x4218C101, 0xF9290005, 0x4E8B9207, 0x7699F804, 0x2C085804, 0x5DCF1901, 0x8A4CB501, 0x2C28F504, 0x825F0902, 0x26DBDF01, 0x89BCB706, 0x56A68205, 0x3D625A07, 0x94B7BD04, 0x62A93706, 0x2D667A05, 0xC78E6B04, 0x0F682106, 0xA7755102, 0x4ACB2100, 0x15329B00, 0xAD1C5F01, 0xDD9B5806, 0x8B16C006, 0xF9B76203, 0x82073007, 0x5323B206, 0x88075406, 0x27031601, 0x11873E02, 0x33400105, 0xD7050400, 0xD24A8C02, 0x42D23602, 0x21E7F205, 0x0AF67C06, 0xCD8ABB05, 0x1AEF6000, 0x8E70AC05, 0x7DBCE207, 0x8697C907, 0x6092E400, 0x1A41E501, 0xFB517D04, 0xA801CD05, 0xEB811007, 0x640FCD04, 0xD2189803, 0x6C093303, 0xD186B604, 0x33D69903, 0x3DC77407, 0x4A7ECE07, 0x45FBE803, 0x9604F007, 0x22969007, 0xB7017704, 0xD1C82705, 0x8D31E907, 0x069C6200, 0x7398C700, 0x1C1C4C02, 0x97B74307, 0x62E6B105, 0x002F5401, 0x8C05AC02, 0x04AE7801, 0x07DC5301, 0xF517D607, 0xB322BC05, 0xD6584605, 0xD128E307, 0x50977604, 0x6ACC1D04, 0x9F5EC504, 0x2F565F02, 0xD9D32A01, 0x246CE107, 0xC2779107, 0xEFA8FC04, 0x8633D902, 0x691E1605, 0x9D9DB306, 0xE0998600, 0x9F510504, 0xBAB69F02, 0xFF338F03, 0x1DED4304, 0x088C6004, 0x876B4202, 0x78A06206, 0xEC48A705, 0xCEA60503, 0x1B84E104, 0x26FE7101, 0xFADC7A05, 0x0A6CF400, 0xC82E0500, 0x84091F05, 0xFF85E306, 0x103F1701, 0xF26D1605, 0xE83A7907, 0x93333C01, 0xF5356100, 0x0009C006, 0x63F7D504, 0x3D99C702, 0xEE9A0407, 0xBDF27202, 0xB55C1D02, 0xFE3EA505, 0xB16A4606, 0x2BDD7F07, 0xA36D9703, 0xC3BAEE05, 0x9BE21503, 0x978D0D00, 0x2F29E000, 0x304AAC01, 0xEA68D405, 0x7A2EF907, 0xB80E7904, 0xE16D4100, 0x9C26C001, 0xC04F5606, 0xD18F4D06]) \ No newline at end of file diff --git a/lib/ntr_twl_srl.py b/lib/ntr_twl_srl.py new file mode 100644 index 0000000..528f364 --- /dev/null +++ b/lib/ntr_twl_srl.py @@ -0,0 +1,833 @@ +from .common import * +from .keys import * + +magic30 = 0x72636E65 # 'encr' +magic34 = 0x6A624F79 # 'yObj' +decrypted_id = 0xE7FFDEFF +block_size = 512 + +def mod_add(a, b): + return (a + b) % (2 ** 32) + +def blowfish_encrypt(key, xl, xr): # xl and xr are u32 + a = xl + b = xr + for i in range(0, 16): + c = key[i] ^ a + a = b ^ f(key, c) + b = c + + xr = a ^ key[16] + xl = b ^ key[17] + return xl, xr + +def blowfish_decrypt(key, xl, xr): + a = xl + b = xr + for i in range(17, 1, -1): + c = key[i] ^ a + a = b ^ f(key, c) + b = c + + xl = b ^ key[0] + xr = a ^ key[1] + return xl, xr + +def f(key, v): + a = key[18 + 0 + ((v >> 24) & 0xFF)] + b = key[18 + 256 + ((v >> 16) & 0xFF)] + c = key[18 + 512 + ((v >> 8) & 0xFF)] + d = key[18 + 768 + ((v >> 0) & 0xFF)] + + return mod_add((mod_add(a, b) ^ c), d) + +def apply_keycode(key, mod, keycode): # keycode is an array of size 3 + mod //= 4 + + keycode[2], keycode[1] = blowfish_encrypt(key, keycode[2], keycode[1]) + keycode[1], keycode[0] = blowfish_encrypt(key, keycode[1], keycode[0]) + + tmp1 = tmp2 = 0 + for i in range(0, 18): + key[i] ^= byteswap32(keycode[i % mod]) + for i in range(0, 18 + 1024, 2): + tmp1, tmp2 = blowfish_encrypt(key, tmp1, tmp2) + key[i + 0] = tmp1 + key[i + 1] = tmp2 + + return key + +def init_keycode(key, gamecode, level, mod): + key = [byteswap32(i) for i in key] # Original table is raw bytes which is big endian + keycode = [gamecode, gamecode // 2, gamecode * 2] + + if level >= 1: + key = apply_keycode(key, mod, keycode) + if level >= 2: + key = apply_keycode(key, mod, keycode) + keycode[1] *= 2 + keycode[2] //= 2 + if level >= 3: + key = apply_keycode(key, mod, keycode) + + return key + +def get_rsa_key_idx(hdr, hdr_ext): # The RSA key to be used depends on which bits in the titleID are set + if hdr.unit_code == 0 and readbe(hdr_ext.sig) != 0: + return 3 + elif hdr.unit_code == 2 or hdr.unit_code == 3: + if (hdr_ext.titleID_hi >> 1) & 1: + return 0 + elif (hdr_ext.titleID_hi >> 4) & 1: + return 2 + elif hdr_ext.titleID_hi & 1: + return 1 + else: + return 3 + +class NTRBaseHdr(Structure): # For all games, 0x0 - 0x17F + _pack_ = 1 + + _fields_ = [ + ('game_title', c_char * 12), + ('game_code', c_char * 4), + ('maker_code', c_char * 2), + ('unit_code', c_uint8), + ('encryption_seed_select', c_uint8), + ('device_capacity', c_uint8), + ('reserved1', c_uint8 * 7), + ('data1', c_uint8), # DS: reserved, DSi enhanced/exclusive: crypto flags + ('data2', c_uint8), # DS: region, DSi enhanced/exclusive: permit jump + ('rom_ver', c_uint8), + ('autostart', c_uint8), + ('arm9_rom_offset', c_uint32), + ('arm9_entry_addr', c_uint32), + ('arm9_ram_addr', c_uint32), + ('arm9_size', c_uint32), + ('arm7_rom_offset', c_uint32), + ('arm7_entry_addr', c_uint32), + ('arm7_ram_addr', c_uint32), + ('arm7_size', c_uint32), + ('fnt_offset', c_uint32), + ('fnt_size', c_uint32), + ('fat_offset', c_uint32), + ('fat_size', c_uint32), + ('arm9_overlay_offset', c_uint32), + ('arm9_overlay_size', c_uint32), + ('arm7_overlay_offset', c_uint32), + ('arm7_overlay_size', c_uint32), + ('rom_control_normal', c_uint32), + ('rom_control_key1', c_uint32), + ('banner_offset', c_uint32), + ('secure_area_crc', c_uint16), + ('secure_area_delay', c_uint16), + ('arm9_autoload_ram_addr', c_uint32), + ('arm7_autoload_ram_addr', c_uint32), + ('secure_area_disable', c_uint64), + ('ntr_rom_size', c_uint32), + ('hdr_size', c_uint32), + ('data3', c_uint32), # DS: unknown, DS games after DSi / DSi enhanced/exclusive: ARM9 parameters table offset + ('data4', c_uint32), # DS: reserved, DS games after DSi / DSi enhanced/exclusive: ARM7 parameters table offset + ('data5', c_uint16), # DS: reserved, DSi enhanced/exclusive: NTR ROM region end + ('data6', c_uint16), # DS: reserved, DSi enhanced/exclusive: TWL ROM region start + ('nand_rom_end', c_uint16), + ('nand_rw_start', c_uint16), + ('reserved2', c_uint8 * 0x28), + ('logo', c_uint8 * 0x9C), + ('logo_crc', c_uint16), + ('hdr_crc', c_uint16), + ('debug_rom_offset', c_uint32), + ('debug_size', c_uint32), + ('debug_ram_addr', c_uint32), + ('reserved3', c_uint8 * 0x14) + ] + + def __new__(cls, buf): + return cls.from_buffer_copy(buf) + + def __init__(self, data): + pass + +class NTRExtendedHdr(Structure): # For DS games released after the DSi, 0x1BF - 0x1000 + _pack_ = 1 + + _fields_ = [ + ('flags', c_uint8), + ('reserved1', c_uint8 * 0x17C), + ('banner_hmac', c_uint8 * 20), + ('reserved2', c_uint8 * 0x28), + ('hdr_arm9_arm7_hmac', c_uint8 * 20), + ('arm9overlay_fat_hmac', c_uint8 * 20), + ('reserved3', c_uint8 * 0xBE0), + ('sig', c_uint8 * 0x80) + ] + + def __new__(cls, buf): + return cls.from_buffer_copy(buf) + + def __init__(self, data): + pass + +class TWLExtendedHdr(Structure): # For DSi enhanced or exclusive games, 0x180 - 0x1000 + _pack_ = 1 + + _fields_ = [ + ('global_mbk1_5_settings', c_uint8 * 20), + ('local_mbk6_8_settings_wram_arm9', c_uint8 * 12), + ('local_mbk6_8_settings_wram_arm7', c_uint8 * 12), + ('global_mbk9_wram_write_protect', c_uint8 * 3), + ('global_wramcnt', c_uint8), + ('region', c_uint32), + ('access_control', c_uint32), + ('arm7_scfg_ext7', c_uint32), + ('reserved1', c_uint8 * 3), + ('flags', c_uint8), + ('arm9i_rom_offset', c_uint32), + ('reserved2', c_uint32), + ('arm9i_ram_addr', c_uint32), + ('arm9i_size', c_uint32), + ('arm7i_rom_offset', c_uint32), + ('arm7i_ram_addr_sd', c_uint32), + ('arm7i_ram_addr', c_uint32), + ('arm7i_size', c_uint32), + ('ntr_digest_region_offset', c_uint32), + ('ntr_digest_region_size', c_uint32), + ('twl_digest_region_offset', c_uint32), + ('twl_digest_region_size', c_uint32), + ('digest1_table_offset', c_uint32), + ('digest1_table_size', c_uint32), + ('digest2_table_offset', c_uint32), + ('digest2_table_size', c_uint32), + ('digest1_block_size', c_uint32), + ('digest2_digest1_count', c_uint32), + ('banner_size', c_uint32), + ('shared2_0000_size', c_uint8), + ('shared2_0001_size', c_uint8), + ('eula_ver', c_uint8), + ('use_ratings', c_uint8), + ('total_rom_size', c_uint32), + ('shared2_0002_size', c_uint8), + ('shared2_0003_size', c_uint8), + ('shared2_0004_size', c_uint8), + ('shared2_0005_size', c_uint8), + ('arm9i_params_table_offset', c_uint32), + ('arm7i_params_table_offset', c_uint32), + ('modcrypt_area_1_offset', c_uint32), + ('modcrypt_area_1_size', c_uint32), + ('modcrypt_area_2_offset', c_uint32), + ('modcrypt_area_2_size', c_uint32), + ('titleID_lo', c_uint32), + ('titleID_hi', c_uint32), + ('pub_save_data_size', c_uint32), + ('priv_save_data_size', c_uint32), + ('reserved3', c_uint8 * 0xB0), + ('parental_control', c_uint8 * 16), + ('arm9_hmac', c_uint8 * 20), + ('arm7_hmac', c_uint8 * 20), + ('digest2_hmac', c_uint8 * 20), + ('banner_hmac', c_uint8 * 20), + ('arm9i_hmac', c_uint8 * 20), + ('arm7i_hmac', c_uint8 * 20), + ('reserved4', c_uint8 * 40), + ('arm9_no_secure_area_hmac', c_uint8 * 20), + ('reserved5', c_uint8 * 0xA4C), + ('debug_args', c_uint8 * 0x180), + ('sig', c_uint8 * 0x80) + ] + + def __new__(cls, buf): + return cls.from_buffer_copy(buf) + + def __init__(self, data): + pass + +class KeyTable(Structure): # In ROM dumps, the NTR KeyTable is all '00', and the TWL KeyTable contains mirrors of the data in 0x8000 - 0x8FFF + _pack_ = 1 + + _fields_ = [ + ('reserved_1', c_uint8 * 0x600), + ('p_array', c_uint8 * 0x48), + ('reserved_2', c_uint8 * 0x5B8), + ('s_boxes', c_uint8 * 0x1000), + ('reserved_3', c_uint8 * 0x400), + ('test_pattern', c_uint8 * 0x1000) # Only in NTR KeyTable + ] + + def __new__(cls, buf): + return cls.from_buffer_copy(buf) + + def __init__(self, data): + pass + +class SRLReader: + def __init__(self, file, dev=0): + self.file = file + self.dev = dev + self.media = 'Game card' + + with open(file, 'rb') as f: + self.hdr = NTRBaseHdr(f.read(0x180)) + if self.hdr.unit_code == 0: + f.seek(0x1BF) + self.hdr_ext = NTRExtendedHdr(f.read(0xE41)) + elif self.hdr.unit_code == 2 or self.hdr.unit_code == 3: + self.hdr_ext = TWLExtendedHdr(f.read(0xE80)) + if (self.hdr_ext.titleID_hi >> 2) & 1: + self.media = 'NAND' + + if self.media == 'Game card': + f.seek(0x1000) + self.keytable = KeyTable(f.read(0x3000)) + if self.hdr.unit_code == 2 or self.hdr.unit_code == 3: + f.seek(self.hdr.data6 * 0x80000) + self.keytable_2 = KeyTable(f.read(0x3000)) + + # Check NTR secure area + if self.hdr.arm9_rom_offset != 0x4000: + self.secure_area_status = 'not present' + else: + f.seek(0x4000) + tmp1 = readle(f.read(4)) + tmp2 = readle(f.read(4)) + if tmp1 == 0 and tmp2 == 0: + self.secure_area_status = 'empty' + elif (tmp1, tmp2) in [ + # properly decrypted standard value + (decrypted_id, decrypted_id), + # properly decrypted non-standard value + (0xD0D48B67, 0x39392F23), # Dragon Quest 5 (EU) + (0x014A191A, 0xA5C470B9), # Dragon Quest 5 (USA) + (0x7829BC8D, 0x9968EF44), # Dragon Quest 5 (JP) + (0xC4A15AB8, 0xD2E667C8), # Prince of Persia (EU) + (0xD5E97D20, 0x21B2A159), # Prince of Persia (USA) + # properly decrypted prototype value + (0xBA35F813, 0xB691AAE8), + # improperly decrypted empty secure area (decrypt empty with woodsec) + (0xE386C397, 0x82775B7E), + (0xF98415B8, 0x698068FC), + (0xA71329EE, 0x2A1D4C38), + (0xC44DCC48, 0x38B6F8CB), + (0x3A9323B5, 0xC0387241), + ]: + self.secure_area_status = 'decrypted' + else: + self.secure_area_status = 'encrypted' + + files = {} + + files['header.bin'] = { + 'name': 'Header', + 'offset': 0, + 'size': 0x1000 + } + + if self.media == 'Game card' and bytes(self.keytable) != b'\x00' * 0x3000: # Only exists for game card SRLs + files['keytable.bin'] = { + 'name': 'KeyTable', + 'offset': 0x1000, + 'size': 0x3000 + } + + if self.hdr.arm9_rom_offset: + size = self.hdr.arm9_size + with open(file, 'rb') as f: + f.seek(self.hdr.arm9_rom_offset + self.hdr.arm9_size) + if readle(f.read(4)) == 0xDEC00621: + size += 0xC + files['arm9.bin'] = { + 'name': 'ARM9', + 'offset': self.hdr.arm9_rom_offset, + 'size': size + } + + if self.hdr.arm9_overlay_offset: + files['arm9overlay.bin'] = { + 'name': 'ARM9 overlay', + 'offset': self.hdr.arm9_overlay_offset, + 'size': self.hdr.arm9_overlay_size + } + + if self.hdr.arm7_rom_offset: + files['arm7.bin'] = { + 'name': 'ARM7', + 'offset': self.hdr.arm7_rom_offset, + 'size': self.hdr.arm7_size + } + + if self.hdr.arm7_overlay_offset: + files['arm7overlay.bin'] = { + 'name': 'ARM7 overlay', + 'offset': self.hdr.arm7_overlay_offset, + 'size': self.hdr.arm7_overlay_size + } + + if self.hdr.banner_offset: + with open(file, 'rb') as f: + f.seek(self.hdr.banner_offset) + ver = readle(f.read(2)) + banner_sizes = { 0x0001: 0x0840, + 0x0002: 0x0940, + 0x0003: 0x1240, + 0x0103: 0x23C0 } + if self.hdr.unit_code == 0: + size = banner_sizes[ver] + elif self.hdr.unit_code == 2 or self.hdr.unit_code == 3: + size = self.hdr_ext.banner_size + files['banner.bin'] = { + 'name': 'Banner', + 'offset': self.hdr.banner_offset, + 'size': size + } + + if self.media == 'Game card' and (self.hdr.unit_code == 2 or self.hdr.unit_code == 3): # Only exists for game card SRLs + if bytes(self.keytable_2) != b'\x00' * 0x3000: + files['keytable2.bin'] = { + 'name': 'KeyTable2', + 'offset': self.hdr.data6 * 0x80000, + 'size': 0x3000 + } + + if self.hdr.unit_code == 2 or self.hdr.unit_code == 3: + if self.hdr_ext.arm9i_rom_offset: + files['arm9i.bin'] = { + 'name': 'ARM9i', + 'offset': self.hdr_ext.arm9i_rom_offset, + 'size': self.hdr_ext.arm9i_size + } + + if self.hdr_ext.arm7i_rom_offset: + files['arm7i.bin'] = { + 'name': 'ARM7i', + 'offset': self.hdr_ext.arm7i_rom_offset, + 'size': self.hdr_ext.arm7i_size + } + + # TODO: parse FNT/FAT + self.files = files + + # Generate modcrypt keys (if present) + if self.hdr.unit_code == 0: + self.modcrypted = False + elif self.hdr.unit_code == 2 or self.hdr.unit_code == 3: + self.modcrypted = True + if self.hdr.data1 >> 1 == 0: + self.modcrypted = False + + modcrypt = [] + if self.modcrypted: + if (self.hdr.data1 >> 2 & 1) or ((self.hdr_ext.flags >> 7) & 1): # ModcryptKeyDebug or DeveloperApp + self.normal_key = bytes(self.hdr)[:16] + else: + keyX = b'Nintendo' + bytes(self.hdr.game_code) + bytes(self.hdr.game_code)[::-1] + keyY = bytes(self.hdr_ext.arm9i_hmac)[:16] + self.normal_key = TWL.key_scrambler(readle(keyX), readle(keyY))[::-1] + self.normal_key = self.normal_key[::-1] # Key and IV needs to be reversed before use + + if self.hdr_ext.modcrypt_area_1_offset: + modcrypt.append({ + 'name': 'modcrypt area 1', + 'offset': self.hdr_ext.modcrypt_area_1_offset, + 'size': self.hdr_ext.modcrypt_area_1_size, + 'key': self.normal_key, + 'counter': bytes(self.hdr_ext.arm9_hmac)[:16][::-1] + }) + if self.hdr_ext.modcrypt_area_2_offset: + modcrypt.append({ + 'name': 'modcrypt area 2', + 'offset': self.hdr_ext.modcrypt_area_2_offset, + 'size': self.hdr_ext.modcrypt_area_2_size, + 'key': self.normal_key, + 'counter': bytes(self.hdr_ext.arm7_hmac)[:16][::-1] + }) + self.modcrypt = modcrypt + + def decrypt_secure_area(self, secure_area, key): + # Checks + if self.secure_area_status != 'encrypted': + raise Exception(f'Secure area is {self.secure_area_status}, cannot be decrypted') + + # Initialize with level 2, modulo 8 and decrypt first 8 bytes of secure area + key_lvl2 = init_keycode(key, readle(self.hdr.game_code), 2, 8) + p1, p0 = blowfish_decrypt(key_lvl2, readle(secure_area[4:8]), readle(secure_area[:4])) + secure_area = int32tobytes(p0) + int32tobytes(p1) + secure_area[8:] + + # Initialize again with level 3, modulo 8 and decrypt first 2KB of secure area + key_lvl3 = init_keycode(key, readle(self.hdr.game_code), 3, 8) + for i in range(0, 0x800, 8): + p1, p0 = blowfish_decrypt(key_lvl3, readle(secure_area[i + 4:i + 8]), readle(secure_area[i:i + 4])) + secure_area = secure_area[:i] + int32tobytes(p0) + int32tobytes(p1) + secure_area[i + 8:] + + if readle(secure_area[:4]) == magic30 and readle(secure_area[4:8]) == magic34: + secure_area = int32tobytes(decrypted_id) + int32tobytes(decrypted_id) + secure_area[8:] + else: + raise Exception('Secure area ID decryption failed') + + secure_area_crc = readle(secure_area[0xE:0x10]) + crc_calculated = crc16(list(secure_area[0x10:0x800])) + if secure_area_crc != crc_calculated: + raise Exception('Secure area CRC invalid') + + return secure_area + + def encrypt_secure_area(self, secure_area, key): + # Checks + if self.secure_area_status != 'decrypted': + raise Exception(f'Secure area is {self.secure_area_status}, cannot be encrypted') + + # Set the secure area ID, which was overwritten with decrypted_id + secure_area = int32tobytes(magic30) + int32tobytes(magic34) + secure_area[8:] + + # Initialize with level 3, modulo 8 and encrypt first 2KB of secure area + key_lvl3 = init_keycode(key, readle(self.hdr.game_code), 3, 8) + for i in range(0, 0x800, 8): + p1, p0 = blowfish_encrypt(key_lvl3, readle(secure_area[i + 4:i + 8]), readle(secure_area[i:i + 4])) + secure_area = secure_area[:i] + int32tobytes(p0) + int32tobytes(p1) + secure_area[i + 8:] + + # Initialize with level 2, modulo 8 and encrypt first 8 bytes of secure area + key_lvl2 = init_keycode(key, readle(self.hdr.game_code), 2, 8) + p1, p0 = blowfish_encrypt(key_lvl2, readle(secure_area[4:8]), readle(secure_area[:4])) + secure_area = int32tobytes(p0) + int32tobytes(p1) + secure_area[8:] + + return secure_area + + def regen_undumpable(self): + # Checks + if self.media == 'NAND': + raise Exception('No undumpable region to re-generate for NAND SRL') + + with open(os.path.join(resources_dir, 'test_pattern.bin'), 'rb') as f: + test_pattern = f.read() + with open(os.path.join(resources_dir, 'keytable2_dev.bin'), 'rb') as f: + keytable2_dev = f.read() + + shutil.copyfile(self.file, 'new.nds') + with open('new.nds', 'r+b') as f: + # KeyTable (NTR) + key = init_keycode(NTR.blowfish_key, readle(self.hdr.game_code), 2, 8) + + f.seek(0x1600) + for i in range(0, 18): + f.write(int32tobytes(key[i])) + + f.seek(0x1C00) + for i in range(18, 18 + 1024): + f.write(int32tobytes(key[i])) + + f.seek(0x3000) + f.write(test_pattern) + + # KeyTable2 (TWL) + if self.hdr.unit_code == 2 or self.hdr.unit_code == 3: + if self.dev: + f.seek(self.hdr.data6 * 0x80000) + f.write(keytable2_dev) + else: + key = init_keycode(TWL.blowfish_key[0], readle(self.hdr.game_code), 1, 8) + + f.seek(self.hdr.data6 * 0x80000 + 0x600) + for i in range(0, 18): + f.write(int32tobytes(key[i])) + + f.seek(self.hdr.data6 * 0x80000 + 0xC00) + for i in range(18, 18 + 1024): + f.write(int32tobytes(key[i])) + + print('Wrote to new.nds') + + def decrypt_modcrypt(self): + if self.modcrypted: + shutil.copyfile(self.file, 'decrypted.nds') + f = open(self.file, 'rb') + for i in self.modcrypt: + g = open('decrypted.nds', 'r+b') + f.seek(i['offset']) + g.seek(i['offset']) + + counter = bytearray(i['counter']) + for data in read_chunks(f, i['size']): + for j in range(len(data) // 16): + output, counter = TWL.aes_ctr_block(i['key'], counter, data[j * 16:(j + 1) * 16]) + g.write(output) + + print(f'Decrypted {i["name"]}') + g.close() + f.close() + print(f'Wrote to decrypted.nds') + else: + raise Exception('Not modcrypted') + + def verify(self): + # NOTE: Some checks (those that involve the ARM9) will report FAIL if (NTR) secure area is decrypted; since the HMACs are calculated over the ARM9 with encrypted secure area + + # Decrypt modcrypt first (if present) since HMACs and digests are calculated with modcrypt decrypted + file = self.file + if self.hdr.data1 >> 1 != 0: + sys.stdout = open(os.devnull, 'w') # Block print statements + self.decrypt_modcrypt() + file = 'decrypted.nds' + sys.stdout = sys.__stdout__ + + crc_check = [] + with open(file, 'rb') as f: + f.seek(self.hdr.arm9_rom_offset) + data = f.read(0x4000) + crc_check.append(('Secure area', crc16(list(data)) == self.hdr.secure_area_crc)) + + f.seek(0xC0) + data = f.read(0x9C) + crc_check.append(('Nintendo logo', crc16(list(data)) == self.hdr.logo_crc)) + + f.seek(0) + data = f.read(0x15E) + crc_check.append(('Header', crc16(list(data)) == self.hdr.hdr_crc)) + + hmac_check = [] + if self.hdr.unit_code == 0 and readbe(self.hdr_ext.sig) != 0: + f = open(file, 'rb') + + if (self.hdr_ext.flags >> 5) & 1 and bytes(self.hdr_ext.banner_hmac) != b'\x00' * 20: + f.seek(self.files['banner.bin']['offset']) + hmac_digest = hmac.new(key=TWL.hmac_key_whitelist34, digestmod=hashlib.sha1) + for data in read_chunks(f, self.files['banner.bin']['size']): + hmac_digest.update(data) + hmac_check.append(('Banner', hmac_digest.digest() == bytes(self.hdr_ext.banner_hmac))) + + if (self.hdr_ext.flags >> 6) & 1 and bytes(self.hdr_ext.hdr_arm9_arm7_hmac) != b'\x00' * 20: + hmac_digest = hmac.new(key=TWL.hmac_key_whitelist12, digestmod=hashlib.sha1) + + # Header + f.seek(0) + hmac_digest.update(f.read(0x160)) + + # ARM9 + f.seek(self.files['arm9.bin']['offset']) + for data in read_chunks(f, self.hdr.arm9_size): + hmac_digest.update(data) + + # ARM7 + f.seek(self.files['arm7.bin']['offset']) + for data in read_chunks(f, self.files['arm7.bin']['size']): + hmac_digest.update(data) + + hmac_check.append(('Hdr,ARM9,ARM7', hmac_digest.digest() == bytes(self.hdr_ext.hdr_arm9_arm7_hmac))) + + if (self.hdr_ext.flags >> 6) & 1 and bytes(self.hdr_ext.arm9overlay_fat_hmac) != b'\x00' * 20: + hmac_digest = hmac.new(key=TWL.hmac_key_whitelist12, digestmod=hashlib.sha1) + + # ARM9 overlay + f.seek(self.files['arm9overlay.bin']['offset']) + for data in read_chunks(f, self.files['arm9overlay.bin']['size']): + hmac_digest.update(data) + + # FAT entries for ARM9 overlay + num_overlays = self.files['arm9overlay.bin']['size'] // 0x20 + f.seek(self.hdr.fat_offset) + for data in read_chunks(f, num_overlays * 8): + hmac_digest.update(data) + + # Partial content of overlays + blocks_read = 0 + for i in range(num_overlays): + f.seek(self.hdr.fat_offset + (i * 8)) + overlay_off = readle(f.read(4)) + overlay_size = roundup(readle(f.read(4)) - overlay_off, block_size) + + remaining_overlays = num_overlays - i + max_size = ((1 << 0xA) - blocks_read) // remaining_overlays * block_size + if overlay_size > max_size: + hash_size = max_size + else: + hash_size = overlay_size + + f.seek(overlay_off) + for data in read_chunks(f, hash_size): + hmac_digest.update(data) + blocks_read += hash_size // block_size + + hmac_check.append(('ARM9overlayFAT', hmac_digest.digest() == bytes(self.hdr_ext.arm9overlay_fat_hmac))) + + f.close() + elif self.hdr.unit_code == 2 or self.hdr.unit_code == 3: + hmac_info = [('arm9.bin', bytes(self.hdr_ext.arm9_hmac)), + ('arm7.bin', bytes(self.hdr_ext.arm7_hmac)), + ('banner.bin', bytes(self.hdr_ext.banner_hmac)), + ('arm9i.bin', bytes(self.hdr_ext.arm9i_hmac)), + ('arm7i.bin', bytes(self.hdr_ext.arm7i_hmac))] + f = open(file, 'rb') + for fname, expected_digest in hmac_info: + if fname in self.files.keys() and expected_digest != b'\x00' * 20: + info = self.files[fname] + f.seek(info['offset']) + hmac_digest = hmac.new(key=TWL.hmac_key, digestmod=hashlib.sha1) + for data in read_chunks(f, info['size']): + hmac_digest.update(data) + hmac_check.append((info['name'], hmac_digest.digest() == expected_digest)) + + expected_digest = bytes(self.hdr_ext.arm9_no_secure_area_hmac) + if expected_digest != b'\x00' * 20: + f.seek(self.files['arm9.bin']['offset'] + 0x4000) + hmac_digest = hmac.new(key=TWL.hmac_key, digestmod=hashlib.sha1) + for data in read_chunks(f, self.files['arm9.bin']['size'] - 0x4000): + hmac_digest.update(data) + hmac_check.append(('ARM9 wosecarea', hmac_digest.digest() == expected_digest)) + f.close() + + # Digests + f = open(file, 'rb') + data = [] + info = [(self.hdr_ext.ntr_digest_region_offset, self.hdr_ext.ntr_digest_region_size), + (self.hdr_ext.twl_digest_region_offset, self.hdr_ext.twl_digest_region_size)] + for off, size in info: + f.seek(off) + data += [f.read(self.hdr_ext.digest1_block_size) for _ in range(size // self.hdr_ext.digest1_block_size)] + digest1 = b''.join([hmac.new(key=TWL.hmac_key, msg=i, digestmod=hashlib.sha1).digest() for i in data]) + f.seek(self.hdr_ext.digest1_table_offset) + expected_digest = f.read(self.hdr_ext.digest1_table_size) + hmac_check.append(('Digest1 table', expected_digest[:len(digest1)] == digest1)) + + block_len = self.hdr_ext.digest2_digest1_count * 20 + data = [digest1[i * block_len:(i + 1) * block_len].ljust(block_len, b'\x00') for i in range(self.hdr_ext.digest2_table_size // 20)] + digest2 = b''.join([hmac.new(key=TWL.hmac_key, msg=i, digestmod=hashlib.sha1).digest() for i in data]) + f.seek(self.hdr_ext.digest2_table_offset) + expected_digest = f.read(self.hdr_ext.digest2_table_size) + hmac_check.append(('Digest2 table', expected_digest == digest2)) + f.close() + + hmac_digest = hmac.new(key=TWL.hmac_key, msg=digest2, digestmod=hashlib.sha1) + hmac_check.append(('Digest2', bytes(self.hdr_ext.digest2_hmac) == hmac_digest.digest())) + + sig_check = [] + # Header signature is the raw SHA1 hash (with padding); easier to manually decrypt and remove the padding + if not (self.hdr.unit_code == 0 and readbe(self.hdr_ext.sig) == 0): # Signature exists + idx = get_rsa_key_idx(self.hdr, self.hdr_ext) + n = readbe(TWL.rsa_key_mod[idx][self.dev]) + e = 0x10001 + dec = pow(readbe(bytes(self.hdr_ext.sig)), e, n).to_bytes(0x80, 'big') + + f = open(self.file, 'rb') + sha1_calculated = Crypto.sha1(f, 0xE00) + f.close() + sig_check.append(('Header', dec[-20:] == sha1_calculated)) + + print("CRCs:") + for i in crc_check: + print(' > {0:15} {1:4}'.format(i[0] + ':', 'GOOD' if i[1] else 'FAIL')) + if hmac_check != []: + print("HMACs:") + for i in hmac_check: + print(' > {0:15} {1:4}'.format(i[0] + ':', 'GOOD' if i[1] else 'FAIL')) + if sig_check != []: + print("Signatures:") + for i in sig_check: + print(' > {0:15} {1:4}'.format(i[0] + ':', 'GOOD' if i[1] else 'FAIL')) + + if os.path.isfile('decrypted.nds'): + os.remove('decrypted.nds') + + def __str__(self): + unit_code = { + 0: 'DS', + 2: 'DSi Enhanced', + 3: 'DSi Exclusive', + } + + ntr = ( + f'Game title: {self.hdr.game_title.decode("ascii")}\n' + f'Game code: {self.hdr.game_code.decode("ascii")}\n' + f'Maker code: {self.hdr.maker_code.decode("ascii")}\n' + f'Unit code: {unit_code[self.hdr.unit_code]}\n' + f'Encryption seed: {hex(self.hdr.encryption_seed_select)[2:].zfill(2)}\n' + f'Chip size (KB): {128 << self.hdr.device_capacity}\n' + f'ROM version: {self.hdr.rom_ver}\n' + f'Autostart: {"Yes" if (self.hdr.autostart >> 2) & 1 else "No"}' + ) + + if self.hdr.unit_code == 2 or self.hdr.unit_code == 3: + reg = '' + if self.hdr_ext.region == 0xFFFFFFFF: + reg = 'Region free' + else: + if self.hdr_ext.region & 1: reg += 'Japan, ' + if (self.hdr_ext.region >> 1) & 1: reg += 'USA, ' + if (self.hdr_ext.region >> 2) & 1: reg += 'Europe, ' + if (self.hdr_ext.region >> 3) & 1: reg += 'Australia, ' + if (self.hdr_ext.region >> 4) & 1: reg += 'China, ' + if (self.hdr_ext.region >> 5) & 1: reg += 'Korea, ' + reg = reg[:-2] + + def split_parental_control(b): + parental_ctrl = str(b & 0b00001111) # age + if (b >> 6) & 1: + parental_ctrl += ', prohibited in country' + elif (b >> 7) & 1: + parental_ctrl += ', rating valid' + return parental_ctrl + + twl = ( + f'\nCrypto flags:\n' + f' > Has DSi excl region:{"Yes" if self.hdr.data1 & 1 else "No"}\n' + f' > Modcrypted: {"Yes" if (self.hdr.data1 >> 1) & 1 else "No"}\n' + f' > Modcrypt key: {"Debug" if (self.hdr.data1 >> 2) & 1 else "Retail"}\n' + f' > Disable debug: {"Yes" if (self.hdr.data1 >> 3) & 1 else "No"}\n' + f'Permit jump:\n' + f' Normal jump: {self.hdr.data2 & 1}\n' + f' Temporary jump: {(self.hdr.data2 >> 1) & 1}\n' + f'Region: {reg}\n' + f'Access control:\n' + f' Common client key: {self.hdr_ext.access_control & 1}\n' + f' AES slot B: {(self.hdr_ext.access_control >> 1) & 1}\n' + f' AES slot C: {(self.hdr_ext.access_control >> 2) & 1}\n' + f' SD card: {(self.hdr_ext.access_control >> 3) & 1}\n' + f' NAND access: {(self.hdr_ext.access_control >> 4) & 1}\n' + f' Game card power on: {(self.hdr_ext.access_control >> 5) & 1}\n' + f' Shared2 file: {(self.hdr_ext.access_control >> 6) & 1}\n' + f' SignJPEGforlauncher: {(self.hdr_ext.access_control >> 7) & 1}\n' + f' Game card NTR mode: {(self.hdr_ext.access_control >> 8) & 1}\n' + f' SSL client cert: {(self.hdr_ext.access_control >> 9) & 1}\n' + f'Flags:\n' + f' > TSC mode: {"DSi" if self.hdr_ext.flags & 1 else "DS"}\n' + f' > EULA required: {"Yes" if (self.hdr_ext.flags >> 1) & 1 else "No"}\n' + f' > Banner: {"Use banner.sav" if (self.hdr_ext.flags >> 2) & 1 else "From ROM"}\n' + f' > ShowWiFiconnicon: {"Yes" if (self.hdr_ext.flags >> 3) & 1 else "No"}\n' + f' > ShowDSwirelessicon: {"Yes" if (self.hdr_ext.flags >> 4) & 1 else "No"}\n' + f' > DScartwithiconSHA1: {"Yes" if (self.hdr_ext.flags >> 5) & 1 else "No"}\n' + f' > DScartwithheaderRSA:{"Yes" if (self.hdr_ext.flags >> 6) & 1 else "No"}\n' + f' > Developer app: {"Yes" if (self.hdr_ext.flags >> 7) & 1 else "No"}\n' + f'EULA ver: {self.hdr_ext.eula_ver}\n' + f'TitleID: {hex(self.hdr_ext.titleID_hi)[2:].zfill(8) + hex(self.hdr_ext.titleID_lo)[2:].zfill(8)}\n' + f' Media: {"NAND" if (self.hdr_ext.titleID_hi >> 2) & 1 else "Game card"}\n' + f'Parental control:\n' + f' CERO (Japan): {split_parental_control(self.hdr_ext.parental_control[0])}\n' + f' ESRB (USA/Canada): {split_parental_control(self.hdr_ext.parental_control[1])}\n' + f' USK (Germany): {split_parental_control(self.hdr_ext.parental_control[3])}\n' + f' PEGI (Pan-Europe): {split_parental_control(self.hdr_ext.parental_control[4])}\n' + f' PEGI (Portugal): {split_parental_control(self.hdr_ext.parental_control[6])}\n' + f' PEGI and BBFC (UK): {split_parental_control(self.hdr_ext.parental_control[7])}\n' + f' AGCB (Australia): {split_parental_control(self.hdr_ext.parental_control[8])}\n' + f' GRB (South Korea): {split_parental_control(self.hdr_ext.parental_control[9])}' + ) + + return ntr + twl + else: + if self.hdr.data2 == 0: + reg = 'Normal' + elif self.hdr.data2 == 0x40: + reg = 'Korea' + elif self.hdr.data2 == 0x80: + reg = 'China' + ntr += f'\nRegion: {reg}' + + if self.hdr_ext.flags != 0: + ntr += ( + f'\nFlags:\n' + f' > TSC mode: {"DSi" if self.hdr_ext.flags & 1 else "DS"}\n' + f' > EULA required: {"Yes" if (self.hdr_ext.flags >> 1) & 1 else "No"}\n' + f' > Banner: {"Use banner.sav" if (self.hdr_ext.flags >> 2) & 1 else "From cartridge"}\n' + f' > ShowWiFiconnicon: {"Yes" if (self.hdr_ext.flags >> 3) & 1 else "No"}\n' + f' > ShowDSwirelessicon: {"Yes" if (self.hdr_ext.flags >> 4) & 1 else "No"}\n' + f' > DScartwithiconSHA1: {"Yes" if (self.hdr_ext.flags >> 5) & 1 else "No"}\n' + f' > DScartwithheaderRSA:{"Yes" if (self.hdr_ext.flags >> 6) & 1 else "No"}\n' + f' > Developer app: {"Yes" if (self.hdr_ext.flags >> 7) & 1 else "No"}' + ) + + return ntr \ No newline at end of file diff --git a/lib/resources/keytable2_dev.bin b/lib/resources/keytable2_dev.bin new file mode 100644 index 0000000..65389cc Binary files /dev/null and b/lib/resources/keytable2_dev.bin differ diff --git a/ntool.py b/ntool.py index d337b27..ae8e5dc 100755 --- a/ntool.py +++ b/ntool.py @@ -2,7 +2,7 @@ import sys from utils import * -if sys.argv[1] in ['cia_dev2retail', 'cia_retail2dev', 'cci_dev2retail', 'cci_retail2dev', 'csu2retailcias']: +if sys.argv[1] in ['srl_retail2dev', 'cia_dev2retail', 'cia_retail2dev', 'cci_dev2retail', 'cci_retail2dev', 'csu2retailcias']: path = sys.argv[2] out = '' for i in range(2, len(sys.argv)): diff --git a/utils.py b/utils.py index 653070c..74aa783 100644 --- a/utils.py +++ b/utils.py @@ -1,4 +1,5 @@ from lib.common import * +from lib.keys import NTR, TWL from lib.ctr_cia import CIAReader, CIABuilder from lib.ctr_cci import CCIReader, CCIBuilder from lib.ctr_ncch import NCCHReader, NCCHBuilder @@ -9,6 +10,70 @@ from lib.ctr_tmd import TMDReader, TMDBuilder from lib.ctr_tik import tikReader, tikBuilder from lib.ctr_cdn import CDNReader, CDNBuilder from lib.ctr_cnt import cntReader +from lib.ntr_twl_srl import SRLReader, get_rsa_key_idx + +def srl_retail2dev(path, out=''): + name = os.path.splitext(os.path.basename(path))[0] + if out == '': + out = f'{name}_dev.srl' + + srl = SRLReader(path, dev=0) + shutil.copyfile(path, 'tmp.nds') + + if srl.media == 'Game card' and srl.secure_area_status == 'decrypted': # Encrypt NTR secure area for decrypted game card SRLs + with open(path, 'rb') as f: + f.seek(0x4000) + secure_area = f.read(2048) + secure_area_enc = srl.encrypt_secure_area(secure_area, NTR.blowfish_key) + with open('tmp.nds', 'r+b') as f: + f.seek(0x4000) + f.write(secure_area_enc) + + if srl.modcrypted: # Decrypt modcrypt regions and re-encrypt with dev key + srl.decrypt_modcrypt() + f = open('decrypted.nds', 'rb') + key = bytes(srl.hdr)[:16][::-1] + for i in srl.modcrypt: + g = open('tmp.nds', 'r+b') + f.seek(i['offset']) + g.seek(i['offset']) + + counter = bytearray(i['counter']) + for data in read_chunks(f, i['size']): + for j in range(len(data) // 16): + output, counter = TWL.aes_ctr_block(key, counter, data[j * 16:(j + 1) * 16]) + g.write(output) + g.close() + f.close() + os.remove('decrypted.nds') + + if srl.hdr.unit_code == 2 or srl.hdr.unit_code == 3 or (srl.hdr.unit_code == 0 and srl.hdr_ext.flags != 0): # Set DeveloperApp flag + srl.hdr_ext.flags |= (1 << 7) + with open('tmp.nds', 'r+b') as f: + f.seek(0x1BF) + f.write(int8tobytes(srl.hdr_ext.flags)) + + if not (srl.hdr.unit_code == 0 and readbe(srl.hdr_ext.sig) == 0): # Re-generate header signature + idx = get_rsa_key_idx(srl.hdr, srl.hdr_ext) + n = TWL.rsa_key_mod[idx] + d = TWL.rsa_key_priv[idx] + + f = open('tmp.nds', 'rb') + sha1_calculated = Crypto.sha1(f, 0xE00) + f.close() + sha1_padded = b'\x00\x01' + b'\xff' * 105 + b'\x00' + sha1_calculated + enc = pow(readbe(sha1_padded), readbe(d[1]), readbe(n[1])).to_bytes(0x80, 'big') + with open('tmp.nds', 'r+b') as f: + f.seek(0xF80) + f.write(enc) + + if srl.media == 'Game card': # Re-generate undumpable area i.e. KeyTables for game card SRLs + srl = SRLReader('tmp.nds', dev=1) + srl.regen_undumpable() + os.remove('tmp.nds') + shutil.move('new.nds', out) + else: + shutil.move('tmp.nds', out) def cia_dev2retail(path, out=''): name = os.path.splitext(os.path.basename(path))[0]