Files
ppsspp/Core/ELF/PrxDecrypter.cpp
T
Henrik RydgårdandClaude Opus 5 322b5122d8 Unpack the firmware out of an official updater EBOOT.PBP
An updater's DATA.PSAR is a flat sequence of records - each is 0x150 bytes of
PRX-style encryption header, a 0x110 byte entry describing one file, and then
its compressed contents. So to get the files, you don't actually have to run it
and let it self-unpack - we can just do it.

Two steps per record. First "demangle": the 0x130 bytes at +0x20 are AES-CBC
encrypted on top of everything else and hide the PRX tag at +0xD0, so a KIRK
CMD7 pass with keyseed 0x55 comes first. Then the record is an ordinary PRX blob
for the decrypter we already have, once it knows the tag - 0x0E000000, which is
new here. Its key needs the kirk7 scramble applied, unlike every other key in
that table, which are stored already scrambled; hence the flag on TAG_INFO.

UnpackPSAR() takes a prefix filter, since the planned main use for this is pulling
flash0:/font out of an updater the user supplies (or from an ISO) rather than
extracting whole firmwares, although that can also be interesting for running
the VSH.

Tested on a 6.61 updater: 436 entries, all 418 files decrypt and decompress,
nothing fails. The contents are what they should be - 295 ~PSP modules, 61 PRF
files, 18 PGF fonts, and the encrypted XMB indices.

Two things it doesn't do yet. Every entry in that archive is named with a
five-digit token rather than a path; the real names live in tables 00001-00012
inside the archive itself, under their own separate encryption, so files come
out under the short name for now and the prefix filter can't match them.
And only the zlib compression format is currently supported.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GZq8ZtJmFY7bkX5FVkr3P9
2026-08-20 22:41:58 +02:00

1076 lines
50 KiB
C++

