Updated the Crypto Engine:

- Fixed several bugs in unedat;
- Improved EDAT/SDAT file decryption.
This commit is contained in:
Hykem 2014-10-01 14:57:44 +01:00
parent eada1fe12c
commit 6d6c1a9672
16 changed files with 1190 additions and 295 deletions

View File

@ -0,0 +1,6 @@
# Note: This folder has to exist. Once the User Account manager is implemented, make sure it creates this folder in case it's missing and delete this .gitignore file.
# Ignore everything in this directory
*
# Except this file
!.gitignore

548
rpcs3/Crypto/ec.cpp Normal file
View File

@ -0,0 +1,548 @@
// Copyright 2007,2008,2010 Segher Boessenkool <segher@kernel.crashing.org>
// Licensed under the terms of the GNU GPL, version 2
// http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
#include "stdafx.h"
#include "utils.h"
void bn_print(char *name, u8 *a, u32 n)
{
u32 i;
printf("%s = ", name);
for (i = 0; i < n; i++)
printf("%02x", a[i]);
printf("\n");
}
static void bn_zero(u8 *d, u32 n)
{
memset(d, 0, n);
}
void bn_copy(u8 *d, u8 *a, u32 n)
{
memcpy(d, a, n);
}
int bn_compare(u8 *a, u8 *b, u32 n)
{
u32 i;
for (i = 0; i < n; i++) {
if (a[i] < b[i])
return -1;
if (a[i] > b[i])
return 1;
}
return 0;
}
static u8 bn_add_1(u8 *d, u8 *a, u8 *b, u32 n)
{
u32 i;
u32 dig;
u8 c;
c = 0;
for (i = n - 1; i < n; i--) {
dig = a[i] + b[i] + c;
c = dig >> 8;
d[i] = dig;
}
return c;
}
static u8 bn_sub_1(u8 *d, u8 *a, u8 *b, u32 n)
{
u32 i;
u32 dig;
u8 c;
c = 1;
for (i = n - 1; i < n; i--) {
dig = a[i] + 255 - b[i] + c;
c = dig >> 8;
d[i] = dig;
}
return 1 - c;
}
void bn_reduce(u8 *d, u8 *N, u32 n)
{
if (bn_compare(d, N, n) >= 0)
bn_sub_1(d, d, N, n);
}
void bn_add(u8 *d, u8 *a, u8 *b, u8 *N, u32 n)
{
if (bn_add_1(d, a, b, n))
bn_sub_1(d, d, N, n);
bn_reduce(d, N, n);
}
void bn_sub(u8 *d, u8 *a, u8 *b, u8 *N, u32 n)
{
if (bn_sub_1(d, a, b, n))
bn_add_1(d, d, N, n);
}
static const u8 inv256[0x80] = {
0x01, 0xab, 0xcd, 0xb7, 0x39, 0xa3, 0xc5, 0xef,
0xf1, 0x1b, 0x3d, 0xa7, 0x29, 0x13, 0x35, 0xdf,
0xe1, 0x8b, 0xad, 0x97, 0x19, 0x83, 0xa5, 0xcf,
0xd1, 0xfb, 0x1d, 0x87, 0x09, 0xf3, 0x15, 0xbf,
0xc1, 0x6b, 0x8d, 0x77, 0xf9, 0x63, 0x85, 0xaf,
0xb1, 0xdb, 0xfd, 0x67, 0xe9, 0xd3, 0xf5, 0x9f,
0xa1, 0x4b, 0x6d, 0x57, 0xd9, 0x43, 0x65, 0x8f,
0x91, 0xbb, 0xdd, 0x47, 0xc9, 0xb3, 0xd5, 0x7f,
0x81, 0x2b, 0x4d, 0x37, 0xb9, 0x23, 0x45, 0x6f,
0x71, 0x9b, 0xbd, 0x27, 0xa9, 0x93, 0xb5, 0x5f,
0x61, 0x0b, 0x2d, 0x17, 0x99, 0x03, 0x25, 0x4f,
0x51, 0x7b, 0x9d, 0x07, 0x89, 0x73, 0x95, 0x3f,
0x41, 0xeb, 0x0d, 0xf7, 0x79, 0xe3, 0x05, 0x2f,
0x31, 0x5b, 0x7d, 0xe7, 0x69, 0x53, 0x75, 0x1f,
0x21, 0xcb, 0xed, 0xd7, 0x59, 0xc3, 0xe5, 0x0f,
0x11, 0x3b, 0x5d, 0xc7, 0x49, 0x33, 0x55, 0xff,
};
static void bn_mon_muladd_dig(u8 *d, u8 *a, u8 b, u8 *N, u32 n)
{
u32 dig;
u32 i;
u8 z = -(d[n-1] + a[n-1]*b) * inv256[N[n-1]/2];
dig = d[n-1] + a[n-1]*b + N[n-1]*z;
dig >>= 8;
for (i = n - 2; i < n; i--) {
dig += d[i] + a[i]*b + N[i]*z;
d[i+1] = dig;
dig >>= 8;
}
d[0] = dig;
dig >>= 8;
if (dig)
bn_sub_1(d, d, N, n);
bn_reduce(d, N, n);
}
void bn_mon_mul(u8 *d, u8 *a, u8 *b, u8 *N, u32 n)
{
u8 t[512];
u32 i;
bn_zero(t, n);
for (i = n - 1; i < n; i--)
bn_mon_muladd_dig(t, a, b[i], N, n);
bn_copy(d, t, n);
}
void bn_to_mon(u8 *d, u8 *N, u32 n)
{
u32 i;
for (i = 0; i < 8*n; i++)
bn_add(d, d, d, N, n);
}
void bn_from_mon(u8 *d, u8 *N, u32 n)
{
u8 t[512];
bn_zero(t, n);
t[n-1] = 1;
bn_mon_mul(d, d, t, N, n);
}
static void bn_mon_exp(u8 *d, u8 *a, u8 *N, u32 n, u8 *e, u32 en)
{
u8 t[512];
u32 i;
u8 mask;
bn_zero(d, n);
d[n-1] = 1;
bn_to_mon(d, N, n);
for (i = 0; i < en; i++)
for (mask = 0x80; mask != 0; mask >>= 1) {
bn_mon_mul(t, d, d, N, n);
if ((e[i] & mask) != 0)
bn_mon_mul(d, t, a, N, n);
else
bn_copy(d, t, n);
}
}
void bn_mon_inv(u8 *d, u8 *a, u8 *N, u32 n)
{
u8 t[512], s[512];
bn_zero(s, n);
s[n-1] = 2;
bn_sub_1(t, N, s, n);
bn_mon_exp(d, a, N, n, t, n);
}
void bn_copy(u8 *d, u8 *a, u32 n);
int bn_compare(u8 *a, u8 *b, u32 n);
void bn_reduce(u8 *d, u8 *N, u32 n);
void bn_add(u8 *d, u8 *a, u8 *b, u8 *N, u32 n);
void bn_sub(u8 *d, u8 *a, u8 *b, u8 *N, u32 n);
void bn_to_mon(u8 *d, u8 *N, u32 n);
void bn_from_mon(u8 *d, u8 *N, u32 n);
void bn_mon_mul(u8 *d, u8 *a, u8 *b, u8 *N, u32 n);
void bn_mon_inv(u8 *d, u8 *a, u8 *N, u32 n);
struct point {
u8 x[20];
u8 y[20];
};
static u8 ec_p[20];
static u8 ec_a[20]; // mon
static u8 ec_b[20]; // mon
static u8 ec_N[21];
static struct point ec_G; // mon
static struct point ec_Q; // mon
static u8 ec_k[21];
static void elt_copy(u8 *d, u8 *a)
{
memcpy(d, a, 20);
}
static void elt_zero(u8 *d)
{
memset(d, 0, 20);
}
static int elt_is_zero(u8 *d)
{
u32 i;
for (i = 0; i < 20; i++)
if (d[i] != 0)
return 0;
return 1;
}
static void elt_add(u8 *d, u8 *a, u8 *b)
{
bn_add(d, a, b, ec_p, 20);
}
static void elt_sub(u8 *d, u8 *a, u8 *b)
{
bn_sub(d, a, b, ec_p, 20);
}
static void elt_mul(u8 *d, u8 *a, u8 *b)
{
bn_mon_mul(d, a, b, ec_p, 20);
}
static void elt_square(u8 *d, u8 *a)
{
elt_mul(d, a, a);
}
static void elt_inv(u8 *d, u8 *a)
{
u8 s[20];
elt_copy(s, a);
bn_mon_inv(d, s, ec_p, 20);
}
static void point_to_mon(struct point *p)
{
bn_to_mon(p->x, ec_p, 20);
bn_to_mon(p->y, ec_p, 20);
}
static void point_from_mon(struct point *p)
{
bn_from_mon(p->x, ec_p, 20);
bn_from_mon(p->y, ec_p, 20);
}
#if 0
static int point_is_on_curve(u8 *p)
{
u8 s[20], t[20];
u8 *x, *y;
x = p;
y = p + 20;
elt_square(t, x);
elt_mul(s, t, x);
elt_mul(t, x, ec_a);
elt_add(s, s, t);
elt_add(s, s, ec_b);
elt_square(t, y);
elt_sub(s, s, t);
return elt_is_zero(s);
}
#endif
static void point_zero(struct point *p)
{
elt_zero(p->x);
elt_zero(p->y);
}
static int point_is_zero(struct point *p)
{
return elt_is_zero(p->x) && elt_is_zero(p->y);
}
static void point_double(struct point *r, struct point *p)
{
u8 s[20], t[20];
struct point pp;
u8 *px, *py, *rx, *ry;
pp = *p;
px = pp.x;
py = pp.y;
rx = r->x;
ry = r->y;
if (elt_is_zero(py)) {
point_zero(r);
return;
}
elt_square(t, px); // t = px*px
elt_add(s, t, t); // s = 2*px*px
elt_add(s, s, t); // s = 3*px*px
elt_add(s, s, ec_a); // s = 3*px*px + a
elt_add(t, py, py); // t = 2*py
elt_inv(t, t); // t = 1/(2*py)
elt_mul(s, s, t); // s = (3*px*px+a)/(2*py)
elt_square(rx, s); // rx = s*s
elt_add(t, px, px); // t = 2*px
elt_sub(rx, rx, t); // rx = s*s - 2*px
elt_sub(t, px, rx); // t = -(rx-px)
elt_mul(ry, s, t); // ry = -s*(rx-px)
elt_sub(ry, ry, py); // ry = -s*(rx-px) - py
}
static void point_add(struct point *r, struct point *p, struct point *q)
{
u8 s[20], t[20], u[20];
u8 *px, *py, *qx, *qy, *rx, *ry;
struct point pp, qq;
pp = *p;
qq = *q;
px = pp.x;
py = pp.y;
qx = qq.x;
qy = qq.y;
rx = r->x;
ry = r->y;
if (point_is_zero(&pp)) {
elt_copy(rx, qx);
elt_copy(ry, qy);
return;
}
if (point_is_zero(&qq)) {
elt_copy(rx, px);
elt_copy(ry, py);
return;
}
elt_sub(u, qx, px);
if (elt_is_zero(u)) {
elt_sub(u, qy, py);
if (elt_is_zero(u))
point_double(r, &pp);
else
point_zero(r);
return;
}
elt_inv(t, u); // t = 1/(qx-px)
elt_sub(u, qy, py); // u = qy-py
elt_mul(s, t, u); // s = (qy-py)/(qx-px)
elt_square(rx, s); // rx = s*s
elt_add(t, px, qx); // t = px+qx
elt_sub(rx, rx, t); // rx = s*s - (px+qx)
elt_sub(t, px, rx); // t = -(rx-px)
elt_mul(ry, s, t); // ry = -s*(rx-px)
elt_sub(ry, ry, py); // ry = -s*(rx-px) - py
}
static void point_mul(struct point *d, u8 *a, struct point *b) // a is bignum
{
u32 i;
u8 mask;
point_zero(d);
for (i = 0; i < 21; i++)
for (mask = 0x80; mask != 0; mask >>= 1) {
point_double(d, d);
if ((a[i] & mask) != 0)
point_add(d, d, b);
}
}
static void generate_ecdsa(u8 *R, u8 *S, u8 *k, u8 *hash)
{
u8 e[21];
u8 kk[21];
u8 m[21];
u8 minv[21];
struct point mG;
e[0] = 0;
memcpy(e + 1, hash, 20);
bn_reduce(e, ec_N, 21);
try_again:
prng(m, 21);
m[0] = 0;
if (bn_compare(m, ec_N, 21) >= 0)
goto try_again;
// R = (mG).x
point_mul(&mG, m, &ec_G);
point_from_mon(&mG);
R[0] = 0;
elt_copy(R+1, mG.x);
// S = m**-1*(e + Rk) (mod N)
bn_copy(kk, k, 21);
bn_reduce(kk, ec_N, 21);
bn_to_mon(m, ec_N, 21);
bn_to_mon(e, ec_N, 21);
bn_to_mon(R, ec_N, 21);
bn_to_mon(kk, ec_N, 21);
bn_mon_mul(S, R, kk, ec_N, 21);
bn_add(kk, S, e, ec_N, 21);
bn_mon_inv(minv, m, ec_N, 21);
bn_mon_mul(S, minv, kk, ec_N, 21);
bn_from_mon(R, ec_N, 21);
bn_from_mon(S, ec_N, 21);
}
static int check_ecdsa(struct point *Q, u8 *R, u8 *S, u8 *hash)
{
u8 Sinv[21];
u8 e[21];
u8 w1[21], w2[21];
struct point r1, r2;
u8 rr[21];
e[0] = 0;
memcpy(e + 1, hash, 20);
bn_reduce(e, ec_N, 21);
bn_to_mon(R, ec_N, 21);
bn_to_mon(S, ec_N, 21);
bn_to_mon(e, ec_N, 21);
bn_mon_inv(Sinv, S, ec_N, 21);
bn_mon_mul(w1, e, Sinv, ec_N, 21);
bn_mon_mul(w2, R, Sinv, ec_N, 21);
bn_from_mon(w1, ec_N, 21);
bn_from_mon(w2, ec_N, 21);
point_mul(&r1, w1, &ec_G);
point_mul(&r2, w2, Q);
point_add(&r1, &r1, &r2);
point_from_mon(&r1);
rr[0] = 0;
memcpy(rr + 1, r1.x, 20);
bn_reduce(rr, ec_N, 21);
bn_from_mon(R, ec_N, 21);
bn_from_mon(S, ec_N, 21);
return (bn_compare(rr, R, 21) == 0);
}
#if 0
static void ec_priv_to_pub(u8 *k, u8 *Q)
{
point_mul(Q, k, ec_G);
}
#endif
int ecdsa_set_curve(u8* p, u8* a, u8* b, u8* N, u8* Gx, u8* Gy)
{
memcpy(ec_p, p, 20);
memcpy(ec_a, a, 20);
memcpy(ec_b, b, 20);
memcpy(ec_N, N, 21);
memcpy(ec_G.x, Gx, 20);
memcpy(ec_G.y, Gy, 20);
bn_to_mon(ec_a, ec_p, 20);
bn_to_mon(ec_b, ec_p, 20);
point_to_mon(&ec_G);
return 0;
}
void ecdsa_set_pub(u8 *Q)
{
memcpy(ec_Q.x, Q, 20);
memcpy(ec_Q.y, Q+20, 20);
point_to_mon(&ec_Q);
}
void ecdsa_set_priv(u8 *k)
{
memcpy(ec_k, k, sizeof ec_k);
}
int ecdsa_verify(u8 *hash, u8 *R, u8 *S)
{
return check_ecdsa(&ec_Q, R, S, hash);
}
void ecdsa_sign(u8 *hash, u8 *R, u8 *S)
{
generate_ecdsa(R, S, ec_k, hash);
}

