rpcs3/rpcs3/Crypto/unedat.cpp
2014-08-23 18:51:51 +04:00

701 lines
20 KiB
C++

#include "stdafx.h"
#include "utils.h"
#include "key_vault.h"
#include "unedat.h"
#include "Utilities/Log.h"
void generate_key(int crypto_mode, int version, unsigned char *key_final, unsigned char *iv_final, unsigned char *key, unsigned char *iv)
{
int mode = (int) (crypto_mode & 0xF0000000);
switch (mode) {
case 0x10000000:
// Encrypted ERK.
// Decrypt the key with EDAT_KEY + EDAT_IV and copy the original IV.
aescbc128_decrypt(version ? EDAT_KEY_1 : EDAT_KEY_0, EDAT_IV, key, key_final, 0x10);
memcpy(iv_final, iv, 0x10);
break;
case 0x20000000:
// Default ERK.
// Use EDAT_KEY and EDAT_IV.
memcpy(key_final, version ? EDAT_KEY_1 : EDAT_KEY_0, 0x10);
memcpy(iv_final, EDAT_IV, 0x10);
break;
case 0x00000000:
// Unencrypted ERK.
// Use the original key and iv.
memcpy(key_final, key, 0x10);
memcpy(iv_final, iv, 0x10);
break;
};
}
void generate_hash(int hash_mode, int version, unsigned char *hash_final, unsigned char *hash)
{
int mode = (int) (hash_mode & 0xF0000000);
switch (mode) {
case 0x10000000:
// Encrypted HASH.
// Decrypt the hash with EDAT_KEY + EDAT_IV.
aescbc128_decrypt(version ? EDAT_KEY_1 : EDAT_KEY_0, EDAT_IV, hash, hash_final, 0x10);
break;
case 0x20000000:
// Default HASH.
// Use EDAT_HASH.
memcpy(hash_final, version ? EDAT_HASH_1 : EDAT_HASH_0, 0x10);
break;
case 0x00000000:
// Unencrypted ERK.
// Use the original hash.
memcpy(hash_final, hash, 0x10);
break;
};
}
bool crypto(int hash_mode, int crypto_mode, int version, unsigned char *in, unsigned char *out, int length, unsigned char *key, unsigned char *iv, unsigned char *hash, unsigned char *test_hash)
{
// Setup buffers for key, iv and hash.
unsigned char key_final[0x10] = {};
unsigned char iv_final[0x10] = {};
unsigned char hash_final_10[0x10] = {};
unsigned char hash_final_14[0x14] = {};
// Generate crypto key and hash.
generate_key(crypto_mode, version, key_final, iv_final, key, iv);
if ((hash_mode & 0xFF) == 0x01)
generate_hash(hash_mode, version, hash_final_14, hash);
else
generate_hash(hash_mode, version, hash_final_10, hash);
if ((crypto_mode & 0xFF) == 0x01) // No algorithm.
{
memcpy(out, in, length);
}
else if ((crypto_mode & 0xFF) == 0x02) // AES128-CBC
{
aescbc128_decrypt(key_final, iv_final, in, out, length);
}
else
{
LOG_ERROR(LOADER, "EDAT: Unknown crypto algorithm!\n");
return false;
}
if ((hash_mode & 0xFF) == 0x01) // 0x14 SHA1-HMAC
{
return hmac_hash_compare(hash_final_14, 0x14, in, length, test_hash);
}
else if ((hash_mode & 0xFF) == 0x02) // 0x10 AES-CMAC
{
return cmac_hash_compare(hash_final_10, 0x10, in, length, test_hash);
}
else if ((hash_mode & 0xFF) == 0x04) //0x10 SHA1-HMAC
{
return hmac_hash_compare(hash_final_10, 0x10, in, length, test_hash);
}
else
{
LOG_ERROR(LOADER, "EDAT: Unknown hashing algorithm!\n");
return false;
}
}
unsigned char* dec_section(unsigned char* metadata)
{
unsigned char *dec = new unsigned char[0x10];
dec[0x00] = (metadata[0xC] ^ metadata[0x8] ^ metadata[0x10]);
dec[0x01] = (metadata[0xD] ^ metadata[0x9] ^ metadata[0x11]);
dec[0x02] = (metadata[0xE] ^ metadata[0xA] ^ metadata[0x12]);
dec[0x03] = (metadata[0xF] ^ metadata[0xB] ^ metadata[0x13]);
dec[0x04] = (metadata[0x4] ^ metadata[0x8] ^ metadata[0x14]);
dec[0x05] = (metadata[0x5] ^ metadata[0x9] ^ metadata[0x15]);
dec[0x06] = (metadata[0x6] ^ metadata[0xA] ^ metadata[0x16]);
dec[0x07] = (metadata[0x7] ^ metadata[0xB] ^ metadata[0x17]);
dec[0x08] = (metadata[0xC] ^ metadata[0x0] ^ metadata[0x18]);
dec[0x09] = (metadata[0xD] ^ metadata[0x1] ^ metadata[0x19]);
dec[0x0A] = (metadata[0xE] ^ metadata[0x2] ^ metadata[0x1A]);
dec[0x0B] = (metadata[0xF] ^ metadata[0x3] ^ metadata[0x1B]);
dec[0x0C] = (metadata[0x4] ^ metadata[0x0] ^ metadata[0x1C]);
dec[0x0D] = (metadata[0x5] ^ metadata[0x1] ^ metadata[0x1D]);
dec[0x0E] = (metadata[0x6] ^ metadata[0x2] ^ metadata[0x1E]);
dec[0x0F] = (metadata[0x7] ^ metadata[0x3] ^ metadata[0x1F]);
return dec;
}
unsigned char* get_block_key(int block, NPD_HEADER *npd)
{
unsigned char empty_key[0x10] = {};
unsigned char *src_key = (npd->version <= 1) ? empty_key : npd->dev_hash;
unsigned char *dest_key = new unsigned char[0x10];
memcpy(dest_key, src_key, 0xC);
dest_key[0xC] = (block >> 24 & 0xFF);
dest_key[0xD] = (block >> 16 & 0xFF);
dest_key[0xE] = (block >> 8 & 0xFF);
dest_key[0xF] = (block & 0xFF);
return dest_key;
}
// EDAT/SDAT functions.
int decrypt_data(rFile *in, rFile *out, EDAT_SDAT_HEADER *edat, NPD_HEADER *npd, unsigned char* crypt_key, bool verbose)
{
// Get metadata info and setup buffers.
int block_num = (int) ((edat->file_size + edat->block_size - 1) / edat->block_size);
int metadata_section_size = ((edat->flags & EDAT_COMPRESSED_FLAG) != 0 || (edat->flags & EDAT_FLAG_0x20) != 0) ? 0x20 : 0x10;
int metadata_offset = 0x100;
unsigned char *enc_data;
unsigned char *dec_data;
unsigned char *b_key;
unsigned char *iv;
unsigned char empty_iv[0x10] = {};
// Decrypt the metadata.
for (int i = 0; i < block_num; i++)
{
in->Seek(metadata_offset + i * metadata_section_size);
unsigned char hash_result[0x10];
long offset;
int length = 0;
int compression_end = 0;
if ((edat->flags & EDAT_FLAG_0x04) != 0)
{
LOG_ERROR(LOADER, "EDAT: Flag 0x04 is not yet supported");
return -1;
}
if ((edat->flags & EDAT_COMPRESSED_FLAG) != 0)
{
unsigned char metadata[0x20];
in->Read(metadata, 0x20);
// If the data is compressed, decrypt the metadata.
unsigned char *result = dec_section(metadata);
offset = ((swap32(*(int*)&result[0]) << 4) | (swap32(*(int*)&result[4])));
length = swap32(*(int*)&result[8]);
compression_end = swap32(*(int*)&result[12]);
delete[] result;
memcpy(hash_result, metadata, 0x10);
}
else if ((edat->flags & EDAT_FLAG_0x20) != 0)
{
// If FLAG 0x20, the metadata precedes each data block.
in->Seek(metadata_offset + i * metadata_section_size + length);
unsigned char metadata[0x20];
in->Read(metadata, 0x20);
// If FLAG 0x20 is set, apply custom xor.
for (int j = 0; j < 0x10; j++) {
hash_result[j] = (unsigned char)(metadata[j] ^ metadata[j+0x10]);
}
offset = metadata_offset + i * edat->block_size + (i + 1) * metadata_section_size;
length = edat->block_size;
if ((i == (block_num - 1)) && (edat->file_size % edat->block_size))
length = (int) (edat->file_size % edat->block_size);
}
else
{
in->Read(hash_result, 0x10);
offset = metadata_offset + i * edat->block_size + block_num * metadata_section_size;
length = edat->block_size;
if ((i == (block_num - 1)) && (edat->file_size % edat->block_size))
length = (int) (edat->file_size % edat->block_size);
}
// Locate the real data.
int pad_length = length;
length = (int) ((pad_length + 0xF) & 0xFFFFFFF0);
in->Seek(offset);
// Setup buffers for decryption and read the data.
enc_data = new unsigned char[length];
dec_data = new unsigned char[length];
unsigned char key_result[0x10];
unsigned char hash[0x10];
in->Read(enc_data, length);
// Generate a key for the current block.
b_key = get_block_key(i, npd);
// Encrypt the block key with the crypto key.
aesecb128_encrypt(crypt_key, b_key, key_result);
if ((edat->flags & EDAT_FLAG_0x10) != 0)
aesecb128_encrypt(crypt_key, key_result, hash); // If FLAG 0x10 is set, encrypt again to get the final hash.
else
memcpy(hash, key_result, 0x10);
// Setup the crypto and hashing mode based on the extra flags.
int crypto_mode = ((edat->flags & EDAT_FLAG_0x02) == 0) ? 0x2 : 0x1;
int hash_mode;
if ((edat->flags & EDAT_FLAG_0x10) == 0)
hash_mode = 0x02;
else if ((edat->flags & EDAT_FLAG_0x20) == 0)
hash_mode = 0x04;
else
hash_mode = 0x01;
if ((edat->flags & EDAT_ENCRYPTED_KEY_FLAG) != 0)
{
crypto_mode |= 0x10000000;
hash_mode |= 0x10000000;
}
if ((edat->flags & EDAT_DEBUG_DATA_FLAG) != 0)
{
// Reset the flags.
crypto_mode |= 0x01000000;
hash_mode |= 0x01000000;
// Simply copy the data without the header or the footer.
memcpy(dec_data, enc_data, length);
}
else
{
// IV is null if NPD version is 1 or 0.
iv = (npd->version <= 1) ? empty_iv : npd->digest;
// Call main crypto routine on this data block.
crypto(hash_mode, crypto_mode, (npd->version == 4), enc_data, dec_data, length, key_result, iv, hash, hash_result);
}
// Apply additional compression if needed and write the decrypted data.
if (((edat->flags & EDAT_COMPRESSED_FLAG) != 0) && compression_end)
{
int decomp_size = (int)edat->file_size;
unsigned char *decomp_data = new unsigned char[decomp_size];
memset(decomp_data, 0, decomp_size);
if (verbose)
LOG_NOTICE(LOADER, "EDAT: Decompressing...\n");
int res = lz_decompress(decomp_data, dec_data, decomp_size);
out->Write(decomp_data, res);
if (verbose)
{
LOG_NOTICE(LOADER, "EDAT: Compressed block size: %d\n", pad_length);
LOG_NOTICE(LOADER, "EDAT: Decompressed block size: %d\n", res);
}
edat->file_size -= res;
if (edat->file_size == 0)
{
if (res < 0)
{
LOG_ERROR(LOADER, "EDAT: Decompression failed!\n");
return 1;
}
else
LOG_SUCCESS(LOADER, "EDAT: Data successfully decompressed!\n");
}
delete[] decomp_data;
}
else
{
out->Write(dec_data, pad_length);
}
delete[] enc_data;
delete[] dec_data;
}
return 0;
}
static bool check_flags(EDAT_SDAT_HEADER *edat, NPD_HEADER *npd)
{
if (edat == nullptr || npd == nullptr)
return false;
if (npd->version == 0 || npd->version == 1)
{
if (edat->flags & 0x7EFFFFFE)
{
LOG_ERROR(LOADER, "EDAT: Bad header flags!\n");
return false;
}
}
else if (npd->version == 2)
{
if (edat->flags & 0x7EFFFFE0)
{
LOG_ERROR(LOADER, "EDAT: Bad header flags!\n");
return false;
}
}
else if (npd->version == 3 || npd->version == 4)
{
if (edat->flags & 0x7EFFFFC0)
{
LOG_ERROR(LOADER, "EDAT: Bad header flags!\n");
return false;
}
}
else if (npd->version > 4)
{
LOG_ERROR(LOADER, "EDAT: Unknown version - %d\n", npd->version);
return false;
}
return true;
}
int check_data(unsigned char *key, EDAT_SDAT_HEADER *edat, NPD_HEADER *npd, rFile *f, bool verbose)
{
f->Seek(0);
unsigned char *header = new unsigned char[0xA0];
unsigned char *tmp = new unsigned char[0xA0];
unsigned char *hash_result = new unsigned char[0x10];
// Check NPD version and EDAT flags.
if (!check_flags(edat, npd))
{
delete[] header;
delete[] tmp;
delete[] hash_result;
return 1;
}
// Read in the file header.
f->Read(header, 0xA0);
f->Read(hash_result, 0x10);
// Setup the hashing mode and the crypto mode used in the file.
int crypto_mode = 0x1;
int hash_mode = ((edat->flags & EDAT_ENCRYPTED_KEY_FLAG) == 0) ? 0x00000002 : 0x10000002;
if ((edat->flags & EDAT_DEBUG_DATA_FLAG) != 0)
{
LOG_ERROR(LOADER, "EDAT: DEBUG data detected!\n");
hash_mode |= 0x01000000;
}
// Setup header key and iv buffers.
unsigned char header_key[0x10] = {};
unsigned char header_iv[0x10] = {};
// Test the header hash (located at offset 0xA0).
if (!crypto(hash_mode, crypto_mode, (npd->version == 4), header, tmp, 0xA0, header_key, header_iv, key, hash_result))
{
if (verbose)
LOG_WARNING(LOADER, "EDAT: Header hash is invalid!\n");
}
// Parse the metadata info.
int metadata_section_size = 0x10;
if (((edat->flags & EDAT_COMPRESSED_FLAG) != 0))
{
LOG_WARNING(LOADER, "EDAT: COMPRESSED data detected!\n");
metadata_section_size = 0x20;
}
int block_num = (int) ((edat->file_size + edat->block_size - 1) / edat->block_size);
int bytes_read = 0;
int metadata_offset = 0x100;
long bytes_to_read = metadata_section_size * block_num;
while (bytes_to_read > 0)
{
// Locate the metadata blocks.
int block_size = (0x3C00 > bytes_to_read) ? (int) bytes_to_read : 0x3C00; // 0x3C00 is the maximum block size.
f->Seek(metadata_offset + bytes_read);
unsigned char *data = new unsigned char[block_size];
// Read in the metadata.
tmp = new unsigned char[block_size];
f->Read(data, block_size);
// Check the generated hash against the metadata hash located at offset 0x90 in the header.
memset(hash_result, 0, 0x10);
f->Seek(0x90);
f->Read(hash_result, 0x10);
// Generate the hash for this block.
if (!crypto(hash_mode, crypto_mode, (npd->version == 4), data, tmp, block_size, header_key, header_iv, key, hash_result))
{
if (verbose)
LOG_WARNING(LOADER, "EDAT: Metadata hash from block 0x%08x is invalid!\n", metadata_offset + bytes_read);
}
// Adjust sizes.
bytes_read += block_size;
bytes_to_read -= block_size;
delete[] data;
}
// Cleanup.
delete[] header;
delete[] tmp;
delete[] hash_result;
return 0;
}
void validate_data(const char* file_name, unsigned char *klicensee, NPD_HEADER *npd, bool verbose)
{
int title_hash_result = 0;
int dev_hash_result = 0;
int file_name_length = strlen(file_name);
unsigned char *buf = new unsigned char[0x30 + file_name_length];
unsigned char key[0x10];
// Build the buffer (content_id + file_name).
memcpy(buf, npd->content_id, 0x30);
memcpy(buf + 0x30, file_name, file_name_length);
// Hash with NP_OMAC_KEY_3 and compare with title_hash.
title_hash_result = cmac_hash_compare(NP_OMAC_KEY_3, 0x10, buf, 0x30 + file_name_length, npd->title_hash);
if (verbose)
{
if (title_hash_result)
LOG_SUCCESS(LOADER, "EDAT: NPD title hash is valid!\n");
else
LOG_WARNING(LOADER, "EDAT: NPD title hash is invalid!\n");
}
// Check for an empty dev_hash (can't validate if devklic is NULL);
bool isDevklicEmpty = true;
for (int i = 0; i < 0x10; i++)
{
if (klicensee[i] != 0)
{
isDevklicEmpty = false;
break;
}
}
if (isDevklicEmpty)
{
if (verbose)
LOG_WARNING(LOADER, "EDAT: NPD dev hash is empty!\n");
}
else
{
// Generate klicensee xor key.
xor_(key, klicensee, NP_OMAC_KEY_2, 0x10);
// Hash with generated key and compare with dev_hash.
dev_hash_result = cmac_hash_compare(key, 0x10, (unsigned char *)npd, 0x60, npd->dev_hash);
if (verbose)
{
if (dev_hash_result)
LOG_SUCCESS(LOADER, "EDAT: NPD dev hash is valid!\n");
else
LOG_WARNING(LOADER, "EDAT: NPD dev hash is invalid!\n");
}
}
delete[] buf;
}
bool extract_data(rFile *input, rFile *output, const char* input_file_name, unsigned char* devklic, unsigned char* rifkey, bool verbose)
{
// Setup NPD and EDAT/SDAT structs.
NPD_HEADER *NPD = new NPD_HEADER();
EDAT_SDAT_HEADER *EDAT = new EDAT_SDAT_HEADER();
// Read in the NPD and EDAT/SDAT headers.
char npd_header[0x80];
char edat_header[0x10];
input->Read(npd_header, 0x80);
input->Read(edat_header, 0x10);
memcpy(NPD->magic, npd_header, 4);
NPD->version = swap32(*(int*)&npd_header[4]);
NPD->license = swap32(*(int*)&npd_header[8]);
NPD->type = swap32(*(int*)&npd_header[12]);
memcpy(NPD->content_id, (unsigned char*)&npd_header[16], 0x30);
memcpy(NPD->digest, (unsigned char*)&npd_header[64], 0x10);
memcpy(NPD->title_hash, (unsigned char*)&npd_header[80], 0x10);
memcpy(NPD->dev_hash, (unsigned char*)&npd_header[96], 0x10);
NPD->unk1 = swap64(*(u64*)&npd_header[112]);
NPD->unk2 = swap64(*(u64*)&npd_header[120]);
unsigned char npd_magic[4] = {0x4E, 0x50, 0x44, 0x00}; //NPD0
if(memcmp(NPD->magic, npd_magic, 4))
{
LOG_ERROR(LOADER, "EDAT: %s has invalid NPD header or already decrypted.", input_file_name);
delete NPD;
delete EDAT;
return 1;
}
EDAT->flags = swap32(*(int*)&edat_header[0]);
EDAT->block_size = swap32(*(int*)&edat_header[4]);
EDAT->file_size = swap64(*(u64*)&edat_header[8]);
if (verbose)
{
LOG_NOTICE(LOADER, "NPD HEADER\n");
LOG_NOTICE(LOADER, "NPD version: %d\n", NPD->version);
LOG_NOTICE(LOADER, "NPD license: %d\n", NPD->license);
LOG_NOTICE(LOADER, "NPD type: %d\n", NPD->type);
LOG_NOTICE(LOADER, "\n");
LOG_NOTICE(LOADER, "EDAT HEADER\n");
LOG_NOTICE(LOADER, "EDAT flags: 0x%08X\n", EDAT->flags);
LOG_NOTICE(LOADER, "EDAT block size: 0x%08X\n", EDAT->block_size);
LOG_NOTICE(LOADER, "EDAT file size: 0x%08X\n", EDAT->file_size);
LOG_NOTICE(LOADER, "\n");
}
// Set decryption key.
unsigned char key[0x10];
memset(key, 0, 0x10);
if((EDAT->flags & SDAT_FLAG) == SDAT_FLAG)
{
LOG_WARNING(LOADER, "EDAT: SDAT detected!\n");
xor_(key, NPD->dev_hash, SDAT_KEY, 0x10);
}
else
{
// Perform header validation (optional step).
validate_data(input_file_name, devklic, NPD, verbose);
if ((NPD->license & 0x3) == 0x3) // Type 3: Use supplied devklic.
memcpy(key, devklic, 0x10);
else if ((NPD->license & 0x2) == 0x2) // Type 2: Use key from RAP file (RIF key).
{
memcpy(key, rifkey, 0x10);
// Make sure we don't have an empty RIF key.
int i, test = 0;
for(i = 0; i < 0x10; i++)
{
if (key[i] != 0)
{
test = 1;
break;
}
}
if (!test)
{
LOG_ERROR(LOADER, "EDAT: A valid RAP file is needed!");
delete NPD;
delete EDAT;
return 1;
}
}
}
LOG_NOTICE(LOADER, "EDAT: Parsing data...\n");
if (check_data(key, EDAT, NPD, input, verbose))
LOG_ERROR(LOADER, "EDAT: Data parsing failed!\n");
else
LOG_SUCCESS(LOADER, "EDAT: Data successfully parsed!\n");
printf("\n");
LOG_NOTICE(LOADER, "EDAT: Decrypting data...\n");
if (decrypt_data(input, output, EDAT, NPD, key, verbose))
LOG_ERROR(LOADER, "EDAT: Data decryption failed!");
else
LOG_SUCCESS(LOADER, "EDAT: Data successfully decrypted!");
delete NPD;
delete EDAT;
return 0;
}
int DecryptEDAT(const std::string& input_file_name, const std::string& output_file_name, int mode, const std::string& rap_file_name, unsigned char *custom_klic, bool verbose)
{
// Prepare the files.
rFile input(input_file_name.c_str());
rFile output(output_file_name.c_str(), rFile::write);
rFile rap(rap_file_name.c_str());
// Set keys (RIF and DEVKLIC).
unsigned char rifkey[0x10];
unsigned char devklic[0x10];
memset(rifkey, 0, 0x10);
memset(devklic, 0, 0x10);
// Select the EDAT key mode.
switch (mode)
{
case 0:
break;
case 1:
memcpy(devklic, NP_KLIC_FREE, 0x10);
break;
case 2:
memcpy(devklic, NP_OMAC_KEY_2, 0x10);
break;
case 3:
memcpy(devklic, NP_OMAC_KEY_3, 0x10);
break;
case 4:
memcpy(devklic, NP_KLIC_KEY, 0x10);
break;
case 5:
memcpy(devklic, NP_PSX_KEY, 0x10);
break;
case 6:
memcpy(devklic, NP_PSP_KEY_1, 0x10);
break;
case 7:
memcpy(devklic, NP_PSP_KEY_2, 0x10);
break;
case 8:
{
if (custom_klic != NULL)
memcpy(devklic, custom_klic, 0x10);
else
{
LOG_ERROR(LOADER, "EDAT: Invalid custom klic!\n");
return -1;
}
break;
}
default:
LOG_ERROR(LOADER, "EDAT: Invalid mode!\n");
return -1;
}
// Check the input/output files.
if (!input.IsOpened() || !output.IsOpened())
{
LOG_ERROR(LOADER, "EDAT: Failed to open files!\n");
return -1;
}
// Read the RAP file, if provided.
if (rap.IsOpened())
{
unsigned char rapkey[0x10];
memset(rapkey, 0, 0x10);
rap.Read(rapkey, 0x10);
rap_to_rif(rapkey, rifkey);
rap.Close();
}
// Delete the bad output file if any errors arise.
if (extract_data(&input, &output, input_file_name.c_str(), devklic, rifkey, verbose))
{
input.Close();
output.Close();
rRemoveFile(output_file_name);
return 0;
}
// Cleanup.
input.Close();
output.Close();
return 0;
}