diff --git a/bin/dev_hdd0/home/00000001/exdata/.gitignore b/bin/dev_hdd0/home/00000001/exdata/.gitignore new file mode 100644 index 0000000000..8f6c3111a2 --- /dev/null +++ b/bin/dev_hdd0/home/00000001/exdata/.gitignore @@ -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 diff --git a/rpcs3/Crypto/ec.cpp b/rpcs3/Crypto/ec.cpp new file mode 100644 index 0000000000..1e8dd23b6e --- /dev/null +++ b/rpcs3/Crypto/ec.cpp @@ -0,0 +1,548 @@ +// Copyright 2007,2008,2010 Segher Boessenkool +// 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); +} \ No newline at end of file diff --git a/rpcs3/Crypto/ec.h b/rpcs3/Crypto/ec.h new file mode 100644 index 0000000000..6b00f51971 --- /dev/null +++ b/rpcs3/Crypto/ec.h @@ -0,0 +1,14 @@ +#pragma once + +// Copyright (C) 2014 Hykem +// Licensed under the terms of the GNU GPL, version 3 +// http://www.gnu.org/licenses/gpl-3.0.txt + +#include +#include + +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); diff --git a/rpcs3/Crypto/key_vault.cpp b/rpcs3/Crypto/key_vault.cpp index 5cbc04ac02..04a949c1be 100644 --- a/rpcs3/Crypto/key_vault.cpp +++ b/rpcs3/Crypto/key_vault.cpp @@ -8,10 +8,10 @@ SELF_KEY::SELF_KEY(u64 ver, u16 rev, u32 type, const std::string& e, const std:: version = ver; revision = rev; self_type = type; - hex_to_bytes(erk, e.c_str()); - hex_to_bytes(riv, r.c_str()); - hex_to_bytes(pub, pb.c_str()); - hex_to_bytes(priv, pr.c_str()); + hex_to_bytes(erk, e.c_str(), 0); + hex_to_bytes(riv, r.c_str(), 0); + hex_to_bytes(pub, pb.c_str(), 0); + hex_to_bytes(priv, pr.c_str(), 0); curve_type = ct; } diff --git a/rpcs3/Crypto/key_vault.h b/rpcs3/Crypto/key_vault.h index e57cfe5b90..293efeda9c 100644 --- a/rpcs3/Crypto/key_vault.h +++ b/rpcs3/Crypto/key_vault.h @@ -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 }; +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 { std::vector sk_LV0_arr; diff --git a/rpcs3/Crypto/lz.cpp b/rpcs3/Crypto/lz.cpp index ec04981725..86d82d7161 100644 --- a/rpcs3/Crypto/lz.cpp +++ b/rpcs3/Crypto/lz.cpp @@ -4,26 +4,20 @@ #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; *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) { - unsigned int val = *range; - - if (decode_range(range, code, src)) - val *= (*c); - else - val = (val >> 8) * (*c); + decode_range(range, code, src); + + unsigned int val = ((*range) >> 8) * (*c); *c -= ((*c) >> 3); 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; - if (index >= 3) + if (index >= 3) { - decode_bit(range, code, &i, src, ptr + 0x18); // Offset 0x978 - if (index >= 4) + decode_bit(range, code, &i, src, ptr + 0x18); + if (index >= 4) { - decode_bit(range, code, &i, src, ptr + 0x18); // Offset 0x978 + decode_bit(range, code, &i, src, ptr + 0x18); if (index >= 5) { 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; (*range) >>= 1; - if (*code < *range) - i++; + if (*code < *range) + i++; else (*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 - if (index >= 2) + decode_bit(range, code, &i, src, ptr + 0x8); + if (index >= 2) { - decode_bit(range, code, &i, src, ptr + 0x10); // Offset 0x970 + decode_bit(range, code, &i, src, ptr + 0x10); } - } + } return i; } @@ -88,12 +82,12 @@ int decode_word(unsigned char *ptr, int index, int *bit_flag, unsigned int *rang int i = 1; index /= 8; - if (index >= 3) + if (index >= 3) { - decode_bit(range, code, &i, src, ptr); // Offset 0x8A8 - if (index >= 4) + decode_bit(range, code, &i, src, ptr + 4); + if (index >= 4) { - decode_bit(range, code, &i, src, ptr); // Offset 0x8A8 + decode_bit(range, code, &i, src, ptr + 4); if (index >= 5) { 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; (*range) >>= 1; - if (*code < *range) - i++; + if (*code < *range) + i++; else (*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 - if (index >= 2) + decode_bit(range, code, &i, src, ptr + 1); + if (index >= 2) { - decode_bit(range, code, &i, src, ptr + 1); // Offset 0x8A8 + 1 + decode_bit(range, code, &i, src, ptr + 2); } - } + } return i; } @@ -128,7 +122,7 @@ int decompress(unsigned char *out, unsigned char *in, unsigned int size) { int result; - unsigned char *tmp = new unsigned char[0xA70]; + unsigned char *tmp = new unsigned char[0xCC8]; int offset = 0; int bit_flag = 0; @@ -159,11 +153,11 @@ int decompress(unsigned char *out, unsigned char *in, unsigned int size) else { // Set up a temporary buffer (sliding window). - memset(tmp, 0x80, 0xA60); + memset(tmp, 0x80, 0xCA8); while (1) { - // Start reading at 0x920. - tmp_sect1 = tmp + offset + 0x920; + // Start reading at 0xB68. + tmp_sect1 = tmp + offset + 0xB68; if (!decode_bit(&range, &code, 0, &in, tmp_sect1)) // Raw char. { // 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; // Identify the data length bit field. - do { - tmp_sect1 += 8; + do + { + tmp_sect1 += 8; bit_flag = decode_bit(&range, &code, 0, &in, tmp_sect1); index += bit_flag; } while ((bit_flag != 0) && (index < 6)); - // Default block size is 0x40. - int b_size = 0x40; + // Default block size is 0x160. + int b_size = 0x160; tmp_sect2 = tmp + index + 0x7F1; // 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. - int sect = (index << 5) | (((((start - out)) << index) & 3) << 3) | (offset & 7); - tmp_sect1 = tmp + 0x960 + sect; + int sect = (index << 5) | (((((int)(start - out)) << index) & 3) << 3) | (offset & 7); + tmp_sect1 = tmp + 0xBA8 + sect; // Decode the data length (8 bit fields). data_length = decode_number(tmp_sect1, index, &bit_flag, &range, &code, &in); - - // If we got valid parameters, seek to find data offset. - if ((data_length != 3) && ((index > 0) || (bit_flag != 0))) { - tmp_sect2 += 0x38; - b_size = 0x80; // Block size is now 0x80. - } - } else { + if (data_length == 0xFF) return (start - out); // End of stream. + } + else + { // Assume one byte of advance. 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 shift = 1; // Identify the data offset bit field. - do { + do + { diff = (shift << 4) - b_size; bit_flag = decode_bit(&range, &code, &shift, &in, tmp_sect2 + (shift << 3)); } while (diff < 0); // 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. if (bit_flag == 0) diff -= 8; // Locate section. - tmp_sect3 = tmp + 0x8A8 + diff; + tmp_sect3 = tmp + 0x928 + diff; // Decode the data offset (1 bit fields). data_offset = decode_word(tmp_sect3, diff, &bit_flag, &range, &code, &in); - } else { + } + else + { // Assume one byte of advance. 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; // Copy data. - do { + do + { *start++ = *buf_start++; } while (start < buf_end); @@ -271,4 +274,4 @@ int decompress(unsigned char *out, unsigned char *in, unsigned int size) } delete[] tmp; return result; -} +} \ No newline at end of file diff --git a/rpcs3/Crypto/lz.h b/rpcs3/Crypto/lz.h index c27192624c..f84415623d 100644 --- a/rpcs3/Crypto/lz.h +++ b/rpcs3/Crypto/lz.h @@ -1,8 +1,15 @@ #pragma once + +// Copyright (C) 2014 Hykem +// Licensed under the terms of the GNU GPL, version 3 +// http://www.gnu.org/licenses/gpl-3.0.txt + +// Reverse-engineered custom Lempel–Ziv–Markov based compression. + #include -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_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 decompress(unsigned char *out, unsigned char *in, unsigned int size); +int decompress(unsigned char *out, unsigned char *in, unsigned int size); \ No newline at end of file diff --git a/rpcs3/Crypto/sha1.h b/rpcs3/Crypto/sha1.h index 84d4bd80d2..f616f468b8 100644 --- a/rpcs3/Crypto/sha1.h +++ b/rpcs3/Crypto/sha1.h @@ -1,4 +1,5 @@ #pragma once + /** * \file sha1.h * diff --git a/rpcs3/Crypto/unedat.cpp b/rpcs3/Crypto/unedat.cpp index 7cd9604ac6..21ccd2914f 100644 --- a/rpcs3/Crypto/unedat.cpp +++ b/rpcs3/Crypto/unedat.cpp @@ -1,5 +1,4 @@ #include "stdafx.h" -#include "utils.h" #include "key_vault.h" #include "unedat.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) { - int mode = (int) (crypto_mode & 0xF0000000); + int mode = (int)(crypto_mode & 0xF0000000); switch (mode) { case 0x10000000: // 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) { - int mode = (int) (hash_mode & 0xF0000000); + int mode = (int)(hash_mode & 0xF0000000); switch (mode) { case 0x10000000: // 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. unsigned char key_final[0x10] = {}; @@ -77,29 +76,30 @@ bool crypto(int hash_mode, int crypto_mode, int version, unsigned char *in, unsi } else { - LOG_ERROR(LOADER, "EDAT: Unknown crypto algorithm!\n"); + LOG_ERROR(LOADER, "EDAT: Unknown crypto algorithm!"); return false; } - + 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 { - 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 { - 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 { - LOG_ERROR(LOADER, "EDAT: Unknown hashing algorithm!\n"); + LOG_ERROR(LOADER, "EDAT: Unknown hashing algorithm!"); return false; } } +// EDAT/SDAT functions. unsigned char* dec_section(unsigned char* metadata) { unsigned char *dec = new unsigned char[0x10]; @@ -135,11 +135,11 @@ unsigned char* get_block_key(int block, NPD_HEADER *npd) 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) +// EDAT/SDAT decryption. +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. - 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_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 *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] = {}; // 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); - unsigned char hash_result[0x10]; - long offset; - int length = 0; - int compression_end = 0; + memset(hash_result, 0, 0x14); - 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; + metadata_sec_offset = metadata_offset + (unsigned long long) i * metadata_section_size; + in->Seek(metadata_sec_offset); + 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); } 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); + metadata_sec_offset = metadata_offset + (unsigned long long) i * (metadata_section_size + length); + in->Seek(metadata_sec_offset); unsigned char metadata[0x20]; + memset(metadata, 0, 0x20); in->Read(metadata, 0x20); + memcpy(hash_result, metadata, 0x14); // 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]); - } + int j; + 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; 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 { + metadata_sec_offset = metadata_offset + (unsigned long long) i * metadata_section_size; + in->Seek(metadata_sec_offset); + 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; - + 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. int pad_length = length; - length = (int) ((pad_length + 0xF) & 0xFFFFFFF0); - in->Seek(offset); + length = (int)((pad_length + 0xF) & 0xFFFFFFF0); // 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]; + memset(enc_data, 0, length); + memset(dec_data, 0, length); + memset(hash, 0, 0x10); + memset(key_result, 0, 0x10); + + in->Seek(offset); in->Read(enc_data, length); - + // Generate a key for the current block. 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; } - if ((edat->flags & EDAT_DEBUG_DATA_FLAG) != 0) + if ((edat->flags & EDAT_DEBUG_DATA_FLAG) != 0) { // Reset the flags. 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 = (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); + 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. @@ -271,28 +302,28 @@ int decrypt_data(rFile *in, rFile *out, EDAT_SDAT_HEADER *edat, NPD_HEADER *npd, memset(decomp_data, 0, decomp_size); if (verbose) - LOG_NOTICE(LOADER, "EDAT: Decompressing...\n"); - - int res = lz_decompress(decomp_data, dec_data, decomp_size); + LOG_NOTICE(LOADER, "EDAT: Decompressing data..."); + + int res = 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); + LOG_NOTICE(LOADER, "EDAT: Compressed block size: %d", pad_length); + LOG_NOTICE(LOADER, "EDAT: Decompressed block size: %d", res); } edat->file_size -= res; - if (edat->file_size == 0) + if (edat->file_size == 0) { if (res < 0) { - LOG_ERROR(LOADER, "EDAT: Decompression failed!\n"); + LOG_ERROR(LOADER, "EDAT: Decompression failed!"); return 1; } else - LOG_SUCCESS(LOADER, "EDAT: Data successfully decompressed!\n"); + LOG_NOTICE(LOADER, "EDAT: Successfully decompressed!"); } delete[] decomp_data; @@ -309,158 +340,262 @@ int decrypt_data(rFile *in, rFile *out, EDAT_SDAT_HEADER *edat, NPD_HEADER *npd, 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) - return false; + f->Seek(0); + 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"); - return false; + LOG_ERROR(LOADER, "EDAT: Bad header flags!"); + 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"); - return false; + LOG_ERROR(LOADER, "EDAT: Bad header flags!"); + return 1; } } - else if (npd->version == 3 || npd->version == 4) + else if ((npd->version == 3) || (npd->version == 4)) { if (edat->flags & 0x7EFFFFC0) { - LOG_ERROR(LOADER, "EDAT: Bad header flags!\n"); - return false; + LOG_ERROR(LOADER, "EDAT: Bad header flags!"); + return 1; } } - else if (npd->version > 4) + else { - 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; - + LOG_ERROR(LOADER, "EDAT: Unknown version!"); return 1; } // Read in the file header. 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. 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; + + if (verbose) + LOG_WARNING(LOADER, "EDAT: DEBUG data detected!"); } // Setup header key and iv buffers. - unsigned char header_key[0x10] = {}; - unsigned char header_iv[0x10] = {}; + unsigned char header_key[0x10]; + unsigned char header_iv[0x10]; + memset(header_key, 0, 0x10); + memset(header_iv, 0, 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 (!decrypt(hash_mode, crypto_mode, (npd->version == 4), header, empty_header, 0xA0, header_key, header_iv, key, header_hash)) { 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. - 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)) { - LOG_WARNING(LOADER, "EDAT: COMPRESSED data detected!\n"); - metadata_section_size = 0x20; + if (verbose) + LOG_WARNING(LOADER, "EDAT: COMPRESSED data detected!"); } - int block_num = (int) ((edat->file_size + edat->block_size - 1) / edat->block_size); - int bytes_read = 0; + int block_num = (int)((edat->file_size + edat->block_size - 1) / edat->block_size); 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) { // 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]; + f->Seek(metadata_section_offset); // 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); - } + f->Read(metadata + bytes_read, metadata_section_size); // Adjust sizes. - bytes_read += block_size; - bytes_to_read -= block_size; + bytes_read += metadata_section_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. - delete[] header; - delete[] tmp; - delete[] hash_result; + delete[] metadata; + delete[] empty_metadata; 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 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 dev[0x60]; 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 + 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); + // Build the dev buffer (first 0x60 bytes of NPD header in big-endian). + 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 (title_hash_result) - LOG_SUCCESS(LOADER, "EDAT: NPD title hash is valid!\n"); - else - LOG_WARNING(LOADER, "EDAT: NPD title hash is invalid!\n"); + LOG_NOTICE(LOADER, "EDAT: NPD title hash is valid!"); + else + LOG_WARNING(LOADER, "EDAT: NPD title hash is invalid!"); } // 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 (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 { // 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. - 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 (dev_hash_result) - LOG_SUCCESS(LOADER, "EDAT: NPD dev hash is valid!\n"); - else - LOG_WARNING(LOADER, "EDAT: NPD dev hash is invalid!\n"); + LOG_NOTICE(LOADER, "EDAT: NPD dev hash is valid!"); + else + LOG_WARNING(LOADER, "EDAT: NPD dev hash is invalid!"); } } 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) { // Setup NPD and EDAT/SDAT structs. 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. char npd_header[0x80]; char edat_header[0x10]; - input->Read(npd_header, 0x80); - input->Read(edat_header, 0x10); - + input->Read(npd_header, sizeof(npd_header)); + input->Read(edat_header, sizeof(edat_header)); + memcpy(NPD->magic, npd_header, 4); NPD->version = swap32(*(int*)&npd_header[4]); 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]); 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); delete NPD; @@ -537,41 +677,65 @@ bool extract_data(rFile *input, rFile *output, const char* input_file_name, unsi 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"); + LOG_NOTICE(LOADER, "NPD HEADER"); + LOG_NOTICE(LOADER, "NPD version: %d", NPD->version); + LOG_NOTICE(LOADER, "NPD license: %d", NPD->license); + LOG_NOTICE(LOADER, "NPD type: %d", NPD->type); } // Set decryption key. unsigned char key[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"); - xor_(key, NPD->dev_hash, SDAT_KEY, 0x10); + if (verbose) + { + 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 { - // Perform header validation (optional step). - validate_data(input_file_name, devklic, NPD, verbose); + if (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); - 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); // Make sure we don't have an empty RIF key. int i, test = 0; - for(i = 0; i < 0x10; i++) + for (i = 0; i < 0x10; i++) { if (key[i] != 0) { @@ -579,30 +743,65 @@ bool extract_data(rFile *input, rFile *output, const char* input_file_name, unsi break; } } - + 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 EDAT; 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)) - LOG_ERROR(LOADER, "EDAT: Data parsing failed!\n"); + { + LOG_ERROR(LOADER, "EDAT: Data parsing failed!"); + delete NPD; + delete EDAT; + return 1; + } else - LOG_SUCCESS(LOADER, "EDAT: Data successfully parsed!\n"); + LOG_NOTICE(LOADER, "EDAT: Data successfully parsed!"); - printf("\n"); - - LOG_NOTICE(LOADER, "EDAT: Decrypting data...\n"); + LOG_NOTICE(LOADER, "EDAT: Decrypting data..."); if (decrypt_data(input, output, EDAT, NPD, key, verbose)) + { LOG_ERROR(LOADER, "EDAT: Data decryption failed!"); + delete NPD; + delete EDAT; + return 1; + } else - LOG_SUCCESS(LOADER, "EDAT: Data successfully decrypted!"); + LOG_NOTICE(LOADER, "EDAT: Data successfully decrypted!"); delete NPD; delete EDAT; @@ -655,21 +854,21 @@ int DecryptEDAT(const std::string& input_file_name, const std::string& output_fi memcpy(devklic, custom_klic, 0x10); else { - LOG_ERROR(LOADER, "EDAT: Invalid custom klic!\n"); + LOG_ERROR(LOADER, "EDAT: Invalid custom klic!"); return -1; } break; } default: - LOG_ERROR(LOADER, "EDAT: Invalid mode!\n"); + LOG_ERROR(LOADER, "EDAT: Invalid mode!"); return -1; } // Check the input/output files. if (!input.IsOpened() || !output.IsOpened()) { - LOG_ERROR(LOADER, "EDAT: Failed to open files!\n"); + LOG_ERROR(LOADER, "EDAT: Failed to open files!"); return -1; } @@ -692,11 +891,11 @@ int DecryptEDAT(const std::string& input_file_name, const std::string& output_fi input.Close(); output.Close(); rRemoveFile(output_file_name); - return 0; + return -1; } // Cleanup. input.Close(); output.Close(); return 0; -} +} \ No newline at end of file diff --git a/rpcs3/Crypto/unedat.h b/rpcs3/Crypto/unedat.h index 7efc8b3808..f1a9f81ab5 100644 --- a/rpcs3/Crypto/unedat.h +++ b/rpcs3/Crypto/unedat.h @@ -1,9 +1,12 @@ #pragma once +#include +#include +#include "utils.h" + #define SDAT_FLAG 0x01000000 #define EDAT_COMPRESSED_FLAG 0x00000001 #define EDAT_FLAG_0x02 0x00000002 -#define EDAT_FLAG_0x04 0x00000004 #define EDAT_ENCRYPTED_KEY_FLAG 0x00000008 #define EDAT_FLAG_0x10 0x00000010 #define EDAT_FLAG_0x20 0x00000020 @@ -28,6 +31,6 @@ typedef struct int flags; int block_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); \ No newline at end of file diff --git a/rpcs3/Crypto/unself.cpp b/rpcs3/Crypto/unself.cpp index 61422f5328..67ef26a4b1 100644 --- a/rpcs3/Crypto/unself.cpp +++ b/rpcs3/Crypto/unself.cpp @@ -813,10 +813,10 @@ bool SELFDecrypter::GetKeyFromRap(u8 *content_id, u8 *npdrm_key) u8 rap_key[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); - // TODO: This shouldn't use current dir - std::string rap_path("./dev_usb000/" + ci_str + ".rap"); + std::string pf_str("00000001"); // TODO: Allow multiple profiles. Use default for now. + std::string rap_path("dev_hdd0/home/" + pf_str + "/exdata/" + ci_str + ".rap"); // Check if we have a valid RAP file. if (!rExists(rap_path)) diff --git a/rpcs3/Crypto/utils.cpp b/rpcs3/Crypto/utils.cpp index 45a71222e3..8cdf567c88 100644 --- a/rpcs3/Crypto/utils.cpp +++ b/rpcs3/Crypto/utils.cpp @@ -1,9 +1,13 @@ -#include "stdafx.h" -#include "aes.h" -#include "sha1.h" -#include "utils.h" +// Copyright (C) 2014 Hykem +// Licensed under the terms of the GNU GPL, version 3 +// http://www.gnu.org/licenses/gpl-3.0.txt -// Endian swap auxiliary functions. +#include "stdafx.h" +#include "utils.h" +#include +#include + +// Auxiliary functions (endian swap, xor and prng). u16 swap16(u16 i) { return ((i & 0xFF00) >> 8) | ((i & 0xFF) << 8); @@ -22,7 +26,7 @@ u64 swap64(u64 i) ((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; 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. u64 hex_to_u64(const char* hex_str) { - u32 length = (u32)strlen(hex_str); + u32 length = (u32) strlen(hex_str); u64 tmp = 0; u64 result = 0; char c; @@ -56,19 +74,19 @@ u64 hex_to_u64(const char* hex_str) 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 data_length = str_length / 2; + u32 strn_length = (str_length > 0) ? str_length : (u32) strlen(hex_str); + u32 data_length = strn_length / 2; char tmp_buf[3] = {0, 0, 0}; // 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; - while (str_length--) + while (strn_length--) { tmp_buf[0] = *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). 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); } +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) { 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); } -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]; 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]) { @@ -119,7 +164,12 @@ bool hmac_hash_compare(unsigned char *key, int key_len, unsigned char *in, int i 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]; @@ -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_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]) { @@ -141,9 +191,20 @@ bool cmac_hash_compare(unsigned char *key, int key_len, unsigned char *in, int i return true; } -#include "lz.h" -// Reverse-engineered custom Lempel–Ziv–Markov based compression (unknown variant of LZRC). -int lz_decompress(unsigned char *out, unsigned char *in, unsigned int size) +void cmac_hash_forge(unsigned char *key, int key_len, unsigned char *in, int in_len, unsigned char *hash) { - 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; +} \ No newline at end of file diff --git a/rpcs3/Crypto/utils.h b/rpcs3/Crypto/utils.h index 4ae1466607..34041d8645 100644 --- a/rpcs3/Crypto/utils.h +++ b/rpcs3/Crypto/utils.h @@ -1,20 +1,35 @@ #pragma once -// Auxiliary functions (endian swap and xor). +// Copyright (C) 2014 Hykem +// 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 +#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); u32 swap32(u32 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. 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). 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); -bool hmac_hash_compare(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); - -// Reverse-engineered custom Lempel–Ziv–Markov based compression (unknown variant of LZRC). -int lz_decompress(unsigned char *out, unsigned char *in, unsigned int size); +bool hmac_hash_compare(unsigned char *key, int key_len, unsigned char *in, int in_len, unsigned char *hash, int hash_len); +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); +void cmac_hash_forge(unsigned char *key, int key_len, unsigned char *in, int in_len, unsigned char *hash); \ No newline at end of file diff --git a/rpcs3/Emu/SysCalls/Modules/sceNp.cpp b/rpcs3/Emu/SysCalls/Modules/sceNp.cpp index 35c8126587..523c81288f 100644 --- a/rpcs3/Emu/SysCalls/Modules/sceNp.cpp +++ b/rpcs3/Emu/SysCalls/Modules/sceNp.cpp @@ -108,8 +108,9 @@ int npDrmIsAvailable(u32 k_licensee_addr, vm::ptr drm_path) // TODO: These shouldn't use current dir std::string enc_drm_path = drm_path.get_ptr(); - std::string dec_drm_path = "/dev_hdd1/" + titleID + "/" + drm_file_name; - std::string rap_path = "/dev_usb000/"; + std::string dec_drm_path = "/dev_hdd1/cache/" + drm_file_name; + 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. vfsDir *raps_dir = new vfsDir(rap_path); @@ -128,19 +129,19 @@ int npDrmIsAvailable(u32 k_licensee_addr, vm::ptr 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. 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(dec_drm_path, dec_drm_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; } diff --git a/rpcs3/emucore.vcxproj b/rpcs3/emucore.vcxproj index 0280445ed3..a37243a656 100644 --- a/rpcs3/emucore.vcxproj +++ b/rpcs3/emucore.vcxproj @@ -39,6 +39,7 @@ + NotUsing @@ -244,6 +245,7 @@ + diff --git a/rpcs3/emucore.vcxproj.filters b/rpcs3/emucore.vcxproj.filters index 907847d5ce..83eee10346 100644 --- a/rpcs3/emucore.vcxproj.filters +++ b/rpcs3/emucore.vcxproj.filters @@ -131,7 +131,7 @@ Emu\SysCalls\Modules - + Emu\SysCalls\Modules @@ -350,7 +350,7 @@ Emu\Memory - + Emu\Memory @@ -623,6 +623,9 @@ Utilities + + Crypto + @@ -727,7 +730,7 @@ Emu\SysCalls\Modules - + Emu\SysCalls\Modules @@ -1222,5 +1225,8 @@ Emu\SysCalls + + Crypto + \ No newline at end of file