So, after finishing reverse engineering the PRX, SAVEDATA and PGD encryption/decryption processes, it's time to attempt something even harder, DLC content...
Many users have been asking if this will be supported in JPCSP, so I've finally started working hard on this and already found some interesting information concerning the formats used by the PSP.
Hopefully, soon JPCSP will have the ability of decrypting and loading DLC content through the CryptoEngine and also applying custom signing methods to expired DLC (with the proper rights, of course).
I'm starting this thread to document and share my findings so far on this subject, so, enjoy!
---------------------------------------------------------------------------------------
DLC formats:
npdrm.prx:
sceIoIoctl commands:
Keys:
Signatures:
---------------------------------------------------------------------------------------
As you can see there's still plenty of work to do, but I think I'm on the right the track, hopefully...
Of course, all help is welcome.
Many users have been asking if this will be supported in JPCSP, so I've finally started working hard on this and already found some interesting information concerning the formats used by the PSP.
Hopefully, soon JPCSP will have the ability of decrypting and loading DLC content through the CryptoEngine and also applying custom signing methods to expired DLC (with the proper rights, of course).
I'm starting this thread to document and share my findings so far on this subject, so, enjoy!
---------------------------------------------------------------------------------------
DLC formats:
Code:
EDAT/SPRX format:
[HEADER]
0x00: 00 50 53 50 -> .PSP
0x04: 45 44 41 54 -> EDAT
0x08: 02 00 00 00 -> License type (0x2000000 paid content -> uses rif key / 0x0000100 free content -> uses fixed key)
0x0C: 90 00 -> Data offset
0x0E: 01 -> Data type (0 - EDAT / 1 - SPRX)
0x0F: 01 -> Version
0x10 - 0x40 -> Content ID (e.g.: JP0700-ULJS00237_00-EXTRAMISSION0001)
0x40 -> Name hash
0x50 - 0x58 -> NULL
0x58 - 0x80 -> Signature
0x80 -> Header hash
[/HEADER]
0x90: [Encrypted PRX (SPRX)] / [PGD (EDATA)]
Code:
RIF format:
0x00: 00 00 00 01 -> Version
0x04: 00 00 00 02 -> License type
0x08 - 0x10 -> Account ID (e.g.: B4 1F 2C 0B DC 1B 43 31)
0x10 - 0x40 -> Content ID (e.g.: UP900-UCUS98721_00-PATAPONPSNDEMO08)
0x40 -> Index hash
0x50 -> Header hash
0x60: 00 00 01 1F -> License start time
0x64: C5 16 7B D8 -> License expiration time
0x68 - 0x70 -> NULL
0x70 - 0x90 -> Signature
Code:
ACT.DAT format:
0x00: 00 00 00 01 -> Version
0x04: 00 00 00 01 -> Unknown
0x08 - 0x10 -> Account ID (e.g.: B4 1F 2C 0B DC 1B 43 31)
0x10 - 0x1010 -> Data hashes' table
0x1010 - 0x1030 -> Signature
npdrm.prx:
Code:
sceNpDrmVerifyAct(int actdat_addr) - Calls sceDdrdbHash and sceDdrdbSigvry to verify the ACT.DAT file signature.
sceNpDrmVerifyRif(int rif_addr) - Verifies the specified .RIF license file signature with extra checks by using sceOpenPSIDGetPSID and the Unique ID (hardware addresses 0xBC100090 and 0xBC100094).
sceNpDrmGetModuleKey(int sprx_addr, int unk) - Decrypts and generates a SPRX module key.
-> Algorithm:
- First, the SPRX file signature is verified in a similar way as it is done with the licensing files.
- A new header hash is generated using the first 0x80 bytes of data and checked against the Header Hash in the file by using sceDrmBBMacFinal2.
- sceNpDrmGetFixedKey is then called to build a decrypting key for this module.
scePspNpDrm_driver_04618D16(int out_addr) - Calls sceOpenPSIDGetOpenPSID(out_addr, 0x1).
sceNpDrmGetContentKey(int file_addr, int actdat_addr, int rif_addr) - Calls sceNpDrmGetVersionKey(int file_addr, int actdat_addr, int rif_addr, 0).
sceNpDrmGetVersionKey(int file_addr, int actdat_addr, int rif_addr, int unk) - Decrypts and generates an EDATA key. Verifies the .RIF file, makes a call to sceMgMemoryStick_driver_735526D6() (unknown), and uses custom algorithms and private keys to process the key generation.
sceNpDrmGetFixedKey(int file_addr, int data_addr, int type_addr) - Generates a fixed key for any kind of data (mostly used by free content).
Checks the Name Hash against a private key and then proceeds to use custom algorithms and private keys to process the key generation.
sceNpDrmEdataGetDataSize(int file_addr) - Gets a file descriptor for file_addr with IoFileMgrForKernel_13A4DEB0(file_addr, 0x4) and calls sceIoIoctl(fd, 0x41000010, 0, 0, 0, 0) to obtain the data size.
sceNpDrmRenameCheck(int file_addr) - Checks if a DRM file has been renamed.
-> Algorithm:
- Open the file with sceIoOpen(file_addr, 0x1, 0).
- Read the EDATA header with sceIoRead(fd, buffer, 0x80).
- Generate a new name hash and compare it with the one in the file:
- sceDrmBBMacInit(ctx_addr, 0x3).
- sceDrmBBMacUpdate(ctx_addr, buffer + 0x10, 0x30).
- strlen(filename).
- sceDrmBBMacUpdate(ctx_addr, filename, filename_size).
- sceDrmBBMacFinal2(ctx_addr, buffer + 0x40, private_hash).
The new hash is generated with the Content ID and the filename and is
checked against a private known hash.
sceNpDrmEdataSetupKey(int edat_addr) - Obtains an .edat key for decryption.
This function gets a file descriptor with IoFileMgrForKernel_13A4DEB0(file_addr, 0x4) and makes several sceIoIoctl calls.
It also verifies the edat file in a similar way as it is done with sprx. The main key decryption is processed with a call to sceNpDrmGetVersionKey. This key is later sent with another sceIoIoctl command.
sceIoIoctl commands:
Code:
// These commands are sent by some of the sceNpDrmXXX functions.
sceIoIoctl(fd, 0x41000010, 0, 0, 0, 0); // Get DRM file size.
sceIoIoctl(fd, 0x41000006, 0, 0, 0, 0) ; // Init DRM mode.
sceIoIoctl(fd, 0x41000005, in_addr, 0x8, out_addr, 0x90); // Unknown.
sceIoIoctl(fd, 0x41000002, in_addr, 0x4, 0, 0); // Unknown.
sceIoIoctl(fd, 0x41000001, in_addr, 0x10, 0, 0); // Send DRM AES-128bit key.
Keys:
Code:
RIF/ACT keys (sceNpDrmGetVersionKey):
byte[] rif_key = {0xDA, 0x7D, 0x4B, 0x5E, 0x49, 0x9A, 0x4F, 0x53, 0xB1, 0xC1, 0xA1, 0x4A, 0x74, 0x84, 0x44, 0x3B};
byte[] actdat_key = {0x5E, 0x06, 0xE0, 0x4F, 0xD9, 0x4A, 0x71, 0xBF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01};
byte[] unk_key1 = {0x69, 0xB4, 0x53, 0xF2, 0xE4, 0x21, 0x89, 0x8E, 0x53, 0xE4, 0xA3, 0x5A, 0x5B, 0x91, 0x79, 0x51};
byte[] unk_key2 = {0xF0, 0x79, 0xD5, 0x19, 0x2D, 0x5D, 0xD3, 0x8C, 0xB5, 0x4B, 0x9E, 0xCD, 0xCD, 0xFD, 0xD3, 0xD7};
Free license key (sceNpDrmGetFixedKey):
byte[] fixed_key = {0x38, 0x20, 0xD0, 0x11, 0x07, 0xA3, 0xFF, 0x3E, 0x0A, 0x4C, 0x20, 0x85, 0x39, 0x10, 0xB5, 0x54};
EDAT/SPRX internal key (sceNpDrmGetModuleKey/sceNpDrmEdataSetupKey):
byte[] module_key = {0xBA, 0x87, 0xE4, 0xAB, 0x2C, 0x60, 0x5F, 0x59, 0xB8, 0x3B, 0xDB, 0xA6, 0x82, 0xFD, 0xAE, 0x14};
Rename hash (sceNpDrmRenameCheck):
byte[] rename_key = {0xEB, 0x71, 0x5D, 0xB8, 0xD3, 0x73, 0xCE, 0xA4, 0x6F, 0xE7, 0x1D, 0xCF, 0xFF, 0x63, 0xFA, 0xEA};
Signatures:
Code:
ACT.DAT/.RIF Signature:
byte[] act_rif_sig = {0x62, 0x27, 0xB0, 0x0A, 0x02, 0x85, 0x6F, 0xB0, 0x41, 0x08, 0x87, 0x67, 0x19, 0xE0, 0xA0, 0x18, 0x32, 0x91, 0xEE, 0xB9, 0x6E, 0x73, 0x6A, 0xBF, 0x81, 0xF7, 0x0E, 0xE9, 0x16, 0x1B, 0x0D, 0xDE, 0xB0, 0x26, 0x76, 0x1A, 0xFF, 0x7B, 0xC8, 0x5B};
EDAT/SPRX Signature:
byte[] edat_sprx_sig = {0x1F, 0x07, 0x2B, 0xCC, 0xC1, 0x62, 0xF2, 0xCF, 0xAE, 0xA0, 0xE7, 0xF4, 0xCD, 0xFD, 0x9C, 0xAE, 0xC6, 0xC4, 0x55, 0x21, 0x53, 0x01, 0xF4, 0xE3, 0x70, 0xC3, 0xED, 0xE2, 0xD4, 0xF5, 0xDB, 0xC3, 0xA7, 0xDE, 0x8C, 0xAA, 0xE8, 0xAD, 0x5B, 0x7D};
---------------------------------------------------------------------------------------
As you can see there's still plenty of work to do, but I think I'm on the right the track, hopefully...
Of course, all help is welcome.