mirror of
https://github.com/naehrwert/scetool.git
synced 2025-04-19 18:06:50 +00:00
261 lines
6.4 KiB
C++
261 lines
6.4 KiB
C++
![]() |
/*
|
||
|
* Copyright (c) 2011-2013 by naehrwert
|
||
|
* Copyright (c) 2012 by flatz
|
||
|
* This file is released under the GPLv2.
|
||
|
*/
|
||
|
|
||
|
#include <stdlib.h>
|
||
|
|
||
|
#include "types.h"
|
||
|
#include "config.h"
|
||
|
#include "np.h"
|
||
|
#include "self.h"
|
||
|
#include "sce.h"
|
||
|
#include "sce_inlines.h"
|
||
|
#include "aes_omac.h"
|
||
|
#include "sha1.h"
|
||
|
#include "ecdsa.h"
|
||
|
#include "keys.h"
|
||
|
#include "aes.h"
|
||
|
#include "util.h"
|
||
|
|
||
|
/*! klicensee key. */
|
||
|
static u8 *_klicensee_key;
|
||
|
|
||
|
static ci_data_npdrm_t *_sce_find_ci_npdrm(sce_buffer_ctxt_t *ctxt)
|
||
|
{
|
||
|
if(ctxt->self.cis != NULL)
|
||
|
{
|
||
|
LIST_FOREACH(iter, ctxt->self.cis)
|
||
|
{
|
||
|
control_info_t *ci = (control_info_t *)iter->value;
|
||
|
|
||
|
if(ci->type == CONTROL_INFO_TYPE_NPDRM)
|
||
|
{
|
||
|
ci_data_npdrm_t *np = (ci_data_npdrm_t *)((u8 *)ci + sizeof(control_info_t));
|
||
|
//Fixup.
|
||
|
_es_ci_data_npdrm(np);
|
||
|
return np;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
void np_set_klicensee(u8 *klicensee)
|
||
|
{
|
||
|
_klicensee_key = klicensee;
|
||
|
}
|
||
|
|
||
|
BOOL np_decrypt_npdrm(sce_buffer_ctxt_t *ctxt)
|
||
|
{
|
||
|
aes_context aes_ctxt;
|
||
|
keyset_t *ks_np_klic_free, *ks_klic_key;
|
||
|
u8 npdrm_key[0x10];
|
||
|
u8 npdrm_iv[0x10];
|
||
|
ci_data_npdrm_t *np;
|
||
|
|
||
|
if((np = _sce_find_ci_npdrm(ctxt)) == NULL)
|
||
|
return FALSE;
|
||
|
|
||
|
//Try to find keysets.
|
||
|
ks_klic_key = keyset_find_by_name(CONFIG_NP_KLIC_KEY_KNAME);
|
||
|
if(ks_klic_key == NULL)
|
||
|
return FALSE;
|
||
|
if(_klicensee_key != NULL)
|
||
|
memcpy(npdrm_key, _klicensee_key, 0x10);
|
||
|
else if(np->license_type == NP_LICENSE_FREE)
|
||
|
{
|
||
|
ks_np_klic_free = keyset_find_by_name(CONFIG_NP_KLIC_FREE_KNAME);
|
||
|
if(ks_np_klic_free == NULL)
|
||
|
return FALSE;
|
||
|
memcpy(npdrm_key, ks_np_klic_free->erk, 0x10);
|
||
|
}
|
||
|
else if(np->license_type == NP_LICENSE_LOCAL)
|
||
|
{
|
||
|
if ((klicensee_by_content_id((s8 *)np->content_id, npdrm_key)) == FALSE)
|
||
|
return FALSE;
|
||
|
}
|
||
|
else
|
||
|
return FALSE;
|
||
|
|
||
|
aes_setkey_dec(&aes_ctxt, ks_klic_key->erk, METADATA_INFO_KEYBITS);
|
||
|
aes_crypt_ecb(&aes_ctxt, AES_DECRYPT, npdrm_key, npdrm_key);
|
||
|
|
||
|
memset(npdrm_iv, 0, 0x10);
|
||
|
aes_setkey_dec(&aes_ctxt, npdrm_key, METADATA_INFO_KEYBITS);
|
||
|
aes_crypt_cbc(&aes_ctxt, AES_DECRYPT, sizeof(metadata_info_t), npdrm_iv, (u8 *)ctxt->metai, (u8 *)ctxt->metai);
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
BOOL np_encrypt_npdrm(sce_buffer_ctxt_t *ctxt)
|
||
|
{
|
||
|
aes_context aes_ctxt;
|
||
|
keyset_t *ks_np_klic_free, *ks_klic_key;
|
||
|
u8 npdrm_key[0x10];
|
||
|
u8 npdrm_iv[0x10];
|
||
|
ci_data_npdrm_t *np;
|
||
|
|
||
|
if((np = _sce_find_ci_npdrm(ctxt)) == NULL)
|
||
|
return FALSE;
|
||
|
|
||
|
//Try to find keysets.
|
||
|
ks_klic_key = keyset_find_by_name(CONFIG_NP_KLIC_KEY_KNAME);
|
||
|
if(ks_klic_key == NULL)
|
||
|
return FALSE;
|
||
|
if(_klicensee_key != NULL)
|
||
|
memcpy(npdrm_key, _klicensee_key, 0x10);
|
||
|
else if(np->license_type == NP_LICENSE_FREE)
|
||
|
{
|
||
|
ks_np_klic_free = keyset_find_by_name(CONFIG_NP_KLIC_FREE_KNAME);
|
||
|
if(ks_np_klic_free == NULL)
|
||
|
return FALSE;
|
||
|
memcpy(npdrm_key, ks_np_klic_free->erk, 0x10);
|
||
|
}
|
||
|
else if(np->license_type == NP_LICENSE_LOCAL)
|
||
|
{
|
||
|
if ((klicensee_by_content_id((s8 *)np->content_id, npdrm_key)) == FALSE)
|
||
|
return FALSE;
|
||
|
}
|
||
|
else
|
||
|
return FALSE;
|
||
|
|
||
|
aes_setkey_dec(&aes_ctxt, ks_klic_key->erk, METADATA_INFO_KEYBITS);
|
||
|
aes_crypt_ecb(&aes_ctxt, AES_DECRYPT, npdrm_key, npdrm_key);
|
||
|
|
||
|
memset(npdrm_iv, 0, 0x10);
|
||
|
aes_setkey_enc(&aes_ctxt, npdrm_key, METADATA_INFO_KEYBITS);
|
||
|
aes_crypt_cbc(&aes_ctxt, AES_ENCRYPT, sizeof(metadata_info_t), npdrm_iv, ctxt->scebuffer + ctxt->off_metai, ctxt->scebuffer + ctxt->off_metai);
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
BOOL np_create_ci(npdrm_config_t *npconf, ci_data_npdrm_t *cinp)
|
||
|
{
|
||
|
u32 i, len;
|
||
|
u8 *cid_fname, ci_key[0x10];
|
||
|
keyset_t *ks_np_tid, *ks_np_ci, *ks_np_klic_free;
|
||
|
u8 npdrm_key[0x10];
|
||
|
|
||
|
//Try to find keysets.
|
||
|
ks_np_tid = keyset_find_by_name(CONFIG_NP_TID_KNAME);
|
||
|
ks_np_ci = keyset_find_by_name(CONFIG_NP_CI_KNAME);
|
||
|
if(ks_np_tid == NULL || ks_np_ci == NULL)
|
||
|
return FALSE;
|
||
|
|
||
|
//Can only create NPDRM SELF with local and free license.
|
||
|
if(_klicensee_key != NULL)
|
||
|
memcpy(npdrm_key, _klicensee_key, 0x10);
|
||
|
else if(npconf->license_type == NP_LICENSE_FREE)
|
||
|
{
|
||
|
ks_np_klic_free = keyset_find_by_name(CONFIG_NP_KLIC_FREE_KNAME);
|
||
|
if(ks_np_klic_free == NULL)
|
||
|
return FALSE;
|
||
|
memcpy(npdrm_key, ks_np_klic_free->erk, 0x10);
|
||
|
}
|
||
|
else if(npconf->license_type == NP_LICENSE_LOCAL)
|
||
|
{
|
||
|
if ((klicensee_by_content_id((s8 *)npconf->content_id, npdrm_key)) == FALSE)
|
||
|
return FALSE;
|
||
|
}
|
||
|
else
|
||
|
return FALSE;
|
||
|
|
||
|
cinp->magic = NP_CI_MAGIC;
|
||
|
cinp->unknown_0 = 1;
|
||
|
cinp->license_type = npconf->license_type;
|
||
|
cinp->app_type = npconf->app_type;
|
||
|
memcpy(cinp->content_id, npconf->content_id, 0x30);
|
||
|
#ifdef CONFIG_PRIVATE_BUILD
|
||
|
_fill_rand_bytes(cinp->rndpad, 0x10);
|
||
|
#else
|
||
|
//Better than boring random bytes!
|
||
|
memcpy(cinp->rndpad, CONFIG_NPDRM_WATERMARK, 0x10);
|
||
|
#endif
|
||
|
cinp->unknown_1 = 0;
|
||
|
cinp->unknown_2 = 0;
|
||
|
|
||
|
//Fixup before hashing.
|
||
|
_es_ci_data_npdrm(cinp);
|
||
|
|
||
|
//Generate control info hash key.
|
||
|
for(i = 0; i < 0x10; i++)
|
||
|
ci_key[i] = ks_np_ci->erk[i] ^ npdrm_key[i];
|
||
|
|
||
|
//Create hash of title id and real filename.
|
||
|
len = strlen(npconf->real_fname) + 0x30;
|
||
|
cid_fname = (u8 *)malloc(sizeof(u8) * (len + 1));
|
||
|
memcpy(cid_fname, cinp->content_id, 0x30);
|
||
|
strcpy((s8 *)(cid_fname + 0x30), npconf->real_fname);
|
||
|
aes_omac1(cinp->hash_cid_fname, cid_fname, len, ks_np_tid->erk, KEYBITS(0x10));
|
||
|
|
||
|
//Create control info hash.
|
||
|
aes_omac1(cinp->hash_ci, (u8 *)cinp, 0x60 /* Only the first 0x60 bytes are hashed. */ , ci_key, KEYBITS(0x10));
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
//TODO: The fwrite/fread error checking was broken.
|
||
|
//Maybe the MS runtime is returning the number of bytes written instead of the element count?
|
||
|
BOOL np_sign_file(s8 *fname)
|
||
|
{
|
||
|
u8 padding_data[0x10] =
|
||
|
{
|
||
|
0xbc, 0x3f, 0x7a, 0x48, 0xaf, 0x45, 0xef, 0x28, 0x3a, 0x05, 0x98, 0x10, 0xbc, 0x3f, 0x7a, 0x48
|
||
|
};
|
||
|
|
||
|
keyset_t *ks;
|
||
|
FILE *fp = NULL;
|
||
|
u8 *buffer = NULL;
|
||
|
u32 length;
|
||
|
u32 padding;
|
||
|
u8 hash[0x14], R[0x15], S[0x15];
|
||
|
|
||
|
//Try to find keyset.
|
||
|
if((ks = keyset_find_by_name(CONFIG_NP_SIG_KNAME)) == NULL)
|
||
|
return FALSE;
|
||
|
|
||
|
if((fp = fopen(fname, "r+b")) == NULL)
|
||
|
return FALSE;
|
||
|
|
||
|
fseek(fp, 0, SEEK_END);
|
||
|
length = ftell(fp);
|
||
|
|
||
|
padding = length % 0x10;
|
||
|
if(padding > 0)
|
||
|
{
|
||
|
fwrite(padding_data, sizeof(u8), padding, fp);
|
||
|
length += padding;
|
||
|
}
|
||
|
|
||
|
fseek(fp, 0, SEEK_SET);
|
||
|
if((buffer = (u8 *)malloc(length)) == NULL)
|
||
|
{
|
||
|
fclose(fp);
|
||
|
return FALSE;
|
||
|
}
|
||
|
fread(buffer, sizeof(u8), length, fp);
|
||
|
|
||
|
//Generate header hash.
|
||
|
sha1(buffer, length, hash);
|
||
|
|
||
|
//Generate signature.
|
||
|
/* TODO: Set the right curve and private key */
|
||
|
ecdsa_set_curve(ks->ctype | USE_VSH_CURVE);
|
||
|
ecdsa_set_pub(ks->pub);
|
||
|
ecdsa_set_priv(ks->priv);
|
||
|
ecdsa_sign(hash, R, S);
|
||
|
fseek(fp, 0, SEEK_END);
|
||
|
fwrite(R + 1, 0x14, 1, fp);
|
||
|
fwrite(S + 1, 0x14, 1, fp);
|
||
|
/* Let's be as stupid as sony here... */
|
||
|
fwrite(hash + 0xC, 8, 1, fp);
|
||
|
|
||
|
free(buffer);
|
||
|
fclose(fp);
|
||
|
|
||
|
return TRUE;
|
||
|
}
|