#include <algorithm>
#include <array>
#include <string.h>
extern "C"
{
#include "ext/libkirk/kirk_engine.h"
#include "ext/libkirk/SHA1.h"
}
#include "Common/Common.h"
#include "Common/Log.h"
#include "Common/Swap.h"
#include "Core/ELF/PrxDecrypter.h"
#define ROUNDUP16(x) (((x)+15)&~15)
// Thank you PSARDUMPER & JPCSP keys
// PRXDecrypter 16-byte tag keys.
static const u8 keys260_0[] = {0xC3, 0x24, 0x89, 0xD3, 0x80, 0x87, 0xB2, 0x4E, 0x4C, 0xD7, 0x49, 0xE4, 0x9D, 0x1D, 0x34, 0xD1};
static const u8 keys260_1[] = {0xF3, 0xAC, 0x6E, 0x7C, 0x04, 0x0A, 0x23, 0xE7, 0x0D, 0x33, 0xD8, 0x24, 0x73, 0x39, 0x2B, 0x4A};
static const u8 keys260_2[] = {0x72, 0xB4, 0x39, 0xFF, 0x34, 0x9B, 0xAE, 0x82, 0x30, 0x34, 0x4A, 0x1D, 0xA2, 0xD8, 0xB4, 0x3C};
static const u8 keys280_0[] = {0xCA, 0xFB, 0xBF, 0xC7, 0x50, 0xEA, 0xB4, 0x40, 0x8E, 0x44, 0x5C, 0x63, 0x53, 0xCE, 0x80, 0xB1};
static const u8 keys280_1[] = {0x40, 0x9B, 0xC6, 0x9B, 0xA9, 0xFB, 0x84, 0x7F, 0x72, 0x21, 0xD2, 0x36, 0x96, 0x55, 0x09, 0x74};
static const u8 keys280_2[] = {0x03, 0xA7, 0xCC, 0x4A, 0x5B, 0x91, 0xC2, 0x07, 0xFF, 0xFC, 0x26, 0x25, 0x1E, 0x42, 0x4B, 0xB5};
static const u8 keys300_0[] = {0x9F, 0x67, 0x1A, 0x7A, 0x22, 0xF3, 0x59, 0x0B, 0xAA, 0x6D, 0xA4, 0xC6, 0x8B, 0xD0, 0x03, 0x77};
static const u8 keys300_1[] = {0x15, 0x07, 0x63, 0x26, 0xDB, 0xE2, 0x69, 0x34, 0x56, 0x08, 0x2A, 0x93, 0x4E, 0x4B, 0x8A, 0xB2};
static const u8 keys300_2[] = {0x56, 0x3B, 0x69, 0xF7, 0x29, 0x88, 0x2F, 0x4C, 0xDB, 0xD5, 0xDE, 0x80, 0xC6, 0x5C, 0xC8, 0x73};
static const u8 keys303_0[] = {0x7b, 0xa1, 0xe2, 0x5a, 0x91, 0xb9, 0xd3, 0x13, 0x77, 0x65, 0x4a, 0xb7, 0xc2, 0x8a, 0x10, 0xaf};
static const u8 keys310_0[] = {0xa2, 0x41, 0xe8, 0x39, 0x66, 0x5b, 0xfa, 0xbb, 0x1b, 0x2d, 0x6e, 0x0e, 0x33, 0xe5, 0xd7, 0x3f};
static const u8 keys310_1[] = {0xA4, 0x60, 0x8F, 0xAB, 0xAB, 0xDE, 0xA5, 0x65, 0x5D, 0x43, 0x3A, 0xD1, 0x5E, 0xC3, 0xFF, 0xEA};
static const u8 keys310_2[] = {0xE7, 0x5C, 0x85, 0x7A, 0x59, 0xB4, 0xE3, 0x1D, 0xD0, 0x9E, 0xCE, 0xC2, 0xD6, 0xD4, 0xBD, 0x2B};
static const u8 keys310_3[] = {0x2E, 0x00, 0xF6, 0xF7, 0x52, 0xCF, 0x95, 0x5A, 0xA1, 0x26, 0xB4, 0x84, 0x9B, 0x58, 0x76, 0x2F};
static const u8 keys330_0[] = {0x3B, 0x9B, 0x1A, 0x56, 0x21, 0x80, 0x14, 0xED, 0x8E, 0x8B, 0x08, 0x42, 0xFA, 0x2C, 0xDC, 0x3A};
static const u8 keys330_1[] = {0xE8, 0xBE, 0x2F, 0x06, 0xB1, 0x05, 0x2A, 0xB9, 0x18, 0x18, 0x03, 0xE3, 0xEB, 0x64, 0x7D, 0x26};
static const u8 keys330_2[] = {0xAB, 0x82, 0x25, 0xD7, 0x43, 0x6F, 0x6C, 0xC1, 0x95, 0xC5, 0xF7, 0xF0, 0x63, 0x73, 0x3F, 0xE7};
static const u8 keys330_3[] = {0xA8, 0xB1, 0x47, 0x77, 0xDC, 0x49, 0x6A, 0x6F, 0x38, 0x4C, 0x4D, 0x96, 0xBD, 0x49, 0xEC, 0x9B};
static const u8 keys330_4[] = {0xEC, 0x3B, 0xD2, 0xC0, 0xFA, 0xC1, 0xEE, 0xB9, 0x9A, 0xBC, 0xFF, 0xA3, 0x89, 0xF2, 0x60, 0x1F};
static const u8 keys360_0[] = {0x3C, 0x2B, 0x51, 0xD4, 0x2D, 0x85, 0x47, 0xDA, 0x2D, 0xCA, 0x18, 0xDF, 0xFE, 0x54, 0x09, 0xED};
static const u8 keys360_1[] = {0x31, 0x1F, 0x98, 0xD5, 0x7B, 0x58, 0x95, 0x45, 0x32, 0xAB, 0x3A, 0xE3, 0x89, 0x32, 0x4B, 0x34};
static const u8 keys370_0[] = {0x26, 0x38, 0x0A, 0xAC, 0xA5, 0xD8, 0x74, 0xD1, 0x32, 0xB7, 0x2A, 0xBF, 0x79, 0x9E, 0x6D, 0xDB};
static const u8 keys370_1[] = {0x53, 0xE7, 0xAB, 0xB9, 0xC6, 0x4A, 0x4B, 0x77, 0x92, 0x17, 0xB5, 0x74, 0x0A, 0xDA, 0xA9, 0xEA};
static const u8 keys370_2[] = {0x71, 0x10, 0xF0, 0xA4, 0x16, 0x14, 0xD5, 0x93, 0x12, 0xFF, 0x74, 0x96, 0xDF, 0x1F, 0xDA, 0x89};
static const u8 keys390_0[] = {0x45, 0xEF, 0x5C, 0x5D, 0xED, 0x81, 0x99, 0x84, 0x12, 0x94, 0x8F, 0xAB, 0xE8, 0x05, 0x6D, 0x7D};
static const u8 keys390_1[] = {0x70, 0x1B, 0x08, 0x25, 0x22, 0xA1, 0x4D, 0x3B, 0x69, 0x21, 0xF9, 0x71, 0x0A, 0xA8, 0x41, 0xA9};
static const u8 keys500_0[] = {0xEB, 0x1B, 0x53, 0x0B, 0x62, 0x49, 0x32, 0x58, 0x1F, 0x83, 0x0A, 0xF4, 0x99, 0x3D, 0x75, 0xD0};
static const u8 keys500_1[] = {0xBA, 0xE2, 0xA3, 0x12, 0x07, 0xFF, 0x04, 0x1B, 0x64, 0xA5, 0x11, 0x85, 0xF7, 0x2F, 0x99, 0x5B};
static const u8 keys500_2[] = {0x2C, 0x8E, 0xAF, 0x1D, 0xFF, 0x79, 0x73, 0x1A, 0xAD, 0x96, 0xAB, 0x09, 0xEA, 0x35, 0x59, 0x8B};
static const u8 keys500_c[] = {0xA3, 0x5D, 0x51, 0xE6, 0x56, 0xC8, 0x01, 0xCA, 0xE3, 0x77, 0xBF, 0xCD, 0xFF, 0x24, 0xDA, 0x4D};
static const u8 keys505_a[] = {0x7B, 0x94, 0x72, 0x27, 0x4C, 0xCC, 0x54, 0x3B, 0xAE, 0xDF, 0x46, 0x37, 0xAC, 0x01, 0x4D, 0x87};
static const u8 keys505_0[] = {0x2E, 0x8E, 0x97, 0xA2, 0x85, 0x42, 0x70, 0x73, 0x18, 0xDA, 0xA0, 0x8A, 0xF8, 0x62, 0xA2, 0xB0};
static const u8 keys505_1[] = {0x58, 0x2A, 0x4C, 0x69, 0x19, 0x7B, 0x83, 0x3D, 0xD2, 0x61, 0x61, 0xFE, 0x14, 0xEE, 0xAA, 0x11};
static const u8 keys570_5k[] = {0x6D, 0x72, 0xA4, 0xBA, 0x7F, 0xBF, 0xD1, 0xF1, 0xA9, 0xF3, 0xBB, 0x07, 0x1B, 0xC0, 0xB3, 0x66};
static const u8 keys600_1[] = {0xE3, 0x52, 0x39, 0x97, 0x3B, 0x84, 0x41, 0x1C, 0xC3, 0x23, 0xF1, 0xB8, 0xA9, 0x09, 0x4B, 0xF0};
static const u8 keys600_2[] = {0xE1, 0x45, 0x93, 0x2C, 0x53, 0xE2, 0xAB, 0x06, 0x6F, 0xB6, 0x8F, 0x0B, 0x66, 0x91, 0xE7, 0x1E};
static const u8 keys620_0[] = {0xD6, 0xBD, 0xCE, 0x1E, 0x12, 0xAF, 0x9A, 0xE6, 0x69, 0x30, 0xDE, 0xDA, 0x88, 0xB8, 0xFF, 0xFB};
static const u8 keys620_1[] = {0x1D, 0x13, 0xE9, 0x50, 0x04, 0x73, 0x3D, 0xD2, 0xE1, 0xDA, 0xB9, 0xC1, 0xE6, 0x7B, 0x25, 0xA7};
static const u8 keys620_a[] = {0xAC, 0x34, 0xBA, 0xB1, 0x97, 0x8D, 0xAE, 0x6F, 0xBA, 0xE8, 0xB1, 0xD6, 0xDF, 0xDF, 0xF1, 0xA2};
static const u8 keys620_e[] = {0xB1, 0xB3, 0x7F, 0x76, 0xC3, 0xFB, 0x88, 0xE6, 0xF8, 0x60, 0xD3, 0x35, 0x3C, 0xA3, 0x4E, 0xF3};
static const u8 keys620_5[] = {0xF1, 0xBC, 0x17, 0x07, 0xAE, 0xB7, 0xC8, 0x30, 0xD8, 0x34, 0x9D, 0x40, 0x6A, 0x8E, 0xDF, 0x4E};
static const u8 keys620_5k[] = {0x41, 0x8A, 0x35, 0x4F, 0x69, 0x3A, 0xDF, 0x04, 0xFD, 0x39, 0x46, 0xA2, 0x5C, 0x2D, 0xF2, 0x21};
static const u8 keys620_5v[] = {0xF2, 0x8F, 0x75, 0xA7, 0x31, 0x91, 0xCE, 0x9E, 0x75, 0xBD, 0x27, 0x26, 0xB4, 0xB4, 0x0C, 0x32};
static const u8 keys630_k1[] = {0x36, 0xB0, 0xDC, 0xFC, 0x59, 0x2A, 0x95, 0x1D, 0x80, 0x2D, 0x80, 0x3F, 0xCD, 0x30, 0xA0, 0x1B};
static const u8 keys630_k2[] = {0xd4, 0x35, 0x18, 0x02, 0x29, 0x68, 0xfb, 0xa0, 0x6a, 0xa9, 0xa5, 0xed, 0x78, 0xfd, 0x2e, 0x9d};
static const u8 keys630_k3[] = {0x23, 0x8D, 0x3D, 0xAE, 0x41, 0x50, 0xA0, 0xFA, 0xF3, 0x2F, 0x32, 0xCE, 0xC7, 0x27, 0xCD, 0x50};
static const u8 keys630_k4[] = {0xAA, 0xA1, 0xB5, 0x7C, 0x93, 0x5A, 0x95, 0xBD, 0xEF, 0x69, 0x16, 0xFC, 0x2B, 0x92, 0x31, 0xDD};
static const u8 keys630_k5[] = {0x87, 0x37, 0x21, 0xCC, 0x65, 0xAE, 0xAA, 0x5F, 0x40, 0xF6, 0x6F, 0x2A, 0x86, 0xC7, 0xA1, 0xC8};
static const u8 keys630_k6[] = {0x8D, 0xDB, 0xDC, 0x5C, 0xF2, 0x70, 0x2B, 0x40, 0xB2, 0x3D, 0x00, 0x09, 0x61, 0x7C, 0x10, 0x60};
static const u8 keys630_k7[] = {0x77, 0x1C, 0x06, 0x5F, 0x53, 0xEC, 0x3F, 0xFC, 0x22, 0xCE, 0x5A, 0x27, 0xFF, 0x78, 0xA8, 0x48};
static const u8 keys630_k8[] = {0x81, 0xD1, 0x12, 0x89, 0x35, 0xC8, 0xEA, 0x8B, 0xE0, 0x02, 0x2D, 0x2D, 0x6A, 0x18, 0x67, 0xB8};
static const u8 keys636_k1[] = {0x07, 0xE3, 0x08, 0x64, 0x7F, 0x60, 0xA3, 0x36, 0x6A, 0x76, 0x21, 0x44, 0xC9, 0xD7, 0x06, 0x83};
static const u8 keys636_k2[] = {0x91, 0xF2, 0x02, 0x9E, 0x63, 0x32, 0x30, 0xA9, 0x1D, 0xDA, 0x0B, 0xA8, 0xB7, 0x41, 0xA3, 0xCC};
static const u8 keys638_k4[] = {0x98, 0x43, 0xFF, 0x85, 0x68, 0xB2, 0xDB, 0x3B, 0xD4, 0x22, 0xD0, 0x4F, 0xAB, 0x5F, 0x0A, 0x31};
static const u8 keys639_k3[] = {0x01, 0x7B, 0xF0, 0xE9, 0xBE, 0x9A, 0xDD, 0x54, 0x37, 0xEA, 0x0E, 0xC4, 0xD6, 0x4D, 0x8E, 0x9E};
static const u8 keys660_k1[] = {0x76, 0xF2, 0x6C, 0x0A, 0xCA, 0x3A, 0xBA, 0x4E, 0xAC, 0x76, 0xD2, 0x40, 0xF5, 0xC3, 0xBF, 0xF9};
static const u8 keys660_k2[] = {0x7A, 0x3E, 0x55, 0x75, 0xB9, 0x6A, 0xFC, 0x4F, 0x3E, 0xE3, 0xDF, 0xB3, 0x6C, 0xE8, 0x2A, 0x82};
static const u8 keys660_k3[] = {0xFA, 0x79, 0x09, 0x36, 0xE6, 0x19, 0xE8, 0xA4, 0xA9, 0x41, 0x37, 0x18, 0x81, 0x02, 0xE9, 0xB3};
static const u8 keys660_v1[] = {0xBA, 0x76, 0x61, 0x47, 0x8B, 0x55, 0xA8, 0x72, 0x89, 0x15, 0x79, 0x6D, 0xD7, 0x2F, 0x78, 0x0E};
static const u8 keys660_v2[] = {0xF9, 0x4A, 0x6B, 0x96, 0x79, 0x3F, 0xEE, 0x0A, 0x04, 0xC8, 0x8D, 0x7E, 0x5F, 0x38, 0x3A, 0xCF};
static const u8 keys660_v3[] = {0x88, 0xAF, 0x18, 0xE9, 0xC3, 0xAA, 0x6B, 0x56, 0xF7, 0xC5, 0xA8, 0xBF, 0x1A, 0x84, 0xE9, 0xF3};
static const u8 keys660_v4[] = {0xD1, 0xB0, 0xAE, 0xC3, 0x24, 0x36, 0x13, 0x49, 0xD6, 0x49, 0xD7, 0x88, 0xEA, 0xA4, 0x99, 0x86};
static const u8 keys660_v5[] = {0xCB, 0x93, 0x12, 0x38, 0x31, 0xC0, 0x2D, 0x2E, 0x7A, 0x18, 0x5C, 0xAC, 0x92, 0x93, 0xAB, 0x32};
static const u8 keys660_v6[] = {0x92, 0x8C, 0xA4, 0x12, 0xD6, 0x5C, 0x55, 0x31, 0x5B, 0x94, 0x23, 0x9B, 0x62, 0xB3, 0xDB, 0x47};
static const u8 keys660_k4[] = {0xC8, 0xA0, 0x70, 0x98, 0xAE, 0xE6, 0x2B, 0x80, 0xD7, 0x91, 0xE6, 0xCA, 0x4C, 0xA9, 0x78, 0x4E};
static const u8 keys660_k5[] = {0xBF, 0xF8, 0x34, 0x02, 0x84, 0x47, 0xBD, 0x87, 0x1C, 0x52, 0x03, 0x23, 0x79, 0xBB, 0x59, 0x81};
static const u8 keys660_k6[] = {0xD2, 0x83, 0xCC, 0x63, 0xBB, 0x10, 0x15, 0xE7, 0x7B, 0xC0, 0x6D, 0xEE, 0x34, 0x9E, 0x4A, 0xFA};
static const u8 keys660_k7[] = {0xEB, 0xD9, 0x1E, 0x05, 0x3C, 0xAE, 0xAB, 0x62, 0xE3, 0xB7, 0x1F, 0x37, 0xE5, 0xCD, 0x68, 0xC3};
static const u8 keys660_v7[] = {0xC5, 0x9C, 0x77, 0x9C, 0x41, 0x01, 0xE4, 0x85, 0x79, 0xC8, 0x71, 0x63, 0xA5, 0x7D, 0x4F, 0xFB};
static const u8 keys660_v8[] = {0x86, 0xA0, 0x7D, 0x4D, 0xB3, 0x6B, 0xA2, 0xFD, 0xF4, 0x15, 0x85, 0x70, 0x2D, 0x6A, 0x0D, 0x3A};
static const u8 keys660_k8[] = {0x85, 0x93, 0x1F, 0xED, 0x2C, 0x4D, 0xA4, 0x53, 0x59, 0x9C, 0x3F, 0x16, 0xF3, 0x50, 0xDE, 0x46};
static const u8 key_21C0[] = {0x6A, 0x19, 0x71, 0xF3, 0x18, 0xDE, 0xD3, 0xA2, 0x6D, 0x3B, 0xDE, 0xC7, 0xBE, 0x98, 0xE2, 0x4C};
static const u8 key_2250[] = {0x50, 0xCC, 0x03, 0xAC, 0x3F, 0x53, 0x1A, 0xFA, 0x0A, 0xA4, 0x34, 0x23, 0x86, 0x61, 0x7F, 0x97};
static const u8 key_22E0[] = {0x66, 0x0F, 0xCB, 0x3B, 0x30, 0x75, 0xE3, 0x10, 0x0A, 0x95, 0x65, 0xC7, 0x3C, 0x93, 0x87, 0x22};
static const u8 key_2D80[] = {0x40, 0x02, 0xC0, 0xBF, 0x20, 0x02, 0xC0, 0xBF, 0x5C, 0x68, 0x2B, 0x95, 0x5F, 0x40, 0x7B, 0xB8};
static const u8 key_2D90[] = {0x55, 0x19, 0x35, 0x10, 0x48, 0xD8, 0x2E, 0x46, 0xA8, 0xB1, 0x47, 0x77, 0xDC, 0x49, 0x6A, 0x6F};
static const u8 key_2DA8[] = {0x80, 0x02, 0xC0, 0xBF, 0x00, 0x0A, 0xC0, 0xBF, 0x40, 0x03, 0xC0, 0xBF, 0x40, 0x00, 0x00, 0x00};
static const u8 key_2DB8[] = {0x4C, 0x2D, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xB8, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
static const u8 key_D91605F0[] = {0xB8, 0x8C, 0x45, 0x8B, 0xB6, 0xE7, 0x6E, 0xB8, 0x51, 0x59, 0xA6, 0x53, 0x7C, 0x5E, 0x86, 0x31};
static const u8 key_D91606F0[] = {0xED, 0x10, 0xE0, 0x36, 0xC4, 0xFE, 0x83, 0xF3, 0x75, 0x70, 0x5E, 0xF6, 0xA4, 0x40, 0x05, 0xF7};
static const u8 key_D91608F0[] = {0x5C, 0x77, 0x0C, 0xBB, 0xB4, 0xC2, 0x4F, 0xA2, 0x7E, 0x3B, 0x4E, 0xB4, 0xB4, 0xC8, 0x70, 0xAF};
static const u8 key_D91609F0[] = {0xD0, 0x36, 0x12, 0x75, 0x80, 0x56, 0x20, 0x43, 0xC4, 0x30, 0x94, 0x3E, 0x1C, 0x75, 0xD1, 0xBF};
static const u8 key_D9160AF0[] = {0x10, 0xA9, 0xAC, 0x16, 0xAE, 0x19, 0xC0, 0x7E, 0x3B, 0x60, 0x77, 0x86, 0x01, 0x6F, 0xF2, 0x63};
static const u8 key_D9160BF0[] = {0x83, 0x83, 0xF1, 0x37, 0x53, 0xD0, 0xBE, 0xFC, 0x8D, 0xA7, 0x32, 0x52, 0x46, 0x0A, 0xC2, 0xC2};
static const u8 key_D91611F0[] = {0x61, 0xB0, 0xC0, 0x58, 0x71, 0x57, 0xD9, 0xFA, 0x74, 0x67, 0x0E, 0x5C, 0x7E, 0x6E, 0x95, 0xB9};
static const u8 key_D91612F0[] = {0x9E, 0x20, 0xE1, 0xCD, 0xD7, 0x88, 0xDE, 0xC0, 0x31, 0x9B, 0x10, 0xAF, 0xC5, 0xB8, 0x73, 0x23};
static const u8 key_D91613F0[] = {0xEB, 0xFF, 0x40, 0xD8, 0xB4, 0x1A, 0xE1, 0x66, 0x91, 0x3B, 0x8F, 0x64, 0xB6, 0xFC, 0xB7, 0x12};
static const u8 key_D91614F0[] = {0xFD, 0xF7, 0xB7, 0x3C, 0x9F, 0xD1, 0x33, 0x95, 0x11, 0xB8, 0xB5, 0xBB, 0x54, 0x23, 0x73, 0x85};
static const u8 key_D91615F0[] = {0xC8, 0x03, 0xE3, 0x44, 0x50, 0xF1, 0xE7, 0x2A, 0x6A, 0x0D, 0xC3, 0x61, 0xB6, 0x8E, 0x5F, 0x51};
static const u8 key_D91616F0[] = {0x53, 0x03, 0xB8, 0x6A, 0x10, 0x19, 0x98, 0x49, 0x1C, 0xAF, 0x30, 0xE4, 0x25, 0x1B, 0x6B, 0x28};
static const u8 key_D91617F0[] = {0x02, 0xFA, 0x48, 0x73, 0x75, 0xAF, 0xAE, 0x0A, 0x67, 0x89, 0x2B, 0x95, 0x4B, 0x09, 0x87, 0xA3};
static const u8 key_D91618F0[] = {0x96, 0x96, 0x7C, 0xC3, 0xF7, 0x12, 0xDA, 0x62, 0x1B, 0xF6, 0x9A, 0x9A, 0x44, 0x44, 0xBC, 0x48};
static const u8 key_D91619F0[] = {0xE0, 0x32, 0xA7, 0x08, 0x6B, 0x2B, 0x29, 0x2C, 0xD1, 0x4D, 0x5B, 0xEE, 0xA8, 0xC8, 0xB4, 0xE9};
static const u8 key_D9161AF0[] = {0x27, 0xE5, 0xA7, 0x49, 0x52, 0xE1, 0x94, 0x67, 0x35, 0x66, 0x91, 0x0C, 0xE8, 0x9A, 0x25, 0x24};
static const u8 key_D91620F0[] = {0x52, 0x1C, 0xB4, 0x5F, 0x40, 0x3B, 0x9A, 0xDD, 0xAC, 0xFC, 0xEA, 0x92, 0xFD, 0xDD, 0xF5, 0x90};
static const u8 key_D91621F0[] = {0xD1, 0x91, 0x2E, 0xA6, 0x21, 0x14, 0x29, 0x62, 0xF6, 0xED, 0xAE, 0xCB, 0xDD, 0xA3, 0xBA, 0xFE};
static const u8 key_D91622F0[] = {0x59, 0x5D, 0x78, 0x4D, 0x21, 0xB2, 0x01, 0x17, 0x6C, 0x9A, 0xB5, 0x1B, 0xDA, 0xB7, 0xF9, 0xE6};
static const u8 key_D91623F0[] = {0xAA, 0x45, 0xEB, 0x4F, 0x62, 0xFB, 0xD1, 0x0D, 0x71, 0xD5, 0x62, 0xD2, 0xF5, 0xBF, 0xA5, 0x2F};
static const u8 key_D91624F0[] = {0x61, 0xB7, 0x26, 0xAF, 0x8B, 0xF1, 0x41, 0x58, 0x83, 0x6A, 0xC4, 0x92, 0x12, 0xCB, 0xB1, 0xE9};
static const u8 key_D91628F0[] = {0x49, 0xA4, 0xFC, 0x66, 0xDC, 0xE7, 0x62, 0x21, 0xDB, 0x18, 0xA7, 0x50, 0xD6, 0xA8, 0xC1, 0xB6};
static const u8 key_D91680F0[] = {0x2C, 0x22, 0x9B, 0x12, 0x36, 0x74, 0x11, 0x67, 0x49, 0xD1, 0xD1, 0x88, 0x92, 0xF6, 0xA1, 0xD8};
static const u8 key_D91681F0[] = {0x52, 0xB6, 0x36, 0x6C, 0x8C, 0x46, 0x7F, 0x7A, 0xCC, 0x11, 0x62, 0x99, 0xC1, 0x99, 0xBE, 0x98};
static const u8 key_2E5E0AF0[] = {0xAC, 0xD7, 0x6D, 0x90, 0x33, 0x37, 0x38, 0x92, 0x31, 0x41, 0xD6, 0x36, 0xAF, 0x9F, 0x17, 0x15};
static const u8 xor_2E5E0AF0[] = {0x69, 0xBA, 0x55, 0x34, 0xF0, 0xC0, 0xD6, 0x71, 0xE3, 0x1F, 0xDB, 0x97, 0xE0, 0x7C, 0xD2, 0x2A};
static const u8 key_2E5E10F0[] = {0x9D, 0x5C, 0x5B, 0xAF, 0x8C, 0xD8, 0x69, 0x7E, 0x51, 0x9F, 0x70, 0x96, 0xE6, 0xD5, 0xC4, 0xE8};
static const u8 xor_2E5E10F0[] = {0x69, 0xBA, 0x55, 0x34, 0xF0, 0xC0, 0xD6, 0x71, 0xE3, 0x1F, 0xDB, 0x97, 0xE0, 0x7C, 0xD2, 0x2A};
static const u8 key_2E5E11F0[] = {0x75, 0xEB, 0xE8, 0x43, 0xF4, 0x87, 0x8F, 0xD0, 0x14, 0x7F, 0x7E, 0x39, 0xAD, 0xAF, 0x04, 0x9D};
static const u8 xor_2E5E11F0[] = {0x69, 0xBA, 0x55, 0x34, 0xF0, 0xC0, 0xD6, 0x71, 0xE3, 0x1F, 0xDB, 0x97, 0xE0, 0x7C, 0xD2, 0x2A};
static const u8 key_2E5E12F0[] = {0x8A, 0x7B, 0xC9, 0xD6, 0x52, 0x58, 0x88, 0xEA, 0x51, 0x83, 0x60, 0xCA, 0x16, 0x79, 0xE2, 0x07};
static const u8 xor_2E5E12F0[] = {0x69, 0xBA, 0x55, 0x34, 0xF0, 0xC0, 0xD6, 0x71, 0xE3, 0x1F, 0xDB, 0x97, 0xE0, 0x7C, 0xD2, 0x2A};
static const u8 key_2E5E13F0[] = {0xFF, 0xA4, 0x68, 0xC3, 0x31, 0xCA, 0xB7, 0x4C, 0xF1, 0x23, 0xFF, 0x01, 0x65, 0x3D, 0x26, 0x36};
static const u8 xor_2E5E13F0[] = {0x69, 0xBA, 0x55, 0x34, 0xF0, 0xC0, 0xD6, 0x71, 0xE3, 0x1F, 0xDB, 0x97, 0xE0, 0x7C, 0xD2, 0x2A};
static const u8 key_2E5E80F0[] = {0x0F, 0x74, 0xAF, 0x43, 0x75, 0xCD, 0xDA, 0x39, 0x81, 0x56, 0xD9, 0x61, 0x3E, 0x16, 0xC8, 0x92};
static const u8 xor_2E5E80F0[] = {0x69, 0xBA, 0x55, 0x34, 0xF0, 0xC0, 0xD6, 0x71, 0xE3, 0x1F, 0xDB, 0x97, 0xE0, 0x7C, 0xD2, 0x2A};
static const u8 key_2E5E90F0[] = {0x67, 0xE4, 0x8F, 0x4C, 0x08, 0xA0, 0x7D, 0xB1, 0x5F, 0x51, 0xA7, 0x72, 0x98, 0xA8, 0x2D, 0x7E};
static const u8 xor_2E5E90F0[] = {0x69, 0xBA, 0x55, 0x34, 0xF0, 0xC0, 0xD6, 0x71, 0xE3, 0x1F, 0xDB, 0x97, 0xE0, 0x7C, 0xD2, 0x2A};
static const u8 key_2FD30BF0[] = {0xD8, 0x58, 0x79, 0xF9, 0xA4, 0x22, 0xAF, 0x86, 0x90, 0xAC, 0xDA, 0x45, 0xCE, 0x60, 0x40, 0x3F};
static const u8 keys02G_E[] = {0x9D, 0x09, 0xFD, 0x20, 0xF3, 0x8F, 0x10, 0x69, 0x0D, 0xB2, 0x6F, 0x00, 0xCC, 0xC5, 0x51, 0x2E};
static const u8 keys03G_E[] = {0x4F, 0x44, 0x5C, 0x62, 0xB3, 0x53, 0xC4, 0x30, 0xFC, 0x3A, 0xA4, 0x5B, 0xEC, 0xFE, 0x51, 0xEA};
static const u8 keys05G_E[] = {0x5D, 0xAA, 0x72, 0xF2, 0x26, 0x60, 0x4D, 0x1C, 0xE7, 0x2D, 0xC8, 0xA3, 0x2F, 0x79, 0xC5, 0x54};
static const u8 oneseg_310[] = {0xC7, 0x27, 0x72, 0x85, 0xAB, 0xA7, 0xF7, 0xF0, 0x4C, 0xC1, 0x86, 0xCC, 0xE3, 0x7F, 0x17, 0xCA};
static const u8 oneseg_300[] = {0x76, 0x40, 0x9E, 0x08, 0xDB, 0x9B, 0x3B, 0xA1, 0x47, 0x8A, 0x96, 0x8E, 0xF3, 0xF7, 0x62, 0x92};
static const u8 oneseg_280[] = {0x23, 0xDC, 0x3B, 0xB5, 0xA9, 0x82, 0xD6, 0xEA, 0x63, 0xA3, 0x6E, 0x2B, 0x2B, 0xE9, 0xE1, 0x54};
static const u8 oneseg_260_271[] = {0x22, 0x43, 0x57, 0x68, 0x2F, 0x41, 0xCE, 0x65, 0x4C, 0xA3, 0x7C, 0xC6, 0xC4, 0xAC, 0xF3, 0x60};
static const u8 oneseg_slim[] = {0x12, 0x57, 0x0D, 0x8A, 0x16, 0x6D, 0x87, 0x06, 0x03, 0x7D, 0xC8, 0x8B, 0x62, 0xA3, 0x32, 0xA9};
static const u8 ms_app_main[] = {0x1E, 0x2E, 0x38, 0x49, 0xDA, 0xD4, 0x16, 0x08, 0x27, 0x2E, 0xF3, 0xBC, 0x37, 0x75, 0x80, 0x93};
static const u8 demokeys_280[] = {0x12, 0x99, 0x70, 0x5E, 0x24, 0x07, 0x6C, 0xD0, 0x2D, 0x06, 0xFE, 0x7E, 0xB3, 0x0C, 0x11, 0x26};
static const u8 demokeys_3XX_1[] = {0x47, 0x05, 0xD5, 0xE3, 0x56, 0x1E, 0x81, 0x9B, 0x09, 0x2F, 0x06, 0xDB, 0x6B, 0x12, 0x92, 0xE0};
static const u8 demokeys_3XX_2[] = {0xF6, 0x62, 0x39, 0x6E, 0x26, 0x22, 0x4D, 0xCA, 0x02, 0x64, 0x16, 0x99, 0x7B, 0x9A, 0xE7, 0xB8};
static const u8 ebootbin_271_new[] = {0xF4, 0xAE, 0xF4, 0xE1, 0x86, 0xDD, 0xD2, 0x9C, 0x7C, 0xC5, 0x42, 0xA6, 0x95, 0xA0, 0x83, 0x88};
static const u8 gameshare_260_271[] = {0xF9, 0x48, 0x38, 0x0C, 0x96, 0x88, 0xA7, 0x74, 0x4F, 0x65, 0xA0, 0x54, 0xC2, 0x76, 0xD9, 0xB8};
static const u8 gameshare_280[] = {0x2D, 0x86, 0x77, 0x3A, 0x56, 0xA4, 0x4F, 0xDD, 0x3C, 0x16, 0x71, 0x93, 0xAA, 0x8E, 0x11, 0x43};
static const u8 gameshare_300[] = {0x78, 0x1A, 0xD2, 0x87, 0x24, 0xBD, 0xA2, 0x96, 0x18, 0x3F, 0x89, 0x36, 0x72, 0x90, 0x92, 0x85};
static const u8 gameshare_310[] = {0xC9, 0x7D, 0x3E, 0x0A, 0x54, 0x81, 0x6E, 0xC7, 0x13, 0x74, 0x99, 0x74, 0x62, 0x18, 0xE7, 0xDD};
static const u8 key_380210F0[] = {0x32, 0x2C, 0xFA, 0x75, 0xE4, 0x7E, 0x93, 0xEB, 0x9F, 0x22, 0x80, 0x85, 0x57, 0x08, 0x98, 0x48};
static const u8 key_380280F0[] = {0x97, 0x09, 0x12, 0xD3, 0xDB, 0x02, 0xBD, 0xD8, 0xE7, 0x74, 0x51, 0xFE, 0xF0, 0xEA, 0x6C, 0x5C};
static const u8 key_380283F0[] = {0x34, 0x20, 0x0C, 0x8E, 0xA1, 0x86, 0x79, 0x84, 0xAF, 0x13, 0xAE, 0x34, 0x77, 0x6F, 0xEA, 0x89};
static const u8 key_407810F0[] = {0xAF, 0xAD, 0xCA, 0xF1, 0x95, 0x59, 0x91, 0xEC, 0x1B, 0x27, 0xD0, 0x4E, 0x8A, 0xF3, 0x3D, 0xE7};
static const u8 drmkeys_6XX_1[] = {0x36, 0xEF, 0x82, 0x4E, 0x74, 0xFB, 0x17, 0x5B, 0x14, 0x14, 0x05, 0xF3, 0xB3, 0x8A, 0x76, 0x18};
static const u8 drmkeys_6XX_2[] = {0x21, 0x52, 0x5D, 0x76, 0xF6, 0x81, 0x0F, 0x15, 0x2F, 0x4A, 0x40, 0x89, 0x63, 0xA0, 0x10, 0x55};
static const u8 pauth_98b83b5d_1[] = {0xB0, 0x24, 0xC8, 0x16, 0x43, 0xE8, 0xF0, 0x1C, 0x8C, 0x30, 0x67, 0x73, 0x3E, 0x96, 0x35, 0xEF};
static const u8 pauth_98b83b5d_xor[] = {0xA9, 0x1E, 0xDD, 0x7B, 0x09, 0xBB, 0x22, 0xB5, 0x9D, 0xA3, 0x30, 0x69, 0x13, 0x6E, 0x0E, 0xD8};
static const u8 pauth_f7aa47f6_1[] = {0xC5, 0xFB, 0x69, 0x03, 0x20, 0x7A, 0xCF, 0xBA, 0x2C, 0x90, 0xF8, 0xB8, 0x4D, 0xD2, 0xF1, 0xDE};
static const u8 pauth_f7aa47f6_2[] = {0x3A, 0x6B, 0x48, 0x96, 0x86, 0xA5, 0xC8, 0x80, 0x69, 0x6C, 0xE6, 0x4B, 0xF6, 0x04, 0x17, 0x44};
static const u8 pauth_f7aa47f6_xor[] = {0xA9, 0x1E, 0xDD, 0x7B, 0x09, 0xBB, 0x22, 0xB5, 0x9D, 0xA3, 0x30, 0x69, 0x13, 0x6E, 0x0E, 0xD8};
// PRXDecrypter 144-byte tag keys.
static const u32 g_key0[] = {
0x7b21f3be, 0x299c5e1d, 0x1c9c5e71, 0x96cb4645, 0x3c9b1be0, 0xeb85de3d,
0x4a7f2022, 0xc2206eaa, 0xd50b3265, 0x55770567, 0x3c080840, 0x981d55f2,
0x5fd8f6f3, 0xee8eb0c5, 0x944d8152, 0xf8278651, 0x2705bafa, 0x8420e533,
0x27154ae9, 0x4819aa32, 0x59a3aa40, 0x2cb3cf65, 0xf274466d, 0x3a655605,
0x21b0f88f, 0xc5b18d26, 0x64c19051, 0xd669c94e, 0xe87035f2, 0x9d3a5909,
0x6f4e7102, 0xdca946ce, 0x8416881b, 0xbab097a5, 0x249125c6, 0xb34c0872};
static const u32 g_key2[] = {
0xccfda932, 0x51c06f76, 0x046dcccf, 0x49e1821e, 0x7d3b024c, 0x9dda5865,
0xcc8c9825, 0xd1e97db5, 0x6874d8cb, 0x3471c987, 0x72edb3fc, 0x81c8365d,
0xe161e33a, 0xfc92db59, 0x2009b1ec, 0xb1a94ce4, 0x2f03696b, 0x87e236d8,
0x3b2b8ce9, 0x0305e784, 0xf9710883, 0xb039db39, 0x893bea37, 0xe74d6805,
0x2a5c38bd, 0xb08dc813, 0x15b32375, 0x46be4525, 0x0103fd90, 0xa90e87a2,
0x52aba66a, 0x85bf7b80, 0x45e8ce63, 0x4dd716d3, 0xf5e30d2d, 0xaf3ae456};
static const u32 g_key3[] = {
0xa6c8f5ca, 0x6d67c080, 0x924f4d3a, 0x047ca06a, 0x08640297, 0x4fd4a758,
0xbd685a87, 0x9b2701c2, 0x83b62a35, 0x726b533c, 0xe522fa0c, 0xc24b06b4,
0x459d1cac, 0xa8c5417b, 0x4fea62a2, 0x0615d742, 0x30628d09, 0xc44fab14,
0x69ff715e, 0xd2d8837d, 0xbeed0b8b, 0x1e6e57ae, 0x61e8c402, 0xbe367a06,
0x543f2b5e, 0xdb3ec058, 0xbe852075, 0x1e7e4dcc, 0x1564ea55, 0xec7825b4,
0xc0538cad, 0x70f72c7f, 0x49e8c3d0, 0xeda97ec5, 0xf492b0a4, 0xe05eb02a};
static const u32 g_key44[] = {
0xef80e005, 0x3a54689f, 0x43c99ccd, 0x1b7727be, 0x5cb80038, 0xdd2efe62,
0xf369f92c, 0x160f94c5, 0x29560019, 0xbf3c10c5, 0xf2ce5566, 0xcea2c626,
0xb601816f, 0x64e7481e, 0x0c34debd, 0x98f29cb0, 0x3fc504d7, 0xc8fb39f0,
0x0221b3d8, 0x63f936a2, 0x9a3a4800, 0x6ecc32e3, 0x8e120cfd, 0xb0361623,
0xaee1e689, 0x745502eb, 0xe4a6c61c, 0x74f23eb4, 0xd7fa5813, 0xb01916eb,
0x12328457, 0xd2bc97d2, 0x646425d8, 0x328380a5, 0x43da8ab1, 0x4b122ac9};
static const u32 g_key20[] = {
0x33b50800, 0xf32f5fcd, 0x3c14881f, 0x6e8a2a95, 0x29feefd5, 0x1394eae3,
0xbd6bd443, 0x0821c083, 0xfab379d3, 0xe613e165, 0xf5a754d3, 0x108b2952,
0x0a4b1e15, 0x61eadeba, 0x557565df, 0x3b465301, 0xae54ecc3, 0x61423309,
0x70c9ff19, 0x5b0ae5ec, 0x989df126, 0x9d987a5f, 0x55bc750e, 0xc66eba27,
0x2de988e8, 0xf76600da, 0x0382dccb, 0x5569f5f2, 0x8e431262, 0x288fe3d3,
0x656f2187, 0x37d12e9c, 0x2f539eb4, 0xa492998e, 0xed3958f7, 0x39e96523};
static const u32 g_key3A[] = {
0x67877069, 0x3abd5617, 0xc23ab1dc, 0xab57507d, 0x066a7f40, 0x24def9b9,
0x06f759e4, 0xdcf524b1, 0x13793e5e, 0x0359022d, 0xaae7e1a2, 0x76b9b2fa,
0x9a160340, 0x87822fba, 0x19e28fbb, 0x9e338a02, 0xd8007e9a, 0xea317af1,
0x630671de, 0x0b67ca7c, 0x865192af, 0xea3c3526, 0x2b448c8e, 0x8b599254,
0x4602e9cb, 0x4de16cda, 0xe164d5bb, 0x07ecd88e, 0x99ffe5f8, 0x768800c1,
0x53b091ed, 0x84047434, 0xb426dbbc, 0x36f948bb, 0x46142158, 0x749bb492};
static const u32 g_keyEBOOT1xx[] = {
0x18CB69EF, 0x158E8912, 0xDEF90EBB, 0x4CB0FB23, 0x3687EE18, 0x868D4A6E,
0x19B5C756, 0xEE16551D, 0xE7CB2D6C, 0x9747C660, 0xCE95143F, 0x2956F477,
0x03824ADE, 0x210C9DF1, 0x5029EB24, 0x81DFE69F, 0x39C89B00, 0xB00C8B91,
0xEF2DF9C2, 0xE13A93FC, 0x8B94A4A8, 0x491DD09D, 0x686A400D, 0xCED4C7E4,
0x96C8B7C9, 0x1EAADC28, 0xA4170B84, 0x505D5DDC, 0x5DA6C3CF, 0x0E5DFA2D,
0x6E7919B5, 0xCE5E29C7, 0xAAACDB94, 0x45F70CDD, 0x62A73725, 0xCCE6563D};
static const u32 g_keyEBOOT2xx[] = {
0xDA8E36FA, 0x5DD97447, 0x76C19874, 0x97E57EAF, 0x1CAB09BD, 0x9835BAC6,
0x03D39281, 0x03B205CF, 0x2882E734, 0xE714F663, 0xB96E2775, 0xBD8AAFC7,
0x1DD3EC29, 0xECA4A16C, 0x5F69EC87, 0x85981E92, 0x7CFCAE21, 0xBAE9DD16,
0xE6A97804, 0x2EEE02FC, 0x61DF8A3D, 0xDD310564, 0x9697E149, 0xC2453F3B,
0xF91D8456, 0x39DA6BC8, 0xB3E5FEF5, 0x89C593A3, 0xFB5C8ABC, 0x6C0B7212,
0xE10DD3CB, 0x98D0B2A8, 0x5FD61847, 0xF0DC2357, 0x7701166A, 0x0F5C3B68};
static const u32 g_demokeys_280[] = {
0x2A5282B4, 0x8706DDA5, 0x4C88EC1C, 0xD504708E, 0x72634DD2, 0xDD2E2F60,
0xE3D5FDB5, 0xE050637D, 0x295C69AC, 0x7B61F57D, 0x594412B0, 0x13D925CE,
0x2A6BE8DD, 0xBC9594E6, 0x1F4A8A39, 0xC56B5909, 0x52CFB2F7, 0x03EE089F,
0x5CA57A21, 0xDB64090F, 0x5E9A56F3, 0x13C56633, 0xD9C48D1D, 0xCDA05972,
0xD09E13B2, 0x7DEDD3DF, 0x364387BB, 0xCB207488, 0xBEC14B3F, 0x7C9C0D11,
0x9916ED40, 0x65909519, 0xC55BB1B3, 0xE997E084, 0xB483438B, 0xB8A2D255};
static const u32 g_keyUPDATER[] = {
0xA5603CBF, 0xD7482441, 0xF65764CC, 0x1F90060B, 0x4EA73E45, 0xE551D192,
0xE7B75D8A, 0x465A506E, 0x40FB1022, 0x2C273350, 0x8096DA44, 0x9947198E,
0x278DEE77, 0x745D062E, 0xC148FA45, 0x832582AF, 0x5FDB86DA, 0xCB15C4CE,
0x2524C62F, 0x6C2EC3B1, 0x369BE39E, 0xF7EB1FC4, 0x1E51CE1A, 0xD70536F4,
0xC34D39D8, 0x7418FB13, 0xE3C84DE1, 0xB118F03C, 0xA2018D4E, 0xE6D8770D,
0x5720F390, 0x17F96341, 0x60A4A68F, 0x1327DD28, 0x05944C64, 0x0C2C4C12};
static const u32 g_keyMEIMG250[] = {
0xA381FEBC, 0x99B9D5C9, 0x6C560A8D, 0x30309F95, 0x792646CC, 0x82B64E5E,
0x1A3951AD, 0x0A182EC4, 0xC46131B4, 0x77C50C8A, 0x325F16C6, 0x02D1942E,
0x0AA38AC4, 0x2A940AC6, 0x67034726, 0xE52DB133, 0xD2EF2107, 0x85C81E90,
0xC8D164BA, 0xC38DCE1D, 0x948BA275, 0x0DB84603, 0xE2473637, 0xCD74FCDA,
0x588E3D66, 0x6D28E822, 0x891E548B, 0xF53CF56D, 0x0BBDDB66, 0xC4B286AA,
0x2BEBBC4B, 0xFC261FF4, 0x92B8E705, 0xDCEE6952, 0x5E0442E5, 0x8BEB7F21};
static const u32 g_keyMEIMG260[] = {
0x11BFD698, 0xD7F9B324, 0xDD524927, 0x16215B86, 0x504AC36D, 0x5843B217,
0xE5A0DA47, 0xBB73A1E7, 0x2915DB35, 0x375CFD3A, 0xBB70A905, 0x272BEFCA,
0x2E960791, 0xEA0799BB, 0xB85AE6C8, 0xC9CAF773, 0x250EE641, 0x06E74A9E,
0x5244895D, 0x466755A5, 0x9A84AF53, 0xE1024174, 0xEEBA031E, 0xED80B9CE,
0xBC315F72, 0x5821067F, 0xE8313058, 0xD2D0E706, 0xE6D8933E, 0xD7D17FB4,
0x505096C4, 0xFDA50B3B, 0x4635AE3D, 0xEB489C8A, 0x422D762D, 0x5A8B3231};
static const u32 g_keyDEMOS27X[] = {
0x1ABF102F, 0xD596D071, 0x6FC552B2, 0xD4F2531F, 0xF025CDD9, 0xAF9AAF03,
0xE0CF57CF, 0x255494C4, 0x7003675E, 0x907BC884, 0x002D4EE4, 0x0B687A0D,
0x9E3AA44F, 0xF58FDA81, 0xEC26AC8C, 0x3AC9B49D, 0x3471C037, 0xB0F3834D,
0x10DC4411, 0xA232EA31, 0xE2E5FA6B, 0x45594B03, 0xE43A1C87, 0x31DAD9D1,
0x08CD7003, 0xFA9C2FDF, 0x5A891D25, 0x9B5C1934, 0x22F366E5, 0x5F084A32,
0x695516D5, 0x2245BE9F, 0x4F6DD705, 0xC4B8B8A1, 0xBC13A600, 0x77B7FC3B};
static const u32 g_keyUNK1[] = {
0x33B50800, 0xF32F5FCD, 0x3C14881F, 0x6E8A2A95, 0x29FEEFD5, 0x1394EAE3,
0xBD6BD443, 0x0821C083, 0xFAB379D3, 0xE613E165, 0xF5A754D3, 0x108B2952,
0x0A4B1E15, 0x61EADEBA, 0x557565DF, 0x3B465301, 0xAE54ECC3, 0x61423309,
0x70C9FF19, 0x5B0AE5EC, 0x989DF126, 0x9D987A5F, 0x55BC750E, 0xC66EBA27,
0x2DE988E8, 0xF76600DA, 0x0382DCCB, 0x5569F5F2, 0x8E431262, 0x288FE3D3,
0x656F2187, 0x37D12E9C, 0x2F539EB4, 0xA492998E, 0xED3958F7, 0x39E96523};
static const u32 g_key_GAMESHARE1xx[] = {
0x721B53E8, 0xFC3E31C6, 0xF85BA2A2, 0x3CF0AC72, 0x54EEA7AB, 0x5959BFCB,
0x54B8836B, 0xBC431313, 0x989EF2CF, 0xF0CE36B2, 0x98BA4CF8, 0xE971C931,
0xA0375DC8, 0x08E52FA0, 0xAC0DD426, 0x57E4D601, 0xC56E61C7, 0xEF1AB98A,
0xD1D9F8F4, 0x5FE9A708, 0x3EF09D07, 0xFA0C1A8C, 0xA91EEA5C, 0x58F482C5,
0x2C800302, 0x7EE6F6C3, 0xFF6ABBBB, 0x2110D0D0, 0xD3297A88, 0x980012D3,
0xDC59C87B, 0x7FDC5792, 0xDB3F5DA6, 0xFC23B787, 0x22698ED3, 0xB680E812};
static const u32 g_key_GAMESHARE2xx[] = {
0x94A757C7, 0x9FD39833, 0xF8508371, 0x328B0B29, 0x2CBCB9DA, 0x2918B9C6,
0x944C50BA, 0xF1DCE7D0, 0x640C3966, 0xC90B3D08, 0xF4AD17BA, 0x6CA0F84B,
0xF7767C67, 0xA4D3A55A, 0x4A085C6A, 0x6BB27071, 0xFA8B38FB, 0x3FDB31B8,
0x8B7196F2, 0xDB9BED4A, 0x51625B84, 0x4C1481B4, 0xF684F508, 0x30B44770,
0x93AA8E74, 0x90C579BC, 0x246EC88D, 0x2E051202, 0xC774842E, 0xA185D997,
0x7A2B3ADD, 0xFE835B6D, 0x508F184D, 0xEB4C4F13, 0x0E1993D3, 0xBA96DFD2};
static const u32 g_key_INDEXDAT1xx[] = {
0x76CB00AF, 0x111CE62F, 0xB7B27E36, 0x6D8DE8F9, 0xD54BF16A, 0xD9E90373,
0x7599D982, 0x51F82B0E, 0x636103AD, 0x8E40BC35, 0x2F332C94, 0xF513AAE9,
0xD22AFEE9, 0x04343987, 0xFC5BB80C, 0x12349D89, 0x14A481BB, 0x25ED3AE8,
0x7D500E4F, 0x43D1B757, 0x7B59FDAD, 0x4CFBBF34, 0xC3D17436, 0xC1DA21DB,
0xA34D8C80, 0x962B235D, 0x3E420548, 0x09CF9FFE, 0xD4883F5C, 0xD90E9CB5,
0x00AEF4E9, 0xF0886DE9, 0x62A58A5B, 0x52A55546, 0x971941B5, 0xF5B79FAC};
// The blocks inside an official updater's DATA.PSAR carry this tag. See Core/Util/PSARUnpack.cpp.
static const u32 g_keyUPDATER_PSAR[] = {
0x77B757DE, 0xEE62DD17, 0x5D03787B, 0x59CA8644, 0x93F68D20, 0x21819328,
0x86A74E71, 0x1B2482CA, 0x5F74AE58, 0x568D016C, 0x9A4D8832, 0x2EA24372,
0x820CF484, 0xFCFC06B9, 0x8A5BFB6A, 0xBF9F9CD7, 0x15850D01, 0x39ED5FBA,
0x4CC38393, 0xED3ADEAF, 0x1AA768BF, 0x89BD8A77, 0x46564165, 0x7333DBD9,
0x62E86C81, 0x03299B96, 0x73AFAE5A, 0x40A05320, 0x10664BE8, 0xE5B76A99,
0x29E0DD70, 0xEA602428, 0x2042AE30, 0x946F8D32, 0xA29E5F71, 0x7C0C7FD5};
struct TAG_INFO
{
u32 tag; // 4 byte value at offset 0xD0 in the PRX file
const u32 *key; // "step1_result" use for XOR step
u8 code;
u8 codeExtra;
// Most keys here are stored already scrambled, so the kirk7 pass the hardware would do is
// skipped. Set this for a key that's stored raw and needs that pass applied first.
bool scrambleKey;
};
static const TAG_INFO g_tagInfo[] =
{
{ 0x00000000, g_key0, 0x42 },
{ 0x02000000, g_key2, 0x45 },
{ 0x03000000, g_key3, 0x46 },
{ 0x4467415d, g_key44, 0x59, 0x59 },
{ 0x207bbf2f, g_key20, 0x5A, 0x5A },
{ 0x3ace4dce, g_key3A, 0x5B, 0x5B },
{ 0x07000000, g_key_INDEXDAT1xx, 0x4A },
{ 0x08000000, g_keyEBOOT1xx, 0x4B },
{ 0xC0CB167C, g_keyEBOOT2xx, 0x5D, 0x5D },
{ 0x7F24BDCD, g_demokeys_280, 0x60, 0x60 },
{ 0x0B000000, g_keyUPDATER, 0x4E },
{ 0x0C000000, g_keyDEMOS27X, 0x4F },
{ 0x0F000000, g_keyMEIMG250, 0x52 },
{ 0x862648D1, g_keyMEIMG260, 0x52, 0x52 },
{ 0x207BBF2F, g_keyUNK1, 0x5A, 0x5A },
{ 0x09000000, g_key_GAMESHARE1xx, 0x4C },
{ 0xBB67C59F, g_key_GAMESHARE2xx, 0x5E, 0x5E },
{ 0x0E000000, g_keyUPDATER_PSAR, 0x51, 0x00, true }
};
static const TAG_INFO *GetTagInfo(u32 tagFind)
{
for (u32 iTag = 0; iTag < sizeof(g_tagInfo)/sizeof(TAG_INFO); iTag++)
if (g_tagInfo[iTag].tag == tagFind)
return &g_tagInfo[iTag];
return NULL; // not found
}
////////// Decryption 2 //////////
struct TAG_INFO2
{
u32 tag; // 4 byte value at offset 0xD0 in the PRX file
const u8 *key; // 16 bytes keys
u8 code; // code for scramble
u8 type;
const u8 *seed;
};
static const TAG_INFO2 g_tagInfo2[] =
{
{ 0x4C9494F0, keys660_k1, 0x43 },
{ 0x4C9495F0, keys660_k2, 0x43 },
{ 0x4C9490F0, keys660_k3, 0x43 },
{ 0x4C9491F0, keys660_k8, 0x43 },
{ 0x4C9493F0, keys660_k4, 0x43 },
{ 0x4C9497F0, keys660_k5, 0x43 },
{ 0x4C9492F0, keys660_k6, 0x43 },
{ 0x4C9496F0, keys660_k7, 0x43 },
{ 0x457B90F0, keys660_v1, 0x5B },
{ 0x457B91F0, keys660_v7, 0x5B },
{ 0x457B92F0, keys660_v6, 0x5B },
{ 0x457B93F0, keys660_v3, 0x5B },
{ 0x380290F0, keys660_v2, 0x5A },
{ 0x380291F0, keys660_v8, 0x5A },
{ 0x380292F0, keys660_v4, 0x5A },
{ 0x380293F0, keys660_v5, 0x5A },
{ 0x4C948CF0, keys639_k3, 0x43 },
{ 0x4C948DF0, keys638_k4, 0x43 },
{ 0x4C948BF0, keys636_k2, 0x43 },
{ 0x4C948AF0, keys636_k1, 0x43 },
{ 0x4C9487F0, keys630_k8, 0x43 },
{ 0x457B83F0, keys630_k7, 0x5B },
{ 0x4C9486F0, keys630_k6, 0x43 },
{ 0x457B82F0, keys630_k5, 0x5B },
{ 0x457B81F0, keys630_k4, 0x5B },
{ 0x4C9485F0, keys630_k3, 0x43 },
{ 0x457B80F0, keys630_k2, 0x5B },
{ 0x4C9484F0, keys630_k1, 0x43 },
{ 0x457B28F0, keys620_e, 0x5B },
{ 0x457B0CF0, keys620_a, 0x5B },
{ 0x380228F0, keys620_5v, 0x5A },
{ 0x4C942AF0, keys620_5k, 0x43 },
{ 0x4C9428F0, keys620_5, 0x43 },
{ 0x4C941DF0, keys620_1, 0x43 },
{ 0x4C941CF0, keys620_0, 0x43 },
{ 0x4C9422F0, keys600_2, 0x43 },
{ 0x4C941EF0, keys600_1, 0x43 },
{ 0x4C9429F0, keys570_5k, 0x43 },
{ 0x457B0BF0, keys505_a, 0x5B },
{ 0x4C9419F0, keys505_1, 0x43 },
{ 0x4C9418F0, keys505_0, 0x43 },
{ 0x457B1EF0, keys500_c, 0x5B },
{ 0x4C941FF0, keys500_2, 0x43 },
{ 0x4C9417F0, keys500_1, 0x43 },
{ 0x4C9416F0, keys500_0, 0x43 },
{ 0x4C9414F0, keys390_0, 0x43 },
{ 0x4C9415F0, keys390_1, 0x43 },
{ 0x4C9412F0, keys370_0, 0x43 },
{ 0x4C9413F0, keys370_1, 0x43 },
{ 0x457B10F0, keys370_2, 0x5B },
{ 0x4C940DF0, keys360_0, 0x43 },
{ 0x4C9410F0, keys360_1, 0x43 },
{ 0x4C940BF0, keys330_0, 0x43 },
{ 0x457B0AF0, keys330_1, 0x5B },
{ 0x38020AF0, keys330_2, 0x5A },
{ 0x4C940AF0, keys330_3, 0x43 },
{ 0x4C940CF0, keys330_4, 0x43 },
{ 0xcfef09f0, keys310_0, 0x62 },
{ 0x457b08f0, keys310_1, 0x5B },
{ 0x380208F0, keys310_2, 0x5A },
{ 0xcfef08f0, keys310_3, 0x62 },
{ 0xCFEF07F0, keys303_0, 0x62 },
{ 0xCFEF06F0, keys300_0, 0x62 },
{ 0x457B06F0, keys300_1, 0x5B },
{ 0x380206F0, keys300_2, 0x5A },
{ 0xCFEF05F0, keys280_0, 0x62 },
{ 0x457B05F0, keys280_1, 0x5B },
{ 0x380205F0, keys280_2, 0x5A },
{ 0x16D59E03, keys260_0, 0x62 },
{ 0x76202403, keys260_1, 0x5B },
{ 0x0F037303, keys260_2, 0x5A },
{ 0x4C940FF0, key_2DA8, 0x43 },
{ 0x4467415D, key_22E0, 0x59 },
{ 0x00000000, key_21C0, 0x42 },
{ 0x01000000, key_2250, 0x43 },
{ 0x2E5E0AF0, key_2E5E0AF0, 0x48, 5, xor_2E5E0AF0 }, //[32]6.20
{ 0x2E5E10F0, key_2E5E10F0, 0x48, 5, xor_2E5E10F0 }, //[32]5.00 PSP-2000 (Game PSN Update 2 LBP)
{ 0x2E5E11F0, key_2E5E11F0, 0x48, 5, xor_2E5E11F0 }, //[32]6.00
{ 0x2E5E12F0, key_2E5E12F0, 0x48, 5, xor_2E5E12F0 },
{ 0x2E5E13F0, key_2E5E13F0, 0x48, 5, xor_2E5E13F0 },
{ 0x2E5E80F0, key_2E5E80F0, 0x48, 5, xor_2E5E80F0 }, //[32]6.30
{ 0x2E5E90F0, key_2E5E90F0, 0x48, 5, xor_2E5E90F0 },
{ 0x2FD30BF0, key_2FD30BF0, 0x47 },
{ 0xD91605F0, key_D91605F0, 0x5D, 2},
{ 0xD91606F0, key_D91606F0, 0x5D, 2},
{ 0xD91608F0, key_D91608F0, 0x5D, 2},
{ 0xD91609F0, key_D91609F0, 0x5D, 2},
{ 0xD9160AF0, key_D9160AF0, 0x5D, 2},
{ 0xD9160BF0, key_D9160BF0, 0x5D, 2},
{ 0xD91611F0, key_D91611F0, 0x5D, 2},
{ 0xD91612F0, key_D91612F0, 0x5D, 2},
{ 0xD91613F0, key_D91613F0, 0x5D, 2},
{ 0xD91614F0, key_D91614F0, 0x5D, 2},
{ 0xD91615F0, key_D91615F0, 0x5D, 2},
{ 0xD91616F0, key_D91616F0, 0x5D, 2},
{ 0xD91617F0, key_D91617F0, 0x5D, 2},
{ 0xD91618F0, key_D91618F0, 0x5D, 2},
{ 0xD91619F0, key_D91619F0, 0x5D, 2},
{ 0xD9161AF0, key_D9161AF0, 0x5D, 2},
{ 0xD91620F0, key_D91620F0, 0x5D, 2},
{ 0xD91621F0, key_D91621F0, 0x5D, 2},
{ 0xD91622F0, key_D91622F0, 0x5D, 2},
{ 0xD91623F0, key_D91623F0, 0x5D, 2},
{ 0xD91624F0, key_D91624F0, 0x5D, 2},
{ 0xD91628F0, key_D91628F0, 0x5D, 2},
{ 0xD91680F0, key_D91680F0, 0x5D, 6},
{ 0xD91681F0, key_D91681F0, 0x5D, 6},
{ 0xD82310F0, keys02G_E, 0x51 },
{ 0xD8231EF0, keys03G_E, 0x51 },
{ 0xD82328F0, keys05G_E, 0x51 },
{ 0x279D08F0, oneseg_310, 0x61 },
{ 0x279D06F0, oneseg_300, 0x61 },
{ 0x279D05F0, oneseg_280, 0x61 },
{ 0xD66DF703, oneseg_260_271, 0x61 },
{ 0x279D10F0, oneseg_slim, 0x61 },
{ 0x3C2A08F0, ms_app_main, 0x67 },
{ 0xADF305F0, demokeys_280, 0x60 },
{ 0xADF306F0, demokeys_3XX_1, 0x60 },
{ 0xADF308F0, demokeys_3XX_2, 0x60 },
{ 0x8004FD03, ebootbin_271_new, 0x5D, 2 },
{ 0x0A35EA03, gameshare_260_271, 0x5E },
{ 0x7B0505F0, gameshare_280, 0x5E },
{ 0x7B0506F0, gameshare_300, 0x5E },
{ 0x7B0508F0, gameshare_310, 0x5E },
{ 0x380210F0, key_380210F0, 0x5A },
{ 0x380280F0, key_380280F0, 0x5A },
{ 0x380283F0, key_380283F0, 0x5A },
{ 0x407810F0, key_407810F0, 0x6A },
{ 0xE92410F0, drmkeys_6XX_1, 0x40 },
{ 0x692810F0, drmkeys_6XX_2, 0x40 },
{ 0x2FD313F0, pauth_98b83b5d_1, 0x47, 5, pauth_98b83b5d_xor },
{ 0x2FD312F0, pauth_f7aa47f6_1, 0x47, 5, pauth_f7aa47f6_xor },
{ 0x2FD311F0, pauth_f7aa47f6_2, 0x47, 5, pauth_f7aa47f6_xor },
};
static const TAG_INFO2 *GetTagInfo2(u32 tagFind)
{
for (u32 iTag = 0; iTag < sizeof(g_tagInfo2) / sizeof(TAG_INFO2); iTag++)
{
if (g_tagInfo2[iTag].tag == tagFind)
{
return &g_tagInfo2[iTag];
}
}
return NULL; // not found
}
static std::array<u8, 0x90> expandSeed(const u8 *seed, int key, const u8 *bonusSeed = nullptr)
{
std::array<u8, 0x90> expandedSeed;
// perform some AES-CTR like encryption of seed
for (auto i = 0u; i < expandedSeed.size(); i += 0x10)
{
memcpy(expandedSeed.data()+i, seed, 0x10);
expandedSeed[i] = i/0x10;
}
kirk7(expandedSeed.data(), expandedSeed.data(), expandedSeed.size(), key);
if (bonusSeed)
{
for (auto i = 0u; i < expandedSeed.size(); ++i)
{
expandedSeed[i] ^= bonusSeed[i % 0x10];
}
}
return expandedSeed;
}
template<typename It>
static void decryptKirkHeader(u8 *outbuf, const u8 *inbuf, It xorbuf, int key)
{
for (auto i = 0; i < 0x40; ++i)
{
outbuf[i] = inbuf[i] ^ *xorbuf++;
}
kirk7(outbuf, outbuf, 0x40, key);
for (auto i = 0; i < 0x40; ++i)
{
outbuf[i] = outbuf[i] ^ *xorbuf++;
}
}
template <typename T>
static void decryptKirkHeaderType0(u8 *outbuf, const u8 *inbuf, T xorbuf, int key)
{
for (auto i = 0; i < 0x70; ++i)
{
outbuf[i] = inbuf[i] ^ xorbuf[i+0x14];
}
kirk7(outbuf, outbuf, 0x70, key);
for (auto i = 0; i < 0x70; ++i)
{
outbuf[i] = outbuf[i] ^ xorbuf[i+0x20];
}
}
struct PRXType0
{
explicit PRXType0(const u8 *prx)
{
memcpy(tag, prx+0xD0, sizeof(tag));
memcpy(sha1, prx+0xD4, sizeof(sha1));
memcpy(unused, prx+0xE8, sizeof(unused));
memcpy(kirkBlock, prx+0x110, 0x40); // key data
memcpy(kirkBlock+0x40, prx+0x80, sizeof(kirkBlock)-0x40);
memcpy(prxHeader, prx, sizeof(prxHeader));
}
u8 tag[4];
u8 sha1[0x14];
u8 unused[0x28];
u8 kirkBlock[0x90];
u8 prxHeader[0x80];
};
static_assert(sizeof(PRXType0) == 0x150, "inconsistent size of PRX Type 0");
struct PRXType1
{
explicit PRXType1(const u8 *prx)
{
memcpy(tag, prx+0xD0, sizeof(tag));
memcpy(sha1, prx+0xD4, sizeof(sha1));
memcpy(unused, prx+0xE8, sizeof(unused));
memcpy(kirkBlock, prx+0x110, 0x40); // key data
memcpy(kirkBlock+0x40, prx+0x80, sizeof(kirkBlock)-0x40);
memcpy(prxHeader, prx, sizeof(prxHeader));
}
void decrypt(int key)
{
kirk7(sha1+0xC, sha1+0xC, 0xA0, key);
}
u8 tag[4];
u8 sha1[0x14];
u8 unused[0x28];
u8 kirkBlock[0x90];
u8 prxHeader[0x80];
};
static_assert(sizeof(PRXType1) == 0x150, "inconsistent size of PRX Type 1");
struct PRXType2
{
explicit PRXType2(const u8 *prx)
{
memcpy(tag, prx+0xD0, sizeof(tag));
memset(empty, 0, sizeof(empty));
memcpy(id, prx+0x140, sizeof(id));
memcpy(sha1, prx+0x12C, sizeof(sha1));
// kirk header is split between 0x80->0xB0 and 0xC0->0xD0
memcpy(kirkHeader, prx+0x80, sizeof(kirkHeader)-0x10);
memcpy(kirkHeader+0x30, prx+0xC0, 0x10);
memcpy(kirkMetadata, prx+0xB0, sizeof(kirkMetadata));
memcpy(prxHeader, prx, sizeof(prxHeader));
}
void decrypt(int key)
{
kirk7(id, id, 0x60, key);
}
u8 tag[4];
u8 empty[0x58];
u8 id[0x10];
u8 sha1[0x14];
u8 kirkHeader[0x40];
u8 kirkMetadata[0x10];
u8 prxHeader[0x80];
};
static_assert(sizeof(PRXType2) == 0x150, "inconsistent size of PRX Type 2");
struct PRXType5
{
explicit PRXType5(const u8 *prx)
{
memcpy(tag, prx+0xD0, sizeof(tag));
memset(empty, 0, sizeof(empty));
memcpy(id, prx+0x140, sizeof(id));
memcpy(sha1, prx+0x12C, sizeof(sha1));
// kirk header is split between 0x80->0xB0 and 0xC0->0xD0
memcpy(kirkHeader, prx+0x80, sizeof(kirkHeader)-0x10);
memcpy(kirkHeader+0x30, prx+0xC0, 0x10);
memcpy(kirkMetadata, prx+0xB0, sizeof(kirkMetadata));
memcpy(prxHeader, prx, sizeof(prxHeader));
}
void decrypt(int key, const u8 *xor1, const u8 *xor2)
{
// first step is to decrypt kirk header + SHA1
u8 data[0x50];
memcpy(data, kirkHeader, sizeof(kirkHeader));
memcpy(data+sizeof(kirkHeader), sha1, 0x10);
for (auto i = 0; i < 0x50; ++i)
{
if (xor1)
{
data[i] ^= xor1[i % 0x10];
}
if (xor2)
{
data[i] ^= xor2[i % 0x10];
}
}
kirk7(data, data, 0x50, key);
// copy the result back
memcpy(kirkHeader, data, sizeof(kirkHeader));
memcpy(sha1, data+sizeof(kirkHeader), 0x10);
// second step is a XOR then decrypt id through to kirk header
if (xor1)
{
u8 *p = id;
for (auto i = 0; i < 0x60; ++i)
{
p[i] ^= xor1[i % 0x10];
}
}
kirk7(id, id, 0x60, key);
}
u8 tag[4];
u8 empty[0x58];
u8 id[0x10];
u8 sha1[0x14];
u8 kirkHeader[0x40];
u8 kirkMetadata[0x10];
u8 prxHeader[0x80];
};
static_assert(sizeof(PRXType5) == 0x150, "inconsistent size of PRX Type 5");
struct PRXType6
{
explicit PRXType6(const u8 *prx)
{
memcpy(tag, prx+0xD0, sizeof(tag));
memset(empty, 0, sizeof(empty));
memcpy(ecdsaSignatureTail, prx+0x10C, sizeof(ecdsaSignatureTail));
memcpy(id, prx+0x140, sizeof(id));
memcpy(sha1, prx+0x12C, sizeof(sha1));
// kirk header is split between 0x80->0xB0 and 0xC0->0xD0
memcpy(kirkHeader, prx+0x80, sizeof(kirkHeader)-0x10);
memcpy(kirkHeader+0x30, prx+0xC0, 0x10);
memcpy(kirkMetadata, prx+0xB0, sizeof(kirkMetadata));
memcpy(prxHeader, prx, sizeof(prxHeader));
}
void decrypt(int key)
{
kirk7(id, id, 0x60, key);
}
u8 tag[4];
u8 empty[0x38];
u8 ecdsaSignatureTail[0x20];
u8 id[0x10];
u8 sha1[0x14];
u8 kirkHeader[0x40];
u8 kirkMetadata[0x10];
u8 prxHeader[0x80];
};
static_assert(sizeof(PRXType6) == 0x150, "inconsistent size of PRX Type 6");
static int pspDecryptType0(KirkState *kirk, const u8 *inbuf, u8 *outbuf, u32 size)
{
INFO_LOG(Log::Loader, "Decrypting tag %02X", (u32)*(u32_le *)&inbuf[0xD0]);
const auto decryptSize = *(s32_le*)&inbuf[0xB0];
const auto pti = GetTagInfo((u32)*(u32_le *)&inbuf[0xD0]);
if (!pti)
{
return -1;
}
// no need to expand seed, and no need to decrypt
// normally this would be a kirk7 op, but we have the seed pre-decrypted
std::array<u8, 0x90> xorbuf;
memcpy(xorbuf.data(), reinterpret_cast<const u8 *>(pti->key), xorbuf.size());
if (pti->scrambleKey)
{
// ...except for keys we have in their raw form, which need that pass after all.
kirk7(xorbuf.data(), xorbuf.data(), xorbuf.size(), pti->code);
}
// construct the header format for a type 0 prx
PRXType0 type0(inbuf);
SHA_CTX ctx;
SHAInit(&ctx);
SHAUpdate(&ctx, xorbuf.data(), 0x14);
SHAUpdate(&ctx, type0.unused, sizeof(type0.unused));
SHAUpdate(&ctx, type0.kirkBlock, sizeof(type0.kirkBlock));
SHAUpdate(&ctx, type0.prxHeader, sizeof(type0.prxHeader));
u8 sha1[0x14];
SHAFinal(sha1, &ctx);
if (memcmp(sha1, type0.sha1, sizeof(sha1)) != 0)
{
return -3;
}
constexpr auto offset = sizeof(PSP_Header)-sizeof(KIRK_CMD1_HEADER)-sizeof(type0.prxHeader);
KIRK_CMD1_HEADER *header = reinterpret_cast<KIRK_CMD1_HEADER *>(outbuf+offset);
if (outbuf != inbuf)
{
memcpy(outbuf, inbuf, size);
}
memcpy(header, type0.kirkBlock, sizeof(KIRK_CMD1_HEADER));
memcpy(reinterpret_cast<u8*>(header)+sizeof(KIRK_CMD1_HEADER), type0.prxHeader, sizeof(type0.prxHeader));
decryptKirkHeaderType0(reinterpret_cast<u8*>(header), type0.kirkBlock, xorbuf, pti->code);
if (kirk_sceUtilsBufferCopyWithRange(kirk, outbuf, size, reinterpret_cast<u8*>(header), size - offset, KIRK_CMD_DECRYPT_PRIVATE) != 0)
{
return -4;
}
return decryptSize;
}
static int pspDecryptType1(KirkState *kirk, const u8 *inbuf, u8 *outbuf, u32 size)
{
INFO_LOG(Log::Loader, "Decrypting tag %02X", (u32)*(u32_le *)&inbuf[0xD0]);
const auto decryptSize = *(s32_le*)&inbuf[0xB0];
const auto pti = GetTagInfo((u32)*(u32_le *)&inbuf[0xD0]);
if (!pti)
{
return -1;
}
// no need to expand seed, and no need to decrypt
// normally this would be a kirk7 op, but we have the seed pre-decrypted
std::array<u8, 0x90> xorbuf;
memcpy(xorbuf.data(), reinterpret_cast<const u8 *>(pti->key), xorbuf.size());
// construct the header format for a type 1 prx
PRXType1 type1(inbuf);
type1.decrypt(pti->code);
SHA_CTX ctx;
SHAInit(&ctx);
SHAUpdate(&ctx, xorbuf.data(), 0x14);
SHAUpdate(&ctx, type1.unused, sizeof(type1.unused));
SHAUpdate(&ctx, type1.kirkBlock, sizeof(type1.kirkBlock));
SHAUpdate(&ctx, type1.prxHeader, sizeof(type1.prxHeader));
u8 sha1[0x14];
SHAFinal(sha1, &ctx);
if (memcmp(sha1, type1.sha1, sizeof(sha1)) != 0)
{
return -3;
}
constexpr auto offset = sizeof(PSP_Header)-sizeof(KIRK_CMD1_HEADER)-sizeof(type1.prxHeader);
KIRK_CMD1_HEADER *header = reinterpret_cast<KIRK_CMD1_HEADER *>(outbuf+offset);
if (outbuf != inbuf)
{
memcpy(outbuf, inbuf, size);
}
memcpy(header, type1.kirkBlock, sizeof(KIRK_CMD1_HEADER));
memcpy(reinterpret_cast<u8*>(header)+sizeof(KIRK_CMD1_HEADER), type1.prxHeader, sizeof(type1.prxHeader));
decryptKirkHeaderType0(reinterpret_cast<u8*>(header), type1.kirkBlock, xorbuf, pti->code);
if (kirk_sceUtilsBufferCopyWithRange(kirk, outbuf, size, reinterpret_cast<u8*>(header), size - offset, KIRK_CMD_DECRYPT_PRIVATE) != 0)
{
return -4;
}
return decryptSize;
}
static int pspDecryptType2(KirkState *kirk, const u8 *inbuf, u8 *outbuf, u32 size)
{
INFO_LOG(Log::Loader, "Decrypting tag %02X", (u32)*(u32_le *)&inbuf[0xD0]);
const auto decryptSize = *(s32_le*)&inbuf[0xB0];
const auto pti = GetTagInfo2((u32)*(u32_le *)&inbuf[0xD0]);
if (!pti)
{
return -1;
}
// check if range is non-zero
if (std::any_of(inbuf+0xD4, inbuf+0xD4+0x58, [](u8 x) { return x != 0; }))
{
return -2;
}
// expand the seed into a xor buffer
auto xorbuf = expandSeed(pti->key, pti->code);
// construct the header format for a type 2 prx
PRXType2 type2(inbuf);
type2.decrypt(pti->code);
SHA_CTX ctx;
SHAInit(&ctx);
SHAUpdate(&ctx, type2.tag, sizeof(type2.tag));
SHAUpdate(&ctx, xorbuf.data(), 0x10);
SHAUpdate(&ctx, type2.empty, sizeof(type2.empty));
SHAUpdate(&ctx, type2.id, sizeof(type2.id));
SHAUpdate(&ctx, type2.kirkHeader, sizeof(type2.kirkHeader));
SHAUpdate(&ctx, type2.kirkMetadata, sizeof(type2.kirkMetadata));
SHAUpdate(&ctx, type2.prxHeader, sizeof(type2.prxHeader));
u8 sha1[0x14];
SHAFinal(sha1, &ctx);
if (memcmp(sha1, type2.sha1, sizeof(sha1)) != 0)
{
return -3;
}
constexpr auto offset = sizeof(PSP_Header)-sizeof(KIRK_CMD1_HEADER)-sizeof(type2.prxHeader);
KIRK_CMD1_HEADER *header = reinterpret_cast<KIRK_CMD1_HEADER *>(outbuf+offset);
if (outbuf != inbuf)
{
memcpy(outbuf, inbuf, size);
}
memset(header, 0, sizeof(KIRK_CMD1_HEADER));
memcpy(reinterpret_cast<u8*>(&header->data_size), type2.kirkMetadata, sizeof(type2.kirkMetadata));
memcpy(reinterpret_cast<u8*>(header)+sizeof(KIRK_CMD1_HEADER), type2.prxHeader, sizeof(type2.prxHeader));
decryptKirkHeader(reinterpret_cast<u8*>(header), type2.kirkHeader, xorbuf.cbegin()+0x10, pti->code);
header->mode = 1;
if (kirk_sceUtilsBufferCopyWithRange(kirk, outbuf, size, reinterpret_cast<u8*>(header), size - offset, KIRK_CMD_DECRYPT_PRIVATE) != 0)
{
return -4;
}
return decryptSize;
}
static int pspDecryptType5(KirkState *kirk, const u8 *inbuf, u8 *outbuf, u32 size, const u8 *seed)
{
INFO_LOG(Log::Loader, "Decrypting tag %02X", (u32)*(u32_le *)&inbuf[0xD0]);
const auto decryptSize = *(s32_le*)&inbuf[0xB0];
const auto pti = GetTagInfo2((u32)*(u32_le *)&inbuf[0xD0]);
if (!pti)
{
return -1;
}
// check if range is non-zero
if (std::any_of(inbuf+0xD4+1, inbuf+0xD4+0x58, [](u8 x) { return x != 0; }))
{
return -2;
}
// expand the seed into a xor buffer
auto xorbuf = expandSeed(pti->key, pti->code, seed);
// construct the header format for a type 2 prx
PRXType5 type5(inbuf);
type5.decrypt(pti->code, pti->seed, seed);
SHA_CTX ctx;
SHAInit(&ctx);
SHAUpdate(&ctx, type5.tag, sizeof(type5.tag));
SHAUpdate(&ctx, xorbuf.data(), 0x10);
SHAUpdate(&ctx, type5.empty, sizeof(type5.empty));
SHAUpdate(&ctx, type5.id, sizeof(type5.id));
SHAUpdate(&ctx, type5.kirkHeader, sizeof(type5.kirkHeader));
SHAUpdate(&ctx, type5.kirkMetadata, sizeof(type5.kirkMetadata));
SHAUpdate(&ctx, type5.prxHeader, sizeof(type5.prxHeader));
u8 sha1[0x14];
SHAFinal(sha1, &ctx);
if (memcmp(sha1, type5.sha1, sizeof(sha1)) != 0)
{
return -3;
}
constexpr auto offset = sizeof(PSP_Header)-sizeof(KIRK_CMD1_HEADER)-sizeof(type5.prxHeader);
KIRK_CMD1_HEADER *header = reinterpret_cast<KIRK_CMD1_HEADER *>(outbuf+offset);
if (outbuf != inbuf)
{
memcpy(outbuf, inbuf, size);
}
memset(header, 0, sizeof(KIRK_CMD1_HEADER));
memcpy(reinterpret_cast<u8*>(&header->data_size), type5.kirkMetadata, sizeof(type5.kirkMetadata));
memcpy(reinterpret_cast<u8*>(header)+sizeof(KIRK_CMD1_HEADER), type5.prxHeader, sizeof(type5.prxHeader));
decryptKirkHeader(reinterpret_cast<u8*>(header), type5.kirkHeader, xorbuf.cbegin()+0x10, pti->code);
header->mode = 1;
if (kirk_sceUtilsBufferCopyWithRange(kirk, outbuf, size, reinterpret_cast<u8*>(header), size - offset, KIRK_CMD_DECRYPT_PRIVATE) != 0)
{
return -4;
}
return decryptSize;
}
static int pspDecryptType6(KirkState *kirk, const u8 *inbuf, u8 *outbuf, u32 size)
{
INFO_LOG(Log::Loader, "Decrypting tag %02X", (u32)*(u32_le *)&inbuf[0xD0]);
const auto decryptSize = *(s32_le*)&inbuf[0xB0];
const auto pti = GetTagInfo2((u32)*(u32_le *)&inbuf[0xD0]);
if (!pti)
{
return -1;
}
// check if range is non-zero
if (std::any_of(inbuf+0xD4, inbuf+0xD4+0x38, [](u8 x) { return x != 0; }))
{
return -2;
}
// expand the seed into a xor buffer
auto xorbuf = expandSeed(pti->key, pti->code);
// construct the header format for a type 2 prx
PRXType6 type6(inbuf);
type6.decrypt(pti->code);
SHA_CTX ctx;
SHAInit(&ctx);
SHAUpdate(&ctx, type6.tag, sizeof(type6.tag));
SHAUpdate(&ctx, xorbuf.data(), 0x10);
SHAUpdate(&ctx, type6.empty, sizeof(type6.empty));
SHAUpdate(&ctx, type6.ecdsaSignatureTail, sizeof(type6.ecdsaSignatureTail));
SHAUpdate(&ctx, type6.id, sizeof(type6.id));
SHAUpdate(&ctx, type6.kirkHeader, sizeof(type6.kirkHeader));
SHAUpdate(&ctx, type6.kirkMetadata, sizeof(type6.kirkMetadata));
SHAUpdate(&ctx, type6.prxHeader, sizeof(type6.prxHeader));
u8 sha1[0x14];
SHAFinal(sha1, &ctx);
if (memcmp(sha1, type6.sha1, sizeof(sha1)) != 0)
{
return -3;
}
constexpr auto offset = sizeof(PSP_Header)-sizeof(KIRK_CMD1_ECDSA_HEADER)-sizeof(type6.prxHeader);
KIRK_CMD1_ECDSA_HEADER *header = reinterpret_cast<KIRK_CMD1_ECDSA_HEADER *>(outbuf+offset);
if (outbuf != inbuf)
{
memcpy(outbuf, inbuf, size);
}
memset(header, 0, sizeof(KIRK_CMD1_ECDSA_HEADER));
memcpy(outbuf+offset+0x40, type6.ecdsaSignatureTail, sizeof(type6.ecdsaSignatureTail));
memcpy(reinterpret_cast<u8*>(&header->data_size), type6.kirkMetadata, sizeof(type6.kirkMetadata));
memcpy(reinterpret_cast<u8*>(header)+sizeof(KIRK_CMD1_ECDSA_HEADER), type6.prxHeader, sizeof(type6.prxHeader));
decryptKirkHeader(reinterpret_cast<u8*>(header), type6.kirkHeader, xorbuf.cbegin()+0x10, pti->code);
header->mode = 1;
header->ecdsa_hash = 1;
if (kirk_sceUtilsBufferCopyWithRange(kirk, outbuf, size, reinterpret_cast<u8*>(header), size - offset, KIRK_CMD_DECRYPT_PRIVATE) != 0)
{
return -4;
}
return decryptSize;
}
int pspDecryptPRX(const u8 *inbuf, u8 *outbuf, u32 size, const u8 *seed)
{
// Every type below reads the tag at 0xD0, the size at 0xB0 and key data as far as 0x150, and
// writes a KIRK header into outbuf at a fixed offset derived from sizeof(PSP_Header). Without
// this, a PRX declaring a tiny psp_size got read past its end, had a header written past the end
// of the (equally tiny) output buffer, and passed "size - offset" to KIRK as an unsigned
// underflow. Callers must supply at least a whole header on both sides.
if (size < sizeof(PSP_Header)) {
ERROR_LOG(Log::Loader, "PRX too small to decrypt: %d bytes, need at least %d", (int)size, (int)sizeof(PSP_Header));
return -1;
}
KirkState kirk{};
kirk_init(&kirk);
// this would be significantly better if we had a log of the tags
// and their appropriate prx types
// since we don't know the PRX type we attempt a decrypt using all
auto res = pspDecryptType0(&kirk, inbuf, outbuf, size);
if (res >= 0)
return res;
res = pspDecryptType1(&kirk, inbuf, outbuf, size);
if (res >= 0)
return res;
res = pspDecryptType2(&kirk, inbuf, outbuf, size);
if (res >= 0)
return res;
res = pspDecryptType5(&kirk, inbuf, outbuf, size, seed);
if (res >= 0)
return res;
return pspDecryptType6(&kirk, inbuf, outbuf, size);
}