295 lines
7.0 KiB
C
295 lines
7.0 KiB
C
|
|
// Copyright (C) 2013 tpu
|
|
// Copyright (C) 2015 Hykem <hykem@hotmail.com>
|
|
// Licensed under the terms of the GNU GPL, version 3
|
|
// http://www.gnu.org/licenses/gpl-3.0.txt
|
|
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "kirk_engine.h"
|
|
#include "crypto.h"
|
|
#include "amctrl.h"
|
|
|
|
/*************************************************************/
|
|
|
|
|
|
typedef struct {
|
|
unsigned char vkey[16];
|
|
|
|
int open_flag;
|
|
int key_index;
|
|
int drm_type;
|
|
int mac_type;
|
|
int cipher_type;
|
|
|
|
int data_size;
|
|
int align_size;
|
|
int block_size;
|
|
int block_nr;
|
|
int data_offset;
|
|
int table_offset;
|
|
|
|
unsigned char* buf;
|
|
} PGD_HEADER;
|
|
|
|
/*
|
|
typedef struct {
|
|
PGD_DESC pgdesc;
|
|
unsigned __int32 key_index; // 0x30
|
|
unsigned __int8 pgd_key[16]; // 0x34
|
|
unsigned __int32 flag; // 0x44
|
|
unsigned __int32 flag_open; // 0x48
|
|
unsigned __int32 pgd_offset; // 0x4C
|
|
int seek_offset; // 0x50
|
|
unsigned __int32 data_offset; // 0x54
|
|
unsigned __int32 table_offset;// 0x58
|
|
unsigned __int32 unk_5c;
|
|
unsigned __int32 unk_60;
|
|
}PspIoHookParam;
|
|
*/
|
|
|
|
unsigned __int8 dnas_key1A90_pgd[] = {0xED,0xE2,0x5D,0x2D,0xBB,0xF8,0x12,0xE5,0x3C,0x5C,0x59,0x32,0xFA,0xE3,0xE2,0x43};
|
|
unsigned __int8 dnas_key1AA0_pgd[] = {0x27,0x74,0xFB,0xEB,0xA4,0xA0, 1,0xD7, 2,0x56,0x9E,0x33,0x8C,0x19,0x57,0x83};
|
|
|
|
__declspec(dllexport) int encrypt_pgd(u8* data, int data_size, int block_size, int key_index, int drm_type, int flag, u8* key, u8* pgd_data)
|
|
{
|
|
MAC_KEY mkey;
|
|
CIPHER_KEY ckey;
|
|
|
|
// Additional size variables.
|
|
int data_offset = 0x90;
|
|
int align_size = (data_size + 15) & ~15;
|
|
int table_offset = data_offset + align_size;
|
|
int block_nr = ((align_size + block_size - 1) & ~(block_size - 1)) / block_size;
|
|
int pgd_size = 0x90 + align_size + block_nr * 16;
|
|
|
|
// Build new PGD header.
|
|
u8* pgd = (u8*)malloc(pgd_size);
|
|
memset(pgd, 0, pgd_size);
|
|
memcpy(pgd + data_offset, data, data_size);
|
|
|
|
// Set magic PGD.
|
|
pgd[0] = 0x00;
|
|
pgd[1] = 0x50;
|
|
pgd[2] = 0x47;
|
|
pgd[3] = 0x44;
|
|
|
|
// Set key index and drm type.
|
|
*(u32*)(pgd + 4) = key_index;
|
|
*(u32*)(pgd + 8) = drm_type;
|
|
|
|
// Select the hashing, crypto and open modes.
|
|
int mac_type;
|
|
int cipher_type;
|
|
int open_flag = flag;
|
|
if (drm_type == 1)
|
|
{
|
|
mac_type = 1;
|
|
open_flag |= 4;
|
|
if (key_index > 1)
|
|
{
|
|
mac_type = 3;
|
|
open_flag |= 8;
|
|
}
|
|
cipher_type = 1;
|
|
}
|
|
else
|
|
{
|
|
mac_type = 2;
|
|
cipher_type = 2;
|
|
}
|
|
|
|
// Select the fixed DNAS key.
|
|
u8* fkey = NULL;
|
|
if ((open_flag & 0x2) == 0x2)
|
|
fkey = dnas_key1A90_pgd;
|
|
if ((open_flag & 0x1) == 0x1)
|
|
fkey = dnas_key1AA0_pgd;
|
|
|
|
if (fkey == NULL)
|
|
{
|
|
printf("PGD: Invalid PGD DNAS flag! %08x\n", flag);
|
|
return -1;
|
|
}
|
|
|
|
// Set the decryption parameters in the decrypted header.
|
|
*(u32*)(pgd + 0x44) = data_size;
|
|
*(u32*)(pgd + 0x48) = block_size;
|
|
*(u32*)(pgd + 0x4C) = data_offset;
|
|
|
|
// Generate random header and data keys.
|
|
sceUtilsBufferCopyWithRange(pgd + 0x10, 0x30, 0, 0, KIRK_CMD_PRNG);
|
|
|
|
// Encrypt the data.
|
|
sceDrmBBCipherInit(&ckey, cipher_type, 2, pgd + 0x30, key, 0);
|
|
sceDrmBBCipherUpdate(&ckey, pgd + data_offset, align_size);
|
|
sceDrmBBCipherFinal(&ckey);
|
|
|
|
// Build data MAC hash.
|
|
int i;
|
|
for (i = 0; i < block_nr; i++)
|
|
{
|
|
int rsize = align_size - i * block_size;
|
|
if (rsize > block_size)
|
|
rsize = block_size;
|
|
|
|
sceDrmBBMacInit(&mkey, mac_type);
|
|
sceDrmBBMacUpdate(&mkey, pgd + data_offset + i * block_size, rsize);
|
|
sceDrmBBMacFinal(&mkey, pgd + table_offset + i * 16, key);
|
|
}
|
|
|
|
// Build table MAC hash.
|
|
sceDrmBBMacInit(&mkey, mac_type);
|
|
sceDrmBBMacUpdate(&mkey, pgd + table_offset, block_nr * 16);
|
|
sceDrmBBMacFinal(&mkey, pgd + 0x60, key);
|
|
|
|
// Encrypt the PGD header block (0x30 bytes).
|
|
sceDrmBBCipherInit(&ckey, cipher_type, 2, pgd + 0x10, key, 0);
|
|
sceDrmBBCipherUpdate(&ckey, pgd + 0x30, 0x30);
|
|
sceDrmBBCipherFinal(&ckey);
|
|
|
|
// Build MAC hash at 0x70 (key hash).
|
|
sceDrmBBMacInit(&mkey, mac_type);
|
|
sceDrmBBMacUpdate(&mkey, pgd + 0x00, 0x70);
|
|
sceDrmBBMacFinal(&mkey, pgd + 0x70, key);
|
|
|
|
// Build MAC hash at 0x80 (DNAS hash).
|
|
sceDrmBBMacInit(&mkey, mac_type);
|
|
sceDrmBBMacUpdate(&mkey, pgd + 0x00, 0x80);
|
|
sceDrmBBMacFinal(&mkey, pgd + 0x80, fkey);
|
|
|
|
// Copy back the generated PGD file.
|
|
memcpy(pgd_data, pgd, pgd_size);
|
|
|
|
return pgd_size;
|
|
}
|
|
|
|
/*
|
|
PGD decrypt function.
|
|
*/
|
|
__declspec(dllexport) int decrypt_pgd(u8* pgd_data, int pgd_size, int flag, u8* key)
|
|
{
|
|
int result;
|
|
PGD_HEADER PGD[sizeof(PGD_HEADER)];
|
|
MAC_KEY mkey;
|
|
CIPHER_KEY ckey;
|
|
u8* fkey;
|
|
|
|
// Read in the PGD header parameters.
|
|
memset(PGD, 0, sizeof(PGD_HEADER));
|
|
|
|
PGD->buf = pgd_data;
|
|
PGD->key_index = *(u32*)(pgd_data + 4);
|
|
PGD->drm_type = *(u32*)(pgd_data + 8);
|
|
|
|
// Set the hashing, crypto and open modes.
|
|
if (PGD->drm_type == 1)
|
|
{
|
|
PGD->mac_type = 1;
|
|
flag |= 4;
|
|
|
|
if (PGD->key_index > 1)
|
|
{
|
|
PGD->mac_type = 3;
|
|
flag |= 8;
|
|
}
|
|
PGD->cipher_type = 1;
|
|
}
|
|
else
|
|
{
|
|
PGD->mac_type = 2;
|
|
PGD->cipher_type = 2;
|
|
}
|
|
PGD->open_flag = flag;
|
|
|
|
// Get the fixed DNAS key.
|
|
fkey = NULL;
|
|
if ((flag & 0x2) == 0x2)
|
|
fkey = dnas_key1A90_pgd;
|
|
if ((flag & 0x1) == 0x1)
|
|
fkey = dnas_key1AA0_pgd;
|
|
|
|
if (fkey == NULL)
|
|
{
|
|
printf("PGD: Invalid PGD DNAS flag! %08x\n", flag);
|
|
return -1;
|
|
}
|
|
|
|
// Test MAC hash at 0x80 (DNAS hash).
|
|
sceDrmBBMacInit(&mkey, PGD->mac_type);
|
|
sceDrmBBMacUpdate(&mkey, pgd_data, 0x80);
|
|
result = sceDrmBBMacFinal2(&mkey, pgd_data + 0x80, fkey);
|
|
|
|
if (result)
|
|
{
|
|
printf("PGD: Invalid PGD 0x80 MAC hash!\n");
|
|
return -1;
|
|
}
|
|
|
|
// Test MAC hash at 0x70 (key hash).
|
|
sceDrmBBMacInit(&mkey, PGD->mac_type);
|
|
sceDrmBBMacUpdate(&mkey, pgd_data, 0x70);
|
|
|
|
// If a key was provided, check it against MAC 0x70.
|
|
if (!isEmpty(key, 0x10))
|
|
{
|
|
result = sceDrmBBMacFinal2(&mkey, pgd_data + 0x70, key);
|
|
if (result)
|
|
{
|
|
printf("PGD: Invalid PGD 0x70 MAC hash!\n");
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
memcpy(PGD->vkey, key, 16);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Generate the key from MAC 0x70.
|
|
bbmac_getkey(&mkey, pgd_data + 0x70, PGD->vkey);
|
|
}
|
|
|
|
// Decrypt the PGD header block (0x30 bytes).
|
|
sceDrmBBCipherInit(&ckey, PGD->cipher_type, 2, pgd_data + 0x10, PGD->vkey, 0);
|
|
sceDrmBBCipherUpdate(&ckey, pgd_data + 0x30, 0x30);
|
|
sceDrmBBCipherFinal(&ckey);
|
|
|
|
// Get the decryption parameters from the decrypted header.
|
|
PGD->data_size = *(u32*)(pgd_data + 0x44);
|
|
PGD->block_size = *(u32*)(pgd_data + 0x48);
|
|
PGD->data_offset = *(u32*)(pgd_data + 0x4c);
|
|
|
|
// Additional size variables.
|
|
PGD->align_size = (PGD->data_size + 15) & ~15;
|
|
PGD->table_offset = PGD->data_offset + PGD->align_size;
|
|
PGD->block_nr = (PGD->align_size + PGD->block_size - 1) & ~(PGD->block_size - 1);
|
|
PGD->block_nr = PGD->block_nr / PGD->block_size;
|
|
|
|
if ((PGD->align_size + PGD->block_nr * 16) > pgd_size)
|
|
{
|
|
printf("ERROR: Invalid PGD data size!\n");
|
|
return -1;
|
|
}
|
|
|
|
// Test MAC hash at 0x60 (table hash).
|
|
sceDrmBBMacInit(&mkey, PGD->mac_type);
|
|
sceDrmBBMacUpdate(&mkey, pgd_data + PGD->table_offset, PGD->block_nr * 16);
|
|
result = sceDrmBBMacFinal2(&mkey, pgd_data + 0x60, PGD->vkey);
|
|
|
|
if (result)
|
|
{
|
|
printf("ERROR: Invalid PGD 0x60 MAC hash!\n");
|
|
return -1;
|
|
}
|
|
|
|
// Decrypt the data.
|
|
sceDrmBBCipherInit(&ckey, PGD->cipher_type, 2, pgd_data + 0x30, PGD->vkey, 0);
|
|
sceDrmBBCipherUpdate(&ckey, pgd_data + 0x90, PGD->align_size);
|
|
sceDrmBBCipherFinal(&ckey);
|
|
|
|
return PGD->data_size;
|
|
} |