14
rpcs3/Crypto/ec.h Normal file
View File

@ -0,0 +1,14 @@
#pragma once
// Copyright (C) 2014 Hykem <hykem@hotmail.com>
// Licensed under the terms of the GNU GPL, version 3
// http://www.gnu.org/licenses/gpl-3.0.txt
#include <string.h>
#include <stdio.h>
int ecdsa_set_curve(unsigned char *p, unsigned char *a, unsigned char *b, unsigned char *N, unsigned char *Gx, unsigned char *Gy);
void ecdsa_set_pub(unsigned char *Q);
void ecdsa_set_priv(unsigned char *k);
int ecdsa_verify(unsigned char *hash, unsigned char *R, unsigned char *S);
void ecdsa_sign(unsigned char *hash, unsigned char *R, unsigned char *S);

View File

@ -8,10 +8,10 @@ SELF_KEY::SELF_KEY(u64 ver, u16 rev, u32 type, const std::string& e, const std::
version = ver; version = ver;
revision = rev; revision = rev;
self_type = type; self_type = type;
hex_to_bytes(erk, e.c_str()); hex_to_bytes(erk, e.c_str(), 0);
hex_to_bytes(riv, r.c_str()); hex_to_bytes(riv, r.c_str(), 0);
hex_to_bytes(pub, pb.c_str()); hex_to_bytes(pub, pb.c_str(), 0);
hex_to_bytes(priv, pr.c_str()); hex_to_bytes(priv, pr.c_str(), 0);
curve_type = ct; curve_type = ct;
} }

View File

@ -104,6 +104,35 @@ static u8 EDAT_IV[0x10] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
}; };
static u8 VSH_CURVE_P[0x14] = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
static u8 VSH_CURVE_A[0x14] = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC
};
static u8 VSH_CURVE_B[0x14] = {
0xA6, 0x8B, 0xED, 0xC3, 0x34, 0x18, 0x02, 0x9C, 0x1D, 0x3C, 0xE3, 0x3B, 0x9A, 0x32, 0x1F, 0xCC, 0xBB, 0x9E, 0x0F, 0x0B
};
static u8 VSH_CURVE_N[0x15] = {
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xB5, 0xAE, 0x3C, 0x52, 0x3E, 0x63, 0x94, 0x4F, 0x21, 0x27
};
static u8 VSH_CURVE_GX[0x14] = {
0x12, 0x8E, 0xC4, 0x25, 0x64, 0x87, 0xFD, 0x8F, 0xDF, 0x64, 0xE2, 0x43, 0x7B, 0xC0, 0xA1, 0xF6, 0xD5, 0xAF, 0xDE, 0x2C
};
static u8 VSH_CURVE_GY[0x14] = {
0x59, 0x58, 0x55, 0x7E, 0xB1, 0xDB, 0x00, 0x12, 0x60, 0x42, 0x55, 0x24, 0xDB, 0xC3, 0x79, 0xD5, 0xAC, 0x5F, 0x4A, 0xDF
};
static u8 VSH_PUB[0x28] = {
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
};
class KeyVault class KeyVault
{ {
std::vector<SELF_KEY> sk_LV0_arr; std::vector<SELF_KEY> sk_LV0_arr;

View File

@ -4,26 +4,20 @@
#include "lz.h" #include "lz.h"
int decode_range(unsigned int *range, unsigned int *code, unsigned char **src) void decode_range(unsigned int *range, unsigned int *code, unsigned char **src)
{ {
if (!((*range) >> 24)) if (!((*range) >> 24))
{ {
(*range) <<= 8; (*range) <<= 8;
*code = ((*code) << 8) + (*src)++[5]; *code = ((*code) << 8) + (*src)++[5];
return 1;
} }
else
return 0;
} }
int decode_bit(unsigned int *range, unsigned int *code, int *index, unsigned char **src, unsigned char *c) int decode_bit(unsigned int *range, unsigned int *code, int *index, unsigned char **src, unsigned char *c)
{ {
unsigned int val = *range; decode_range(range, code, src);
if (decode_range(range, code, src)) unsigned int val = ((*range) >> 8) * (*c);
val *= (*c);
else
val = (val >> 8) * (*c);
*c -= ((*c) >> 3); *c -= ((*c) >> 3);
if (index) (*index) <<= 1; if (index) (*index) <<= 1;
@ -47,12 +41,12 @@ int decode_number(unsigned char *ptr, int index, int *bit_flag, unsigned int *ra
{ {
int i = 1; int i = 1;
if (index >= 3) if (index >= 3)
{ {
decode_bit(range, code, &i, src, ptr + 0x18); // Offset 0x978 decode_bit(range, code, &i, src, ptr + 0x18);
if (index >= 4) if (index >= 4)
{ {
decode_bit(range, code, &i, src, ptr + 0x18); // Offset 0x978 decode_bit(range, code, &i, src, ptr + 0x18);
if (index >= 5) if (index >= 5)
{ {
decode_range(range, code, src); decode_range(range, code, src);
@ -60,8 +54,8 @@ int decode_number(unsigned char *ptr, int index, int *bit_flag, unsigned int *ra
{ {
i <<= 1; i <<= 1;
(*range) >>= 1; (*range) >>= 1;
if (*code < *range) if (*code < *range)
i++; i++;
else else
(*code) -= *range; (*code) -= *range;
} }
@ -69,16 +63,16 @@ int decode_number(unsigned char *ptr, int index, int *bit_flag, unsigned int *ra
} }
} }
*bit_flag = decode_bit(range, code, &i, src, ptr); // Offset 0x960 *bit_flag = decode_bit(range, code, &i, src, ptr);
if (index >= 1) if (index >= 1)
{ {
decode_bit(range, code, &i, src, ptr + 0x8); // Offset 0x968 decode_bit(range, code, &i, src, ptr + 0x8);
if (index >= 2) if (index >= 2)
{ {
decode_bit(range, code, &i, src, ptr + 0x10); // Offset 0x970 decode_bit(range, code, &i, src, ptr + 0x10);
} }
} }
return i; return i;
} }
@ -88,12 +82,12 @@ int decode_word(unsigned char *ptr, int index, int *bit_flag, unsigned int *rang
int i = 1; int i = 1;
index /= 8; index /= 8;
if (index >= 3) if (index >= 3)
{ {
decode_bit(range, code, &i, src, ptr); // Offset 0x8A8 decode_bit(range, code, &i, src, ptr + 4);
if (index >= 4) if (index >= 4)
{ {
decode_bit(range, code, &i, src, ptr); // Offset 0x8A8 decode_bit(range, code, &i, src, ptr + 4);
if (index >= 5) if (index >= 5)
{ {
decode_range(range, code, src); decode_range(range, code, src);
@ -101,8 +95,8 @@ int decode_word(unsigned char *ptr, int index, int *bit_flag, unsigned int *rang
{ {
i <<= 1; i <<= 1;
(*range) >>= 1; (*range) >>= 1;
if (*code < *range) if (*code < *range)
i++; i++;
else else
(*code) -= *range; (*code) -= *range;
} }
@ -110,16 +104,16 @@ int decode_word(unsigned char *ptr, int index, int *bit_flag, unsigned int *rang
} }
} }
*bit_flag = decode_bit(range, code, &i, src, ptr + 3); // Offset 0x8A8 + 3 *bit_flag = decode_bit(range, code, &i, src, ptr);
if (index >= 1) if (index >= 1)
{ {
decode_bit(range, code, &i, src, ptr + 2); // Offset 0x8A8 + 2 decode_bit(range, code, &i, src, ptr + 1);
if (index >= 2) if (index >= 2)
{ {
decode_bit(range, code, &i, src, ptr + 1); // Offset 0x8A8 + 1 decode_bit(range, code, &i, src, ptr + 2);
} }
} }
return i; return i;
} }
@ -128,7 +122,7 @@ int decompress(unsigned char *out, unsigned char *in, unsigned int size)
{ {
int result; int result;
unsigned char *tmp = new unsigned char[0xA70]; unsigned char *tmp = new unsigned char[0xCC8];
int offset = 0; int offset = 0;
int bit_flag = 0; int bit_flag = 0;
@ -159,11 +153,11 @@ int decompress(unsigned char *out, unsigned char *in, unsigned int size)
else else
{ {
// Set up a temporary buffer (sliding window). // Set up a temporary buffer (sliding window).
memset(tmp, 0x80, 0xA60); memset(tmp, 0x80, 0xCA8);
while (1) while (1)
{ {
// Start reading at 0x920. // Start reading at 0xB68.
tmp_sect1 = tmp + offset + 0x920; tmp_sect1 = tmp + offset + 0xB68;
if (!decode_bit(&range, &code, 0, &in, tmp_sect1)) // Raw char. if (!decode_bit(&range, &code, 0, &in, tmp_sect1)) // Raw char.
{ {
// Adjust offset and check for stream end. // Adjust offset and check for stream end.
@ -189,57 +183,65 @@ int decompress(unsigned char *out, unsigned char *in, unsigned int size)
int index = -1; int index = -1;
// Identify the data length bit field. // Identify the data length bit field.
do { do
tmp_sect1 += 8; {
tmp_sect1 += 8;
bit_flag = decode_bit(&range, &code, 0, &in, tmp_sect1); bit_flag = decode_bit(&range, &code, 0, &in, tmp_sect1);
index += bit_flag; index += bit_flag;
} while ((bit_flag != 0) && (index < 6)); } while ((bit_flag != 0) && (index < 6));
// Default block size is 0x40. // Default block size is 0x160.
int b_size = 0x40; int b_size = 0x160;
tmp_sect2 = tmp + index + 0x7F1; tmp_sect2 = tmp + index + 0x7F1;
// If the data length was found, parse it as a number. // If the data length was found, parse it as a number.
if ((index >= 0) || (bit_flag != 0)) if ((index >= 0) || (bit_flag != 0))
{ {
// Locate next section. // Locate next section.
int sect = (index << 5) | (((((start - out)) << index) & 3) << 3) | (offset & 7); int sect = (index << 5) | (((((int)(start - out)) << index) & 3) << 3) | (offset & 7);
tmp_sect1 = tmp + 0x960 + sect; tmp_sect1 = tmp + 0xBA8 + sect;
// Decode the data length (8 bit fields). // Decode the data length (8 bit fields).
data_length = decode_number(tmp_sect1, index, &bit_flag, &range, &code, &in); data_length = decode_number(tmp_sect1, index, &bit_flag, &range, &code, &in);
if (data_length == 0xFF) return (start - out); // End of stream.
// If we got valid parameters, seek to find data offset. }
if ((data_length != 3) && ((index > 0) || (bit_flag != 0))) { else
tmp_sect2 += 0x38; {
b_size = 0x80; // Block size is now 0x80.
}
} else {
// Assume one byte of advance. // Assume one byte of advance.
data_length = 1; data_length = 1;
} }
// If we got valid parameters, seek to find data offset.
if ((data_length <= 2))
{
tmp_sect2 += 0xF8;
b_size = 0x40; // Block size is now 0x40.
}
int diff = 0; int diff = 0;
int shift = 1; int shift = 1;
// Identify the data offset bit field. // Identify the data offset bit field.
do { do
{
diff = (shift << 4) - b_size; diff = (shift << 4) - b_size;
bit_flag = decode_bit(&range, &code, &shift, &in, tmp_sect2 + (shift << 3)); bit_flag = decode_bit(&range, &code, &shift, &in, tmp_sect2 + (shift << 3));
} while (diff < 0); } while (diff < 0);
// If the data offset was found, parse it as a number. // If the data offset was found, parse it as a number.
if ((diff > 0) || (bit_flag != 0)) if ((diff > 0) || (bit_flag != 0))
{ {
// Adjust diff if needed. // Adjust diff if needed.
if (bit_flag == 0) diff -= 8; if (bit_flag == 0) diff -= 8;
// Locate section. // Locate section.
tmp_sect3 = tmp + 0x8A8 + diff; tmp_sect3 = tmp + 0x928 + diff;
// Decode the data offset (1 bit fields). // Decode the data offset (1 bit fields).
data_offset = decode_word(tmp_sect3, diff, &bit_flag, &range, &code, &in); data_offset = decode_word(tmp_sect3, diff, &bit_flag, &range, &code, &in);
} else { }
else
{
// Assume one byte of advance. // Assume one byte of advance.
data_offset = 1; data_offset = 1;
} }
@ -260,7 +262,8 @@ int decompress(unsigned char *out, unsigned char *in, unsigned int size)
offset = ((((int)(buf_end - out)) + 1) & 1) + 6; offset = ((((int)(buf_end - out)) + 1) & 1) + 6;
// Copy data. // Copy data.
do { do
{
*start++ = *buf_start++; *start++ = *buf_start++;
} while (start < buf_end); } while (start < buf_end);
@ -271,4 +274,4 @@ int decompress(unsigned char *out, unsigned char *in, unsigned int size)
} }
delete[] tmp; delete[] tmp;
return result; return result;
} }

View File

@ -1,8 +1,15 @@
#pragma once #pragma once
// Copyright (C) 2014 Hykem <hykem@hotmail.com>
// Licensed under the terms of the GNU GPL, version 3
// http://www.gnu.org/licenses/gpl-3.0.txt
// Reverse-engineered custom LempelZivMarkov based compression.
#include <string.h> #include <string.h>
int decode_range(unsigned int *range, unsigned int *code, unsigned char **src); void decode_range(unsigned int *range, unsigned int *code, unsigned char **src);
int decode_bit(unsigned int *range, unsigned int *code, int *index, unsigned char **src, unsigned char *c); int decode_bit(unsigned int *range, unsigned int *code, int *index, unsigned char **src, unsigned char *c);
int decode_number(unsigned char *ptr, int index, int *bit_flag, unsigned int *range, unsigned int *code, unsigned char **src); int decode_number(unsigned char *ptr, int index, int *bit_flag, unsigned int *range, unsigned int *code, unsigned char **src);
int decode_word(unsigned char *ptr, int index, int *bit_flag, unsigned int *range, unsigned int *code, unsigned char **src); int decode_word(unsigned char *ptr, int index, int *bit_flag, unsigned int *range, unsigned int *code, unsigned char **src);
int decompress(unsigned char *out, unsigned char *in, unsigned int size); int decompress(unsigned char *out, unsigned char *in, unsigned int size);

View File

@ -1,4 +1,5 @@
#pragma once #pragma once
/** /**
* \file sha1.h * \file sha1.h
* *

View File

@ -1,5 +1,4 @@
#include "stdafx.h" #include "stdafx.h"
#include "utils.h"
#include "key_vault.h" #include "key_vault.h"
#include "unedat.h" #include "unedat.h"
#include "Utilities/Log.h" #include "Utilities/Log.h"
@ -7,7 +6,7 @@
void generate_key(int crypto_mode, int version, unsigned char *key_final, unsigned char *iv_final, unsigned char *key, unsigned char *iv) 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); int mode = (int)(crypto_mode & 0xF0000000);
switch (mode) { switch (mode) {
case 0x10000000: case 0x10000000:
// Encrypted ERK. // Encrypted ERK.
@ -32,7 +31,7 @@ void generate_key(int crypto_mode, int version, unsigned char *key_final, unsign
void generate_hash(int hash_mode, int version, unsigned char *hash_final, unsigned char *hash) void generate_hash(int hash_mode, int version, unsigned char *hash_final, unsigned char *hash)
{ {
int mode = (int) (hash_mode & 0xF0000000); int mode = (int)(hash_mode & 0xF0000000);
switch (mode) { switch (mode) {
case 0x10000000: case 0x10000000:
// Encrypted HASH. // Encrypted HASH.
@ -52,7 +51,7 @@ void generate_hash(int hash_mode, int version, unsigned char *hash_final, unsign
}; };
} }
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) bool decrypt(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. // Setup buffers for key, iv and hash.
unsigned char key_final[0x10] = {}; unsigned char key_final[0x10] = {};
@ -77,29 +76,30 @@ bool crypto(int hash_mode, int crypto_mode, int version, unsigned char *in, unsi
} }
else else
{ {
LOG_ERROR(LOADER, "EDAT: Unknown crypto algorithm!\n"); LOG_ERROR(LOADER, "EDAT: Unknown crypto algorithm!");
return false; return false;
} }
if ((hash_mode & 0xFF) == 0x01) // 0x14 SHA1-HMAC if ((hash_mode & 0xFF) == 0x01) // 0x14 SHA1-HMAC
{ {
return hmac_hash_compare(hash_final_14, 0x14, in, length, test_hash); return hmac_hash_compare(hash_final_14, 0x14, in, length, test_hash, 0x14);
} }
else if ((hash_mode & 0xFF) == 0x02) // 0x10 AES-CMAC else if ((hash_mode & 0xFF) == 0x02) // 0x10 AES-CMAC
{ {
return cmac_hash_compare(hash_final_10, 0x10, in, length, test_hash); return cmac_hash_compare(hash_final_10, 0x10, in, length, test_hash, 0x10);
} }
else if ((hash_mode & 0xFF) == 0x04) //0x10 SHA1-HMAC else if ((hash_mode & 0xFF) == 0x04) //0x10 SHA1-HMAC
{ {
return hmac_hash_compare(hash_final_10, 0x10, in, length, test_hash); return hmac_hash_compare(hash_final_10, 0x10, in, length, test_hash, 0x10);
} }
else else
{ {
LOG_ERROR(LOADER, "EDAT: Unknown hashing algorithm!\n"); LOG_ERROR(LOADER, "EDAT: Unknown hashing algorithm!");
return false; return false;
} }
} }
// EDAT/SDAT functions.
unsigned char* dec_section(unsigned char* metadata) unsigned char* dec_section(unsigned char* metadata)
{ {
unsigned char *dec = new unsigned char[0x10]; unsigned char *dec = new unsigned char[0x10];
@ -135,11 +135,11 @@ unsigned char* get_block_key(int block, NPD_HEADER *npd)
return dest_key; return dest_key;
} }
// EDAT/SDAT functions. // EDAT/SDAT decryption.
int decrypt_data(rFile *in, rFile *out, EDAT_SDAT_HEADER *edat, NPD_HEADER *npd, unsigned char* crypt_key, bool verbose) int decrypt_data(rFile *in, rFile *out, EDAT_HEADER *edat, NPD_HEADER *npd, unsigned char* crypt_key, bool verbose)
{ {
// Get metadata info and setup buffers. // Get metadata info and setup buffers.
int block_num = (int) ((edat->file_size + edat->block_size - 1) / edat->block_size); 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_section_size = ((edat->flags & EDAT_COMPRESSED_FLAG) != 0 || (edat->flags & EDAT_FLAG_0x20) != 0) ? 0x20 : 0x10;
int metadata_offset = 0x100; int metadata_offset = 0x100;
@ -148,78 +148,103 @@ int decrypt_data(rFile *in, rFile *out, EDAT_SDAT_HEADER *edat, NPD_HEADER *npd,
unsigned char *b_key; unsigned char *b_key;
unsigned char *iv; unsigned char *iv;
unsigned char hash[0x10];
unsigned char key_result[0x10];
unsigned char hash_result[0x14];
memset(hash, 0, 0x10);
memset(key_result, 0, 0x10);
memset(hash_result, 0, 0x14);
unsigned long long offset = 0;
unsigned long long metadata_sec_offset = 0;
int length = 0;
int compression_end = 0;
unsigned char empty_iv[0x10] = {}; unsigned char empty_iv[0x10] = {};
// Decrypt the metadata. // Decrypt the metadata.
for (int i = 0; i < block_num; i++) int i;
for (i = 0; i < block_num; i++)
{ {
in->Seek(metadata_offset + i * metadata_section_size); memset(hash_result, 0, 0x14);
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) if ((edat->flags & EDAT_COMPRESSED_FLAG) != 0)
{ {
unsigned char metadata[0x20]; metadata_sec_offset = metadata_offset + (unsigned long long) i * metadata_section_size;
in->Read(metadata, 0x20); in->Seek(metadata_sec_offset);
// 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;
unsigned char metadata[0x20];
memset(metadata, 0, 0x20);
in->Read(metadata, 0x20);
// If the data is compressed, decrypt the metadata.
// NOTE: For NPD version 1 the metadata is not encrypted.
if (npd->version <= 1)
{
offset = swap64(*(unsigned long long*)&metadata[0x10]);
length = swap32(*(int*)&metadata[0x18]);
compression_end = swap32(*(int*)&metadata[0x1C]);
}
else
{
unsigned char *result = dec_section(metadata);
offset = swap64(*(unsigned long long*)&result[0]);
length = swap32(*(int*)&result[8]);
compression_end = swap32(*(int*)&result[12]);
delete[] result;
}
memcpy(hash_result, metadata, 0x10); memcpy(hash_result, metadata, 0x10);
} }
else if ((edat->flags & EDAT_FLAG_0x20) != 0) else if ((edat->flags & EDAT_FLAG_0x20) != 0)
{ {
// If FLAG 0x20, the metadata precedes each data block. // If FLAG 0x20, the metadata precedes each data block.
in->Seek(metadata_offset + i * metadata_section_size + length); metadata_sec_offset = metadata_offset + (unsigned long long) i * (metadata_section_size + length);
in->Seek(metadata_sec_offset);
unsigned char metadata[0x20]; unsigned char metadata[0x20];
memset(metadata, 0, 0x20);
in->Read(metadata, 0x20); in->Read(metadata, 0x20);
memcpy(hash_result, metadata, 0x14);
// If FLAG 0x20 is set, apply custom xor. // If FLAG 0x20 is set, apply custom xor.
for (int j = 0; j < 0x10; j++) { int j;
hash_result[j] = (unsigned char)(metadata[j] ^ metadata[j+0x10]); for (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; offset = metadata_sec_offset + 0x20;
length = edat->block_size; length = edat->block_size;
if ((i == (block_num - 1)) && (edat->file_size % edat->block_size)) if ((i == (block_num - 1)) && (edat->file_size % edat->block_size))
length = (int) (edat->file_size % edat->block_size); length = (int)(edat->file_size % edat->block_size);
} }
else else
{ {
metadata_sec_offset = metadata_offset + (unsigned long long) i * metadata_section_size;
in->Seek(metadata_sec_offset);
in->Read(hash_result, 0x10); in->Read(hash_result, 0x10);
offset = metadata_offset + i * edat->block_size + block_num * metadata_section_size; offset = metadata_offset + (unsigned long long) i * edat->block_size + (unsigned long long) block_num * metadata_section_size;
length = edat->block_size; length = edat->block_size;
if ((i == (block_num - 1)) && (edat->file_size % edat->block_size)) if ((i == (block_num - 1)) && (edat->file_size % edat->block_size))
length = (int) (edat->file_size % edat->block_size); length = (int)(edat->file_size % edat->block_size);
} }
// Locate the real data. // Locate the real data.
int pad_length = length; int pad_length = length;
length = (int) ((pad_length + 0xF) & 0xFFFFFFF0); length = (int)((pad_length + 0xF) & 0xFFFFFFF0);
in->Seek(offset);
// Setup buffers for decryption and read the data. // Setup buffers for decryption and read the data.
enc_data = new unsigned char[length]; enc_data = new unsigned char[length];
dec_data = new unsigned char[length]; dec_data = new unsigned char[length];
unsigned char key_result[0x10]; memset(enc_data, 0, length);
unsigned char hash[0x10]; memset(dec_data, 0, length);
memset(hash, 0, 0x10);
memset(key_result, 0, 0x10);
in->Seek(offset);
in->Read(enc_data, length); in->Read(enc_data, length);
// Generate a key for the current block. // Generate a key for the current block.
b_key = get_block_key(i, npd); b_key = get_block_key(i, npd);
@ -247,7 +272,7 @@ int decrypt_data(rFile *in, rFile *out, EDAT_SDAT_HEADER *edat, NPD_HEADER *npd,
hash_mode |= 0x10000000; hash_mode |= 0x10000000;
} }
if ((edat->flags & EDAT_DEBUG_DATA_FLAG) != 0) if ((edat->flags & EDAT_DEBUG_DATA_FLAG) != 0)
{ {
// Reset the flags. // Reset the flags.
crypto_mode |= 0x01000000; crypto_mode |= 0x01000000;
@ -260,7 +285,13 @@ int decrypt_data(rFile *in, rFile *out, EDAT_SDAT_HEADER *edat, NPD_HEADER *npd,
// IV is null if NPD version is 1 or 0. // IV is null if NPD version is 1 or 0.
iv = (npd->version <= 1) ? empty_iv : npd->digest; iv = (npd->version <= 1) ? empty_iv : npd->digest;
// Call main crypto routine on this data block. // 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); if (!decrypt(hash_mode, crypto_mode, (npd->version == 4), enc_data, dec_data, length, key_result, iv, hash, hash_result))
{
if (verbose)
LOG_WARNING(LOADER, "EDAT: Block at offset 0x%llx has invalid hash!", offset);
return 1;
}
} }
// Apply additional compression if needed and write the decrypted data. // Apply additional compression if needed and write the decrypted data.
@ -271,28 +302,28 @@ int decrypt_data(rFile *in, rFile *out, EDAT_SDAT_HEADER *edat, NPD_HEADER *npd,
memset(decomp_data, 0, decomp_size); memset(decomp_data, 0, decomp_size);
if (verbose) if (verbose)
LOG_NOTICE(LOADER, "EDAT: Decompressing...\n"); LOG_NOTICE(LOADER, "EDAT: Decompressing data...");
int res = lz_decompress(decomp_data, dec_data, decomp_size); int res = decompress(decomp_data, dec_data, decomp_size);
out->Write(decomp_data, res); out->Write(decomp_data, res);
if (verbose) if (verbose)
{ {
LOG_NOTICE(LOADER, "EDAT: Compressed block size: %d\n", pad_length); LOG_NOTICE(LOADER, "EDAT: Compressed block size: %d", pad_length);
LOG_NOTICE(LOADER, "EDAT: Decompressed block size: %d\n", res); LOG_NOTICE(LOADER, "EDAT: Decompressed block size: %d", res);
} }
edat->file_size -= res; edat->file_size -= res;
if (edat->file_size == 0) if (edat->file_size == 0)
{ {
if (res < 0) if (res < 0)
{ {
LOG_ERROR(LOADER, "EDAT: Decompression failed!\n"); LOG_ERROR(LOADER, "EDAT: Decompression failed!");
return 1; return 1;
} }
else else
LOG_SUCCESS(LOADER, "EDAT: Data successfully decompressed!\n"); LOG_NOTICE(LOADER, "EDAT: Successfully decompressed!");
} }
delete[] decomp_data; delete[] decomp_data;
@ -309,158 +340,262 @@ int decrypt_data(rFile *in, rFile *out, EDAT_SDAT_HEADER *edat, NPD_HEADER *npd,
return 0; return 0;
} }
static bool check_flags(EDAT_SDAT_HEADER *edat, NPD_HEADER *npd) int check_data(unsigned char *key, EDAT_HEADER *edat, NPD_HEADER *npd, rFile *f, bool verbose)
{ {
if (edat == nullptr || npd == nullptr) f->Seek(0);
return false; unsigned char header[0xA0];
unsigned char empty_header[0xA0];
unsigned char header_hash[0x10];
unsigned char metadata_hash[0x10];
memset(header, 0, 0xA0);
memset(empty_header, 0, 0xA0);
memset(header_hash, 0, 0x10);
memset(metadata_hash, 0, 0x10);
if (npd->version == 0 || npd->version == 1) // Check NPD version and flags.
if ((npd->version == 0) || (npd->version == 1))
{ {
if (edat->flags & 0x7EFFFFFE) if (edat->flags & 0x7EFFFFFE)
{ {
LOG_ERROR(LOADER, "EDAT: Bad header flags!\n"); LOG_ERROR(LOADER, "EDAT: Bad header flags!");
return false; return 1;
} }
} }
else if (npd->version == 2) else if (npd->version == 2)
{ {
if (edat->flags & 0x7EFFFFE0) if (edat->flags & 0x7EFFFFE0)
{ {
LOG_ERROR(LOADER, "EDAT: Bad header flags!\n"); LOG_ERROR(LOADER, "EDAT: Bad header flags!");
return false; return 1;
} }
} }
else if (npd->version == 3 || npd->version == 4) else if ((npd->version == 3) || (npd->version == 4))
{ {
if (edat->flags & 0x7EFFFFC0) if (edat->flags & 0x7EFFFFC0)
{ {
LOG_ERROR(LOADER, "EDAT: Bad header flags!\n"); LOG_ERROR(LOADER, "EDAT: Bad header flags!");
return false; return 1;
} }
} }
else if (npd->version > 4) else
{ {
LOG_ERROR(LOADER, "EDAT: Unknown version - %d\n", npd->version); LOG_ERROR(LOADER, "EDAT: Unknown 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; return 1;
} }
// Read in the file header. // Read in the file header.
f->Read(header, 0xA0); f->Read(header, 0xA0);
f->Read(hash_result, 0x10);
// Read in the header and metadata section hashes.
f->Seek(0x90);
f->Read(metadata_hash, 0x10);
f->Read(header_hash, 0x10);
// Setup the hashing mode and the crypto mode used in the file. // Setup the hashing mode and the crypto mode used in the file.
int crypto_mode = 0x1; int crypto_mode = 0x1;
int hash_mode = ((edat->flags & EDAT_ENCRYPTED_KEY_FLAG) == 0) ? 0x00000002 : 0x10000002; int hash_mode = ((edat->flags & EDAT_ENCRYPTED_KEY_FLAG) == 0) ? 0x00000002 : 0x10000002;
if ((edat->flags & EDAT_DEBUG_DATA_FLAG) != 0) if ((edat->flags & EDAT_DEBUG_DATA_FLAG) != 0)
{ {
LOG_ERROR(LOADER, "EDAT: DEBUG data detected!\n");
hash_mode |= 0x01000000; hash_mode |= 0x01000000;
if (verbose)
LOG_WARNING(LOADER, "EDAT: DEBUG data detected!");
} }
// Setup header key and iv buffers. // Setup header key and iv buffers.
unsigned char header_key[0x10] = {}; unsigned char header_key[0x10];
unsigned char header_iv[0x10] = {}; unsigned char header_iv[0x10];
memset(header_key, 0, 0x10);
memset(header_iv, 0, 0x10);
// Test the header hash (located at offset 0xA0). // 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 (!decrypt(hash_mode, crypto_mode, (npd->version == 4), header, empty_header, 0xA0, header_key, header_iv, key, header_hash))
{ {
if (verbose) if (verbose)
LOG_WARNING(LOADER, "EDAT: Header hash is invalid!\n"); LOG_WARNING(LOADER, "EDAT: Header hash is invalid!");
// If the header hash test fails and the data is not DEBUG, then RAP/RIF/KLIC key is invalid.
if ((edat->flags & EDAT_DEBUG_DATA_FLAG) != EDAT_DEBUG_DATA_FLAG)
{
LOG_ERROR(LOADER, "EDAT: RAP/RIF/KLIC key is invalid!");
return 1;
}
} }
// Parse the metadata info. // Parse the metadata info.
int metadata_section_size = 0x10; int metadata_section_size = ((edat->flags & EDAT_COMPRESSED_FLAG) != 0 || (edat->flags & EDAT_FLAG_0x20) != 0) ? 0x20 : 0x10;
if (((edat->flags & EDAT_COMPRESSED_FLAG) != 0)) if (((edat->flags & EDAT_COMPRESSED_FLAG) != 0))
{ {
LOG_WARNING(LOADER, "EDAT: COMPRESSED data detected!\n"); if (verbose)
metadata_section_size = 0x20; LOG_WARNING(LOADER, "EDAT: COMPRESSED data detected!");
} }
int block_num = (int) ((edat->file_size + edat->block_size - 1) / edat->block_size); int block_num = (int)((edat->file_size + edat->block_size - 1) / edat->block_size);
int bytes_read = 0;
int metadata_offset = 0x100; int metadata_offset = 0x100;
int metadata_size = metadata_section_size * block_num;
long long metadata_section_offset = metadata_offset;
long bytes_read = 0;
long bytes_to_read = metadata_size;
unsigned char *metadata = new unsigned char[metadata_size];
unsigned char *empty_metadata = new unsigned char[metadata_size];
long bytes_to_read = metadata_section_size * block_num;
while (bytes_to_read > 0) while (bytes_to_read > 0)
{ {
// Locate the metadata blocks. // 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_section_offset);
f->Seek(metadata_offset + bytes_read);
unsigned char *data = new unsigned char[block_size];
// Read in the metadata. // Read in the metadata.
tmp = new unsigned char[block_size]; f->Read(metadata + bytes_read, metadata_section_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. // Adjust sizes.
bytes_read += block_size; bytes_read += metadata_section_size;
bytes_to_read -= block_size; bytes_to_read -= metadata_section_size;
delete[] data; if (((edat->flags & EDAT_FLAG_0x20) != 0)) // Metadata block before each data block.
metadata_section_offset += (metadata_section_size + edat->block_size);
else
metadata_section_offset += metadata_section_size;
}
// Test the metadata section hash (located at offset 0x90).
if (!decrypt(hash_mode, crypto_mode, (npd->version == 4), metadata, empty_metadata, metadata_size, header_key, header_iv, key, metadata_hash))
{
if (verbose)
LOG_WARNING(LOADER, "EDAT: Metadata section hash is invalid!");
}
// Checking ECDSA signatures.
if ((edat->flags & EDAT_DEBUG_DATA_FLAG) == 0)
{
LOG_NOTICE(LOADER, "EDAT: Checking signatures...");
// Setup buffers.
unsigned char metadata_signature[0x28];
unsigned char header_signature[0x28];
unsigned char signature_hash[20];
unsigned char signature_r[0x15];
unsigned char signature_s[0x15];
unsigned char zero_buf[0x15];
memset(metadata_signature, 0, 0x28);
memset(header_signature, 0, 0x28);
memset(signature_hash, 0, 20);
memset(signature_r, 0, 0x15);
memset(signature_s, 0, 0x15);
memset(zero_buf, 0, 0x15);
// Setup ECDSA curve and public key.
ecdsa_set_curve(VSH_CURVE_P, VSH_CURVE_A, VSH_CURVE_B, VSH_CURVE_N, VSH_CURVE_GX, VSH_CURVE_GY);
ecdsa_set_pub(VSH_PUB);
// Read in the metadata and header signatures.
f->Seek(0xB0);
f->Read(metadata_signature, 0x28);
f->Seek(0xD8);
f->Read(header_signature, 0x28);
// Checking metadata signature.
// Setup signature r and s.
memcpy(signature_r + 01, metadata_signature, 0x14);
memcpy(signature_s + 01, metadata_signature + 0x14, 0x14);
if ((!memcmp(signature_r, zero_buf, 0x15)) || (!memcmp(signature_s, zero_buf, 0x15)))
LOG_WARNING(LOADER, "EDAT: Metadata signature is invalid!");
else
{
// Setup signature hash.
if ((edat->flags & EDAT_FLAG_0x20) != 0) //Sony failed again, they used buffer from 0x100 with half size of real metadata.
{
int metadata_buf_size = block_num * 0x10;
unsigned char *metadata_buf = new unsigned char[metadata_buf_size];
f->Seek(metadata_offset);
f->Read(metadata_buf, metadata_buf_size);
sha1(metadata_buf, metadata_buf_size, signature_hash);
delete[] metadata_buf;
}
else
sha1(metadata, metadata_size, signature_hash);
if (!ecdsa_verify(signature_hash, signature_r, signature_s))
{
LOG_WARNING(LOADER, "EDAT: Metadata signature is invalid!");
if (((unsigned long long)edat->block_size * block_num) > 0x100000000)
LOG_WARNING(LOADER, "EDAT: *Due to large file size, metadata signature status may be incorrect!");
}
else
LOG_NOTICE(LOADER, "EDAT: Metadata signature is valid!");
}
// Checking header signature.
// Setup header signature r and s.
memset(signature_r, 0, 0x15);
memset(signature_s, 0, 0x15);
memcpy(signature_r + 01, header_signature, 0x14);
memcpy(signature_s + 01, header_signature + 0x14, 0x14);
if ((!memcmp(signature_r, zero_buf, 0x15)) || (!memcmp(signature_s, zero_buf, 0x15)))
LOG_WARNING(LOADER, "EDAT: Header signature is invalid!");
else
{
// Setup header signature hash.
memset(signature_hash, 0, 20);
unsigned char *header_buf = new unsigned char[0xD8];
f->Seek(0x00);
f->Read(header_buf, 0xD8);
sha1(header_buf, 0xD8, signature_hash );
delete[] header_buf;
if (ecdsa_verify(signature_hash, signature_r, signature_s))
LOG_NOTICE(LOADER, "EDAT: Header signature is valid!");
else
LOG_WARNING(LOADER, "EDAT: Header signature is invalid!");
}
} }
// Cleanup. // Cleanup.
delete[] header; delete[] metadata;
delete[] tmp; delete[] empty_metadata;
delete[] hash_result;
return 0; return 0;
} }
void validate_data(const char* file_name, unsigned char *klicensee, NPD_HEADER *npd, bool verbose) int validate_npd_hashes(const char* file_name, unsigned char *klicensee, NPD_HEADER *npd, bool verbose)
{ {
int title_hash_result = 0; int title_hash_result = 0;
int dev_hash_result = 0; int dev_hash_result = 0;
int file_name_length = (int)strlen(file_name); int file_name_length = (int) strlen(file_name);
unsigned char *buf = new unsigned char[0x30 + file_name_length]; unsigned char *buf = new unsigned char[0x30 + file_name_length];
unsigned char dev[0x60];
unsigned char key[0x10]; unsigned char key[0x10];
memset(dev, 0, 0x60);
memset(key, 0, 0x10);
// Build the buffer (content_id + file_name). // Build the title buffer (content_id + file_name).
memcpy(buf, npd->content_id, 0x30); memcpy(buf, npd->content_id, 0x30);
memcpy(buf + 0x30, file_name, file_name_length); memcpy(buf + 0x30, file_name, file_name_length);
// Hash with NP_OMAC_KEY_3 and compare with title_hash. // Build the dev buffer (first 0x60 bytes of NPD header in big-endian).
title_hash_result = cmac_hash_compare(NP_OMAC_KEY_3, 0x10, buf, 0x30 + file_name_length, npd->title_hash); memcpy(dev, npd, 0x60);
// Fix endianness.
int version = swap32(npd->version);
int license = swap32(npd->license);
int type = swap32(npd->type);
memcpy(dev + 0x4, &version, 4);
memcpy(dev + 0x8, &license, 4);
memcpy(dev + 0xC, &type, 4);
// Hash with NPDRM_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, 0x10);
if (verbose) if (verbose)
{ {
if (title_hash_result) if (title_hash_result)
LOG_SUCCESS(LOADER, "EDAT: NPD title hash is valid!\n"); LOG_NOTICE(LOADER, "EDAT: NPD title hash is valid!");
else else
LOG_WARNING(LOADER, "EDAT: NPD title hash is invalid!\n"); LOG_WARNING(LOADER, "EDAT: NPD title hash is invalid!");
} }
// Check for an empty dev_hash (can't validate if devklic is NULL); // Check for an empty dev_hash (can't validate if devklic is NULL);
@ -477,40 +612,45 @@ void validate_data(const char* file_name, unsigned char *klicensee, NPD_HEADER *
if (isDevklicEmpty) if (isDevklicEmpty)
{ {
if (verbose) if (verbose)
LOG_WARNING(LOADER, "EDAT: NPD dev hash is empty!\n"); LOG_WARNING(LOADER, "EDAT: NPD dev hash is empty!");
// Allow empty dev hash.
dev_hash_result = 1;
} }
else else
{ {
// Generate klicensee xor key. // Generate klicensee xor key.
xor_(key, klicensee, NP_OMAC_KEY_2, 0x10); xor(key, klicensee, NP_OMAC_KEY_2, 0x10);
// Hash with generated key and compare with dev_hash. // Hash with generated key and compare with dev_hash.
dev_hash_result = cmac_hash_compare(key, 0x10, (unsigned char *)npd, 0x60, npd->dev_hash); dev_hash_result = cmac_hash_compare(key, 0x10, dev, 0x60, npd->dev_hash, 0x10);
if (verbose) if (verbose)
{ {
if (dev_hash_result) if (dev_hash_result)
LOG_SUCCESS(LOADER, "EDAT: NPD dev hash is valid!\n"); LOG_NOTICE(LOADER, "EDAT: NPD dev hash is valid!");
else else
LOG_WARNING(LOADER, "EDAT: NPD dev hash is invalid!\n"); LOG_WARNING(LOADER, "EDAT: NPD dev hash is invalid!");
} }
} }
delete[] buf; delete[] buf;
return (title_hash_result && dev_hash_result);
} }
bool extract_data(rFile *input, rFile *output, const char* input_file_name, unsigned char* devklic, unsigned char* rifkey, bool verbose) 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. // Setup NPD and EDAT/SDAT structs.
NPD_HEADER *NPD = new NPD_HEADER(); NPD_HEADER *NPD = new NPD_HEADER();
EDAT_SDAT_HEADER *EDAT = new EDAT_SDAT_HEADER(); EDAT_HEADER *EDAT = new EDAT_HEADER();
// Read in the NPD and EDAT/SDAT headers. // Read in the NPD and EDAT/SDAT headers.
char npd_header[0x80]; char npd_header[0x80];
char edat_header[0x10]; char edat_header[0x10];
input->Read(npd_header, 0x80); input->Read(npd_header, sizeof(npd_header));
input->Read(edat_header, 0x10); input->Read(edat_header, sizeof(edat_header));
memcpy(NPD->magic, npd_header, 4); memcpy(NPD->magic, npd_header, 4);
NPD->version = swap32(*(int*)&npd_header[4]); NPD->version = swap32(*(int*)&npd_header[4]);
NPD->license = swap32(*(int*)&npd_header[8]); NPD->license = swap32(*(int*)&npd_header[8]);
@ -523,7 +663,7 @@ bool extract_data(rFile *input, rFile *output, const char* input_file_name, unsi
NPD->unk2 = swap64(*(u64*)&npd_header[120]); NPD->unk2 = swap64(*(u64*)&npd_header[120]);
unsigned char npd_magic[4] = {0x4E, 0x50, 0x44, 0x00}; //NPD0 unsigned char npd_magic[4] = {0x4E, 0x50, 0x44, 0x00}; //NPD0
if(memcmp(NPD->magic, npd_magic, 4)) if (memcmp(NPD->magic, npd_magic, 4))
{ {
LOG_ERROR(LOADER, "EDAT: %s has invalid NPD header or already decrypted.", input_file_name); LOG_ERROR(LOADER, "EDAT: %s has invalid NPD header or already decrypted.", input_file_name);
delete NPD; delete NPD;
@ -537,41 +677,65 @@ bool extract_data(rFile *input, rFile *output, const char* input_file_name, unsi
if (verbose) if (verbose)
{ {
LOG_NOTICE(LOADER, "NPD HEADER\n"); LOG_NOTICE(LOADER, "NPD HEADER");
LOG_NOTICE(LOADER, "NPD version: %d\n", NPD->version); LOG_NOTICE(LOADER, "NPD version: %d", NPD->version);
LOG_NOTICE(LOADER, "NPD license: %d\n", NPD->license); LOG_NOTICE(LOADER, "NPD license: %d", NPD->license);
LOG_NOTICE(LOADER, "NPD type: %d\n", NPD->type); LOG_NOTICE(LOADER, "NPD type: %d", 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. // Set decryption key.
unsigned char key[0x10]; unsigned char key[0x10];
memset(key, 0, 0x10); memset(key, 0, 0x10);
if((EDAT->flags & SDAT_FLAG) == SDAT_FLAG) // Check EDAT/SDAT flag.
if ((EDAT->flags & SDAT_FLAG) == SDAT_FLAG)
{ {
LOG_WARNING(LOADER, "EDAT: SDAT detected!\n"); if (verbose)
xor_(key, NPD->dev_hash, SDAT_KEY, 0x10); {
LOG_NOTICE(LOADER, "SDAT HEADER");
LOG_NOTICE(LOADER, "SDAT flags: 0x%08X", EDAT->flags);
LOG_NOTICE(LOADER, "SDAT block size: 0x%08X", EDAT->block_size);
LOG_NOTICE(LOADER, "SDAT file size: 0x%08X", EDAT->file_size);
}
// Generate SDAT key.
xor(key, NPD->dev_hash, SDAT_KEY, 0x10);
} }
else else
{ {
// Perform header validation (optional step). if (verbose)
validate_data(input_file_name, devklic, NPD, verbose); {
LOG_NOTICE(LOADER, "EDAT HEADER");
LOG_NOTICE(LOADER, "EDAT flags: 0x%08X", EDAT->flags);
LOG_NOTICE(LOADER, "EDAT block size: 0x%08X", EDAT->block_size);
LOG_NOTICE(LOADER, "EDAT file size: 0x%08X", EDAT->file_size);
}
if ((NPD->license & 0x3) == 0x3) // Type 3: Use supplied devklic. // Perform header validation (EDAT only).
char real_file_name[MAX_PATH];
extract_file_name(input_file_name, real_file_name);
if (!validate_npd_hashes(real_file_name, devklic, NPD, verbose))
{
// Ignore header validation in DEBUG data.
if ((EDAT->flags & EDAT_DEBUG_DATA_FLAG) != EDAT_DEBUG_DATA_FLAG)
{
LOG_ERROR(LOADER, "EDAT: NPD hash validation failed!");
delete NPD;
delete EDAT;
return 1;
}
}
// Select EDAT key.
if ((NPD->license & 0x3) == 0x3) // Type 3: Use supplied devklic.
memcpy(key, devklic, 0x10); memcpy(key, devklic, 0x10);
else if ((NPD->license & 0x2) == 0x2) // Type 2: Use key from RAP file (RIF key). else if ((NPD->license & 0x2) == 0x2) // Type 2: Use key from RAP file (RIF key).
{ {
memcpy(key, rifkey, 0x10); memcpy(key, rifkey, 0x10);
// Make sure we don't have an empty RIF key. // Make sure we don't have an empty RIF key.
int i, test = 0; int i, test = 0;
for(i = 0; i < 0x10; i++) for (i = 0; i < 0x10; i++)
{ {
if (key[i] != 0) if (key[i] != 0)
{ {
@ -579,30 +743,65 @@ bool extract_data(rFile *input, rFile *output, const char* input_file_name, unsi
break; break;
} }
} }
if (!test) if (!test)
{ {
LOG_ERROR(LOADER, "EDAT: A valid RAP file is needed!"); LOG_ERROR(LOADER, "EDAT: A valid RAP file is needed for this EDAT file!");
delete NPD; delete NPD;
delete EDAT; delete EDAT;
return 1; return 1;
} }
} }
else if ((NPD->license & 0x1) == 0x1) // Type 1: Use network activation.
{
LOG_ERROR(LOADER, "EDAT: Network license not supported!");
delete NPD;
delete EDAT;
return 1;
}
if (verbose)
{
int i;
LOG_NOTICE(LOADER, "DEVKLIC: ");
for (i = 0; i < 0x10; i++)
LOG_NOTICE(LOADER, "%02X", devklic[i]);
LOG_NOTICE(LOADER, "RIF KEY: ");
for (i = 0; i < 0x10; i++)
LOG_NOTICE(LOADER, "%02X", rifkey[i]);
}
} }
LOG_NOTICE(LOADER, "EDAT: Parsing data...\n"); if (verbose)
{
int i;
LOG_NOTICE(LOADER, "DECRYPTION KEY: ");
for (i = 0; i < 0x10; i++)
LOG_NOTICE(LOADER, "%02X", key[i]);
}
LOG_NOTICE(LOADER, "EDAT: Parsing data...");
if (check_data(key, EDAT, NPD, input, verbose)) if (check_data(key, EDAT, NPD, input, verbose))
LOG_ERROR(LOADER, "EDAT: Data parsing failed!\n"); {
LOG_ERROR(LOADER, "EDAT: Data parsing failed!");
delete NPD;
delete EDAT;
return 1;
}
else else
LOG_SUCCESS(LOADER, "EDAT: Data successfully parsed!\n"); LOG_NOTICE(LOADER, "EDAT: Data successfully parsed!");
printf("\n"); LOG_NOTICE(LOADER, "EDAT: Decrypting data...");
LOG_NOTICE(LOADER, "EDAT: Decrypting data...\n");
if (decrypt_data(input, output, EDAT, NPD, key, verbose)) if (decrypt_data(input, output, EDAT, NPD, key, verbose))
{
LOG_ERROR(LOADER, "EDAT: Data decryption failed!"); LOG_ERROR(LOADER, "EDAT: Data decryption failed!");
delete NPD;
delete EDAT;
return 1;
}
else else
LOG_SUCCESS(LOADER, "EDAT: Data successfully decrypted!"); LOG_NOTICE(LOADER, "EDAT: Data successfully decrypted!");
delete NPD; delete NPD;
delete EDAT; delete EDAT;
@ -655,21 +854,21 @@ int DecryptEDAT(const std::string& input_file_name, const std::string& output_fi
memcpy(devklic, custom_klic, 0x10); memcpy(devklic, custom_klic, 0x10);
else else
{ {
LOG_ERROR(LOADER, "EDAT: Invalid custom klic!\n"); LOG_ERROR(LOADER, "EDAT: Invalid custom klic!");
return -1; return -1;
} }
break; break;
} }
default: default:
LOG_ERROR(LOADER, "EDAT: Invalid mode!\n"); LOG_ERROR(LOADER, "EDAT: Invalid mode!");
return -1; return -1;
} }
// Check the input/output files. // Check the input/output files.
if (!input.IsOpened() || !output.IsOpened()) if (!input.IsOpened() || !output.IsOpened())
{ {
LOG_ERROR(LOADER, "EDAT: Failed to open files!\n"); LOG_ERROR(LOADER, "EDAT: Failed to open files!");
return -1; return -1;
} }
@ -692,11 +891,11 @@ int DecryptEDAT(const std::string& input_file_name, const std::string& output_fi
input.Close(); input.Close();
output.Close(); output.Close();
rRemoveFile(output_file_name); rRemoveFile(output_file_name);
return 0; return -1;
} }
// Cleanup. // Cleanup.
input.Close(); input.Close();
output.Close(); output.Close();
return 0; return 0;
} }

View File

@ -1,9 +1,12 @@
#pragma once #pragma once
#include <stdio.h>
#include <string.h>
#include "utils.h"
#define SDAT_FLAG 0x01000000 #define SDAT_FLAG 0x01000000
#define EDAT_COMPRESSED_FLAG 0x00000001 #define EDAT_COMPRESSED_FLAG 0x00000001
#define EDAT_FLAG_0x02 0x00000002 #define EDAT_FLAG_0x02 0x00000002
#define EDAT_FLAG_0x04 0x00000004
#define EDAT_ENCRYPTED_KEY_FLAG 0x00000008 #define EDAT_ENCRYPTED_KEY_FLAG 0x00000008
#define EDAT_FLAG_0x10 0x00000010 #define EDAT_FLAG_0x10 0x00000010
#define EDAT_FLAG_0x20 0x00000020 #define EDAT_FLAG_0x20 0x00000020
@ -28,6 +31,6 @@ typedef struct
int flags; int flags;
int block_size; int block_size;
unsigned long long file_size; unsigned long long file_size;
} EDAT_SDAT_HEADER; } EDAT_HEADER;
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); 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);

View File

@ -813,10 +813,10 @@ bool SELFDecrypter::GetKeyFromRap(u8 *content_id, u8 *npdrm_key)
u8 rap_key[0x10]; u8 rap_key[0x10];
memset(rap_key, 0, 0x10); memset(rap_key, 0, 0x10);
// Try to find a matching RAP file under dev_usb000. // Try to find a matching RAP file under exdata folder.
std::string ci_str((const char *)content_id); std::string ci_str((const char *)content_id);
// TODO: This shouldn't use current dir std::string pf_str("00000001"); // TODO: Allow multiple profiles. Use default for now.
std::string rap_path("./dev_usb000/" + ci_str + ".rap"); std::string rap_path("dev_hdd0/home/" + pf_str + "/exdata/" + ci_str + ".rap");
// Check if we have a valid RAP file. // Check if we have a valid RAP file.
if (!rExists(rap_path)) if (!rExists(rap_path))

View File

@ -1,9 +1,13 @@
#include "stdafx.h" // Copyright (C) 2014 Hykem <hykem@hotmail.com>
#include "aes.h" // Licensed under the terms of the GNU GPL, version 3
#include "sha1.h" // http://www.gnu.org/licenses/gpl-3.0.txt
#include "utils.h"
// Endian swap auxiliary functions. #include "stdafx.h"
#include "utils.h"
#include <stdio.h>
#include <time.h>
// Auxiliary functions (endian swap, xor and prng).
u16 swap16(u16 i) u16 swap16(u16 i)
{ {
return ((i & 0xFF00) >> 8) | ((i & 0xFF) << 8); return ((i & 0xFF00) >> 8) | ((i & 0xFF) << 8);
@ -22,7 +26,7 @@ u64 swap64(u64 i)
((i & 0x00ff000000000000) >> 40) | ((i & 0xff00000000000000) >> 56); ((i & 0x00ff000000000000) >> 40) | ((i & 0xff00000000000000) >> 56);
} }
void xor_(unsigned char *dest, unsigned char *src1, unsigned char *src2, int size) void xor(unsigned char *dest, unsigned char *src1, unsigned char *src2, int size)
{ {
int i; int i;
for(i = 0; i < size; i++) for(i = 0; i < size; i++)
@ -31,10 +35,24 @@ void xor_(unsigned char *dest, unsigned char *src1, unsigned char *src2, int siz
} }
} }
void prng(unsigned char *dest, int size)
{
unsigned char *buffer = new unsigned char[size];
srand((u32)time(0));
int i;
for(i = 0; i < size; i++)
buffer[i] = (unsigned char)(rand() & 0xFF);
memcpy(dest, buffer, size);
delete[] buffer;
}
// Hex string conversion auxiliary functions. // Hex string conversion auxiliary functions.
u64 hex_to_u64(const char* hex_str) u64 hex_to_u64(const char* hex_str)
{ {
u32 length = (u32)strlen(hex_str); u32 length = (u32) strlen(hex_str);
u64 tmp = 0; u64 tmp = 0;
u64 result = 0; u64 result = 0;
char c; char c;
@ -56,19 +74,19 @@ u64 hex_to_u64(const char* hex_str)
return result; return result;
} }
void hex_to_bytes(unsigned char *data, const char *hex_str) void hex_to_bytes(unsigned char *data, const char *hex_str, unsigned int str_length)
{ {
u32 str_length = (u32)strlen(hex_str); u32 strn_length = (str_length > 0) ? str_length : (u32) strlen(hex_str);
u32 data_length = str_length / 2; u32 data_length = strn_length / 2;
char tmp_buf[3] = {0, 0, 0}; char tmp_buf[3] = {0, 0, 0};
// Don't convert if the string length is odd. // Don't convert if the string length is odd.
if (!(str_length % 2)) if (!(strn_length % 2))
{ {
u8 *out = (u8 *) malloc (str_length * sizeof(u8)); u8 *out = (u8 *)malloc(strn_length * sizeof(u8));
u8 *pos = out; u8 *pos = out;
while (str_length--) while (strn_length--)
{ {
tmp_buf[0] = *hex_str++; tmp_buf[0] = *hex_str++;
tmp_buf[1] = *hex_str++; tmp_buf[1] = *hex_str++;
@ -81,6 +99,23 @@ void hex_to_bytes(unsigned char *data, const char *hex_str)
} }
} }
bool is_hex(const char* hex_str, unsigned int str_length)
{
static const char hex_chars[] = "0123456789abcdefABCDEF";
if (hex_str == NULL)
return false;
unsigned int i;
for (i = 0; i < str_length; i++)
{
if (strchr(hex_chars, hex_str[i]) == 0)
return false;
}
return true;
}
// Crypto functions (AES128-CBC, AES128-ECB, SHA1-HMAC and AES-CMAC). // Crypto functions (AES128-CBC, AES128-ECB, SHA1-HMAC and AES-CMAC).
void aescbc128_decrypt(unsigned char *key, unsigned char *iv, unsigned char *in, unsigned char *out, int len) void aescbc128_decrypt(unsigned char *key, unsigned char *iv, unsigned char *in, unsigned char *out, int len)
{ {
@ -92,6 +127,16 @@ void aescbc128_decrypt(unsigned char *key, unsigned char *iv, unsigned char *in,
memset(iv, 0, 0x10); memset(iv, 0, 0x10);
} }
void aescbc128_encrypt(unsigned char *key, unsigned char *iv, unsigned char *in, unsigned char *out, int len)
{
aes_context ctx;
aes_setkey_enc(&ctx, key, 128);
aes_crypt_cbc(&ctx, AES_ENCRYPT, len, iv, in, out);
// Reset the IV.
memset(iv, 0, 0x10);
}
void aesecb128_encrypt(unsigned char *key, unsigned char *in, unsigned char *out) void aesecb128_encrypt(unsigned char *key, unsigned char *in, unsigned char *out)
{ {
aes_context ctx; aes_context ctx;
@ -99,13 +144,13 @@ void aesecb128_encrypt(unsigned char *key, unsigned char *in, unsigned char *out
aes_crypt_ecb(&ctx, AES_ENCRYPT, in, out); aes_crypt_ecb(&ctx, AES_ENCRYPT, in, out);
} }
bool hmac_hash_compare(unsigned char *key, int key_len, unsigned char *in, int in_len, unsigned char *hash) bool hmac_hash_compare(unsigned char *key, int key_len, unsigned char *in, int in_len, unsigned char *hash, int hash_len)
{ {
unsigned char *out = new unsigned char[key_len]; unsigned char *out = new unsigned char[key_len];
sha1_hmac(key, key_len, in, in_len, out); sha1_hmac(key, key_len, in, in_len, out);
for (int i = 0; i < 0x10; i++) for (int i = 0; i < hash_len; i++)
{ {
if (out[i] != hash[i]) if (out[i] != hash[i])
{ {
@ -119,7 +164,12 @@ bool hmac_hash_compare(unsigned char *key, int key_len, unsigned char *in, int i
return true; return true;
} }
bool cmac_hash_compare(unsigned char *key, int key_len, unsigned char *in, int in_len, unsigned char *hash) void hmac_hash_forge(unsigned char *key, int key_len, unsigned char *in, int in_len, unsigned char *hash)
{
sha1_hmac(key, key_len, in, in_len, hash);
}
bool cmac_hash_compare(unsigned char *key, int key_len, unsigned char *in, int in_len, unsigned char *hash, int hash_len)
{ {
unsigned char *out = new unsigned char[key_len]; unsigned char *out = new unsigned char[key_len];
@ -127,7 +177,7 @@ bool cmac_hash_compare(unsigned char *key, int key_len, unsigned char *in, int i
aes_setkey_enc(&ctx, key, 128); aes_setkey_enc(&ctx, key, 128);
aes_cmac(&ctx, in_len, in, out); aes_cmac(&ctx, in_len, in, out);
for (int i = 0; i < key_len; i++) for (int i = 0; i < hash_len; i++)
{ {
if (out[i] != hash[i]) if (out[i] != hash[i])
{ {
@ -141,9 +191,20 @@ bool cmac_hash_compare(unsigned char *key, int key_len, unsigned char *in, int i
return true; return true;
} }
#include "lz.h" void cmac_hash_forge(unsigned char *key, int key_len, unsigned char *in, int in_len, unsigned char *hash)
// Reverse-engineered custom LempelZivMarkov based compression (unknown variant of LZRC).
int lz_decompress(unsigned char *out, unsigned char *in, unsigned int size)
{ {
return decompress(out,in,size); aes_context ctx;
aes_setkey_enc(&ctx, key, 128);
aes_cmac(&ctx, in_len, in, hash);
} }
char* extract_file_name(const char* file_path, char real_file_name[MAX_PATH])
{
size_t file_path_len = strlen(file_path);
const char* p = strrchr(file_path, '/');
if (!p) p = strrchr(file_path, '\\');
if (p) file_path_len = file_path + file_path_len - p - 1;
strncpy(real_file_name, p ? (p + 1) : file_path, file_path_len + 1);
return real_file_name;
}

View File

@ -1,20 +1,35 @@
#pragma once #pragma once
// Auxiliary functions (endian swap and xor). // Copyright (C) 2014 Hykem <hykem@hotmail.com>
// Licensed under the terms of the GNU GPL, version 3
// http://www.gnu.org/licenses/gpl-3.0.txt
#define MAX_PATH _MAX_PATH
#include <stdlib.h>
#include "aes.h"
#include "sha1.h"
#include "lz.h"
#include "ec.h"
// Auxiliary functions (endian swap, xor, prng and file name).
u16 swap16(u16 i); u16 swap16(u16 i);
u32 swap32(u32 i); u32 swap32(u32 i);
u64 swap64(u64 i); u64 swap64(u64 i);
void xor_(unsigned char *dest, unsigned char *src1, unsigned char *src2, int size); void xor(unsigned char *dest, unsigned char *src1, unsigned char *src2, int size);
void prng(unsigned char *dest, int size);
char* extract_file_name(const char* file_path, char real_file_name[MAX_PATH]);
// Hex string conversion auxiliary functions. // Hex string conversion auxiliary functions.
u64 hex_to_u64(const char* hex_str); u64 hex_to_u64(const char* hex_str);
void hex_to_bytes(unsigned char *data, const char *hex_str); void hex_to_bytes(unsigned char *data, const char *hex_str, unsigned int str_length);
bool is_hex(const char* hex_str, unsigned int str_length);
// Crypto functions (AES128-CBC, AES128-ECB, SHA1-HMAC and AES-CMAC). // Crypto functions (AES128-CBC, AES128-ECB, SHA1-HMAC and AES-CMAC).
void aescbc128_decrypt(unsigned char *key, unsigned char *iv, unsigned char *in, unsigned char *out, int len); void aescbc128_decrypt(unsigned char *key, unsigned char *iv, unsigned char *in, unsigned char *out, int len);
void aescbc128_encrypt(unsigned char *key, unsigned char *iv, unsigned char *in, unsigned char *out, int len);
void aesecb128_encrypt(unsigned char *key, unsigned char *in, unsigned char *out); void aesecb128_encrypt(unsigned char *key, unsigned char *in, unsigned char *out);
bool hmac_hash_compare(unsigned char *key, int key_len, unsigned char *in, int in_len, unsigned char *hash); bool hmac_hash_compare(unsigned char *key, int key_len, unsigned char *in, int in_len, unsigned char *hash, int hash_len);
bool cmac_hash_compare(unsigned char *key, int key_len, unsigned char *in, int in_len, unsigned char *hash); void hmac_hash_forge(unsigned char *key, int key_len, unsigned char *in, int in_len, unsigned char *hash);
bool cmac_hash_compare(unsigned char *key, int key_len, unsigned char *in, int in_len, unsigned char *hash, int hash_len);
// Reverse-engineered custom LempelZivMarkov based compression (unknown variant of LZRC). void cmac_hash_forge(unsigned char *key, int key_len, unsigned char *in, int in_len, unsigned char *hash);
int lz_decompress(unsigned char *out, unsigned char *in, unsigned int size);

View File

@ -108,8 +108,9 @@ int npDrmIsAvailable(u32 k_licensee_addr, vm::ptr<const char> drm_path)
// TODO: These shouldn't use current dir // TODO: These shouldn't use current dir
std::string enc_drm_path = drm_path.get_ptr(); std::string enc_drm_path = drm_path.get_ptr();
std::string dec_drm_path = "/dev_hdd1/" + titleID + "/" + drm_file_name; std::string dec_drm_path = "/dev_hdd1/cache/" + drm_file_name;
std::string rap_path = "/dev_usb000/"; std::string pf_str("00000001"); // TODO: Allow multiple profiles. Use default for now.
std::string rap_path("../dev_hdd0/home/" + pf_str + "/exdata/");
// Search dev_usb000 for a compatible RAP file. // Search dev_usb000 for a compatible RAP file.
vfsDir *raps_dir = new vfsDir(rap_path); vfsDir *raps_dir = new vfsDir(rap_path);
@ -128,19 +129,19 @@ int npDrmIsAvailable(u32 k_licensee_addr, vm::ptr<const char> drm_path)
} }
} }
// Create a new directory under dev_hdd1/titleID to hold the decrypted data.
// TODO: These shouldn't use current dir
std::string tmp_dir = "./dev_hdd1/" + titleID;
if (!rExists(tmp_dir))
rMkdir("./dev_hdd1/" + titleID);
// Decrypt this EDAT using the supplied k_licensee and matching RAP file. // Decrypt this EDAT using the supplied k_licensee and matching RAP file.
std::string enc_drm_path_local, dec_drm_path_local, rap_path_local; std::string enc_drm_path_local, dec_drm_path_local, rap_path_local;
Emu.GetVFS().GetDevice(enc_drm_path, enc_drm_path_local); Emu.GetVFS().GetDevice(enc_drm_path, enc_drm_path_local);
Emu.GetVFS().GetDevice(dec_drm_path, dec_drm_path_local); Emu.GetVFS().GetDevice(dec_drm_path, dec_drm_path_local);
Emu.GetVFS().GetDevice(rap_path, rap_path_local); Emu.GetVFS().GetDevice(rap_path, rap_path_local);
DecryptEDAT(enc_drm_path_local, dec_drm_path_local, 8, rap_path_local, k_licensee, false); if (DecryptEDAT(enc_drm_path_local, dec_drm_path_local, 8, rap_path_local, k_licensee, false) >= 0)
{
// If decryption succeeds, replace the encrypted file with it.
rRemoveFile(enc_drm_path_local);
rRename(dec_drm_path_local, enc_drm_path_local);
}
return CELL_OK; return CELL_OK;
} }

View File

@ -39,6 +39,7 @@
<ClCompile Include="..\Utilities\StrFmt.cpp" /> <ClCompile Include="..\Utilities\StrFmt.cpp" />
<ClCompile Include="..\Utilities\Thread.cpp" /> <ClCompile Include="..\Utilities\Thread.cpp" />
<ClCompile Include="Crypto\aes.cpp" /> <ClCompile Include="Crypto\aes.cpp" />
<ClCompile Include="Crypto\ec.cpp" />
<ClCompile Include="Crypto\key_vault.cpp" /> <ClCompile Include="Crypto\key_vault.cpp" />
<ClCompile Include="Crypto\lz.cpp"> <ClCompile Include="Crypto\lz.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug - MemLeak|Win32'">NotUsing</PrecompiledHeader> <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug - MemLeak|Win32'">NotUsing</PrecompiledHeader>
@ -244,6 +245,7 @@
<ClInclude Include="..\Utilities\Thread.h" /> <ClInclude Include="..\Utilities\Thread.h" />
<ClInclude Include="..\Utilities\Timer.h" /> <ClInclude Include="..\Utilities\Timer.h" />
<ClInclude Include="Crypto\aes.h" /> <ClInclude Include="Crypto\aes.h" />
<ClInclude Include="Crypto\ec.h" />
<ClInclude Include="Crypto\key_vault.h" /> <ClInclude Include="Crypto\key_vault.h" />
<ClInclude Include="Crypto\lz.h" /> <ClInclude Include="Crypto\lz.h" />
<ClInclude Include="Crypto\sha1.h" /> <ClInclude Include="Crypto\sha1.h" />

View File

@ -131,7 +131,7 @@
<ClCompile Include="Emu\SysCalls\Modules\cellAudio.cpp"> <ClCompile Include="Emu\SysCalls\Modules\cellAudio.cpp">
<Filter>Emu\SysCalls\Modules</Filter> <Filter>Emu\SysCalls\Modules</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="Emu\SysCalls\Modules\cellAvconfExt.cpp"> <ClCompile Include="Emu\SysCalls\Modules\cellAvconfExt.cpp">
<Filter>Emu\SysCalls\Modules</Filter> <Filter>Emu\SysCalls\Modules</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="Emu\SysCalls\Modules\cellCamera.cpp"> <ClCompile Include="Emu\SysCalls\Modules\cellCamera.cpp">
@ -350,7 +350,7 @@
<ClCompile Include="Emu\Memory\Memory.cpp"> <ClCompile Include="Emu\Memory\Memory.cpp">
<Filter>Emu\Memory</Filter> <Filter>Emu\Memory</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="Emu\Memory\vm.cpp"> <ClCompile Include="Emu\Memory\vm.cpp">
<Filter>Emu\Memory</Filter> <Filter>Emu\Memory</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="Loader\ELF.cpp"> <ClCompile Include="Loader\ELF.cpp">
@ -623,6 +623,9 @@
<ClCompile Include="..\Utilities\rXml.cpp"> <ClCompile Include="..\Utilities\rXml.cpp">
<Filter>Utilities</Filter> <Filter>Utilities</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="Crypto\ec.cpp">
<Filter>Crypto</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="Crypto\aes.h"> <ClInclude Include="Crypto\aes.h">
@ -727,7 +730,7 @@
<ClInclude Include="Emu\SysCalls\Modules\cellPamf.h"> <ClInclude Include="Emu\SysCalls\Modules\cellPamf.h">
<Filter>Emu\SysCalls\Modules</Filter> <Filter>Emu\SysCalls\Modules</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="Emu\SysCalls\Modules\cellPng.h"> <ClInclude Include="Emu\SysCalls\Modules\cellPng.h">
<Filter>Emu\SysCalls\Modules</Filter> <Filter>Emu\SysCalls\Modules</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="Emu\SysCalls\Modules\cellPngDec.h"> <ClInclude Include="Emu\SysCalls\Modules\cellPngDec.h">
@ -1222,5 +1225,8 @@
<ClInclude Include="Emu\SysCalls\SyncPrimitivesManager.h"> <ClInclude Include="Emu\SysCalls\SyncPrimitivesManager.h">
<Filter>Emu\SysCalls</Filter> <Filter>Emu\SysCalls</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="Crypto\ec.h">
<Filter>Crypto</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
</Project> </Project>