Add BNSF decryption [The Idolmaster 2 (X360/PS3)]

This commit is contained in:
bnnm 2020-02-09 10:15:17 +01:00
parent b5767fe2d8
commit 9fd679e57c
13 changed files with 580 additions and 30 deletions

View File

@ -220,6 +220,7 @@ a companion file:
- .hca: .hcakey (8 byte decryption key, a 64-bit number)
- May be followed by 2 byte AWB scramble key for newer HCA
- .fsb: .fsbkey (decryption key, in hex)
- .bnsf: .bnsfkey (decryption key, a string up to 24 chars)
The key file can be ".(ext)key" (for the whole folder), or "(name).(ext)key"
(for a single file). The format is made up to suit vgmstream.
@ -286,6 +287,9 @@ can reduce the number of channels to a playable amount. Note that this type
of downmixing is very generic, not meant to be used when converting to other
formats.
You can also choose which channels to play using *TXTP*. For example, create
a file named `song.adx#C1,2.txtp` to play only channels 1 and 2 from `song.adx`.
## Tagging
Some of vgmstream's plugins support simple read-only tagging via external files.
@ -702,10 +706,11 @@ This list is not complete and many other files are supported.
- .sfl (loop info for .ogg)
- .vgmstream + .vgmstream.pos (FFmpeg formats + loop assist)
- other:
- .adxkey (decryption key for .adx, in start/mult/add format)
- .ahxkey (decryption key for .ahx, in start/mult/add format)
- .hcakey (decryption key for .hca, in HCA Decoder format)
- .fsbkey (decryption key for .fsb, in hex)
- .adxkey (decryption key for .adx)
- .ahxkey (decryption key for .ahx)
- .hcakey (decryption key for .hca)
- .fsbkey (decryption key for .fsb)
- .bnsfkey (decryption key for .bnsf)
- .txtp (per song segment/layer handler and player configurator)
Enjoy! *hcs*

View File

@ -263,6 +263,8 @@ g7221_codec_data* init_g7221(int channel_count, int frame_size);
void decode_g7221(VGMSTREAM* vgmstream, sample_t* outbuf, int channelspacing, int32_t samples_to_do, int channel);
void reset_g7221(g7221_codec_data* data);
void free_g7221(g7221_codec_data* data);
void set_key_g7221(g7221_codec_data* data, const uint8_t* key);
int test_key_g7221(g7221_codec_data* data, off_t start, STREAMFILE* sf);
#endif
#ifdef VGM_USE_G719

View File

@ -91,4 +91,69 @@ void free_g7221(g7221_codec_data* data) {
free(data);
}
void set_key_g7221(g7221_codec_data* data, const uint8_t* key) {
int i;
if (!data) return;
for (i = 0; i < data->channels; i++) {
g7221_set_key(data->ch[i].handle, key);
}
}
/* just in case but very few should be enough */
#define S14_KEY_MIN_TEST_FRAMES 3
#define S14_KEY_MAX_TEST_FRAMES 5
/* Test a number of frames to check if current key decrypts correctly.
* Returns score: <0: error/wrong, 0: unknown/silent, >0: good (closer to 1 is better). */
int test_key_g7221(g7221_codec_data* data, off_t start, STREAMFILE* sf) {
size_t test_frames = 0, current_frame = 0;
int total_score = 0;
int max_frames = (get_streamfile_size(sf) - start) / data->frame_size;
int cur_ch = 0;
/* assumes key was set before this call */
while (test_frames < S14_KEY_MAX_TEST_FRAMES && current_frame < max_frames) {
int score, ok;
size_t bytes;
uint8_t buf[G7221_MAX_FRAME_SIZE];
bytes = read_streamfile(buf, start, data->frame_size, sf);
if (bytes != data->frame_size) {
total_score = -1;
break;
}
ok = g7221_decode_frame(data->ch[cur_ch].handle, buf, data->ch[cur_ch].buffer);
if (!ok) {
total_score = -1;
break;
}
/* alternate channels (decode may affect scores otherwise) */
cur_ch = (cur_ch + 1) % data->channels;
/* good key if it decodes without error, encryption is easily detectable */
score = 1;
test_frames++;
total_score += score;
start += data->frame_size;
}
/* signal best possible score (many perfect frames and few blank frames) */
if (test_frames > S14_KEY_MIN_TEST_FRAMES && total_score > 0 && total_score <= test_frames) {
total_score = 1;
}
/* clean up decoders */
reset_g7221(data);
return total_score;
}
#endif

View File

@ -0,0 +1,326 @@
#include <stdlib.h>
#include "g7221_decoder_aes.h"
/* Namco's NUS AES is just standard AES-192 in ECB mode, so this can be swapped with another lib,
* if more code needs AES. Most implementations out there either use pre-calculated look-up tables,
* or calculate manually every AES op. Namco's code calculates tables first in a slightly different
* layout, so it may be interesting as a sort of doc piece. */
struct s14aes_handle {
/* substitution box look-up table and the inverse */
uint8_t sbox[256];
uint8_t ibox[256];
/* helper for various tables, not too sure about it */
uint8_t xors[256];
/* round constant, though only sets up to 8 */
uint8_t rcon[16];
/* MixColumn(?) LUTs, unlike normal Rijndael which uses 4 tables: Td0a Td0b..., Td1a Td1b..., ...
* layout is: Td0a Td1a Td2a Td3a, Td0b Td0b Td1b Td2b, ... (better for CPU cache?) */
uint32_t tds[256*4];
/* expanded roundkey, actual final key */
uint32_t rk[52];
} ;
#define GET_U32LE(p) (((p)[0] << 0 ) | ((p)[1] << 8 ) | ((p)[2] << 16) | ((p)[3] << 24))
#define GET_U32BE(p) (((p)[3] << 0 ) | ((p)[2] << 8 ) | ((p)[1] << 16) | ((p)[0] << 24))
#define GET_B0(x) (((x) >> 0 ) & 0xFF)
#define GET_B1(x) (((x) >> 8 ) & 0xFF)
#define GET_B2(x) (((x) >> 16) & 0xFF)
#define GET_B3(x) (((x) >> 24) & 0xFF)
static void aes_key_expand(s14aes_handle* ctx, const uint8_t* key, uint32_t* rk) {
int i;
rk[0] = GET_U32LE(key + 0);
rk[1] = GET_U32LE(key + 4);
rk[2] = GET_U32LE(key + 8);
rk[3] = GET_U32LE(key + 12);
rk[4] = GET_U32LE(key + 16);
rk[5] = GET_U32LE(key + 20);
for (i = 6; i < 52; i++) {
uint8_t temp0 = (rk[5] >> 0 ) & 0xFF;
uint8_t temp1 = (rk[5] >> 8 ) & 0xFF;
uint8_t temp2 = (rk[5] >> 16) & 0xFF;
uint8_t temp3 = (rk[5] >> 24) & 0xFF;
if (i == 6 * (i / 6)) {
uint8_t sv = ctx->sbox[temp1];
temp1 = ctx->sbox[temp2];
temp2 = ctx->sbox[temp3];
temp3 = ctx->sbox[temp0];
temp0 = ctx->rcon[i / 6u - 1] ^ sv;
}
rk[6] = ((temp0 ^ ((rk[0] >> 0 ) & 0xFF)) << 0 ) |
((temp1 ^ ((rk[0] >> 8 ) & 0xFF)) << 8 ) |
((temp2 ^ ((rk[0] >> 16) & 0xFF)) << 16) |
((temp3 ^ ((rk[0] >> 24) & 0xFF)) << 24);
rk++;
}
}
static void aes_init_key(s14aes_handle* ctx, const uint8_t* key) {
const uint8_t invcols[4][4] = {
{0x0E,0x0B,0x0D,0x09},
{0x09,0x0E,0x0B,0x0D},
{0x0D,0x09,0x0E,0x0B},
{0x0B,0x0D,0x09,0x0E}
};
unsigned int roundkey[52];
int i, j, row, col, b;
aes_key_expand(ctx, key, roundkey);
for (i = 0; i < 4; i++) {
ctx->rk[i] = ((roundkey[i] << 24) & 0xFF000000) |
((roundkey[i] << 8 ) & 0x00FF0000) |
((roundkey[i] >> 8 ) & 0x0000FF00) |
((roundkey[i] >> 24) & 0x000000FF);
}
for (i = 4; i < 48; i += 4) {
for (j = i; j < i + 4; j++) {
uint32_t val = 0;
for (row = 0; row < 4; row++) {
uint8_t xv = 0;
for (col = 0; col < 4; col++) {
uint16_t rv1 = 0;
uint16_t rv2 = (roundkey[j] >> (col * 8)) & 0xFF;
uint8_t ic = invcols[row][col];
for (b = 0; b < 8; b++) {
if (ic & (1 << b))
rv1 ^= rv2;
rv2 *= 2;
}
xv ^= rv1 ^ ctx->xors[rv1 >> 8];
}
val = (val << 8) | xv;
}
ctx->rk[j] = val;
}
}
for (i = 48; i < 52; i++) {
ctx->rk[i] = ((roundkey[i] << 24) & 0xFF000000) |
((roundkey[i] << 8 ) & 0x00FF0000) |
((roundkey[i] >> 8 ) & 0x0000FF00) |
((roundkey[i] >> 24) & 0x000000FF);
}
}
static void aes_init_state(s14aes_handle* ctx) {
const uint8_t invcol[4] = {
0x0E, 0x0B, 0x0D, 0x09
};
unsigned int *tds_ptr;
uint8_t rcon[32];
uint8_t box[256];
int i, j, k, b;
for (i = 0; i < 32; i++) {
uint16_t rv;
if (i >= 8) {
rv = 128;
for (j = 0; j < i - 7; j++) {
rv *= 2;
if (rv & 256)
rv ^= 0x11Bu;
}
}
else {
rv = 1 << i;
}
rcon[i] = rv;
}
for (i = 0; i < 256; i++) {
uint8_t xv = 0;
for (j = 0; j < 8; j++) {
if (i & (1 << j))
xv ^= rcon[j + 8];
}
ctx->xors[i] = xv;
}
tds_ptr = ctx->tds;
for (i = 0; i < 256; i++) {
uint32_t val = 0;
for (j = 0; j < 4; j++) {
uint16_t tv1 = 0;
uint16_t tv2 = invcol[j];
for (b = 0; b < 8; b++) {
if (i & (1 << b))
tv1 ^= tv2;
tv2 *= 2;
}
val = ((val >> 8u) & 0x00FFFFFF) | ((val << 24u) & 0xFF000000);
val = ((uint8_t)tv1 ^ ctx->xors[tv1 >> 8]) | val;
}
val = ((val >> 16u) & 0x0000FFFF) | ((val << 16u) & 0xFFFF0000);
for (j = 0; j < 4; j++) {
*tds_ptr++ = val;
val = ((val >> 8u) & 0x00FFFFFF) | ((val << 24u) & 0xFF000000);
}
}
box[0] = 0;
for (i = 1; i < 256; i++) {
for (j = 1; j < 256; j++) {
uint16_t bv1 = 0;
uint16_t bv2 = j;
for (k = 0; k < 8; k++) {
if (i & (1 << k))
bv1 ^= bv2;
bv2 *= 2;
}
if (((uint8_t)bv1 ^ ctx->xors[bv1 >> 8]) == 1)
break;
}
box[i] = j;
if (j == 256) /* ??? */
return;
}
for (i = 0; i < 256; i += 16) {
for (j = 0; j < 16; j++) {
uint8_t val = 0;
for (k = 0; k < 8; k++) {
val |= box[i | j] & (1 << k);
for (b = 0; b < 4; b++) {
uint8_t bv = box[i | j];
if (bv & (1 << ((b + k - 4) & 7)))
val ^= 1 << k;
}
}
ctx->sbox[i + j] = val ^ 0x63;
ctx->ibox[val ^ 0x63] = i + j;
}
}
/* originally recalculated in Namco's code (inlined?) */
for (i = 0; i < 8; i++) {
ctx->rcon[i] = rcon[i];
}
}
static void aes_decrypt_block(s14aes_handle* ctx, uint8_t* buf) {
uint32_t s0, s1, s2, s3, t0, t1, t2, t3;
uint8_t* ibox = ctx->ibox;
uint32_t* tds = ctx->tds;
uint32_t* rk = ctx->rk;
s0 = rk[48] ^ GET_U32BE(buf + 0);
s1 = rk[49] ^ GET_U32BE(buf + 4);
s2 = rk[50] ^ GET_U32BE(buf + 8);
s3 = rk[51] ^ GET_U32BE(buf + 12);
t0 = rk[44] ^ tds[4 * ibox[GET_B0(s1)] + 3] ^ tds[4 * ibox[GET_B1(s2)] + 2] ^ tds[4 * ibox[GET_B2(s3)] + 1] ^ tds[4 * ibox[GET_B3(s0)] + 0];
t1 = rk[45] ^ tds[4 * ibox[GET_B0(s2)] + 3] ^ tds[4 * ibox[GET_B1(s3)] + 2] ^ tds[4 * ibox[GET_B2(s0)] + 1] ^ tds[4 * ibox[GET_B3(s1)] + 0];
t2 = rk[46] ^ tds[4 * ibox[GET_B0(s3)] + 3] ^ tds[4 * ibox[GET_B1(s0)] + 2] ^ tds[4 * ibox[GET_B2(s1)] + 1] ^ tds[4 * ibox[GET_B3(s2)] + 0];
t3 = rk[47] ^ tds[4 * ibox[GET_B0(s0)] + 3] ^ tds[4 * ibox[GET_B1(s1)] + 2] ^ tds[4 * ibox[GET_B2(s2)] + 1] ^ tds[4 * ibox[GET_B3(s3)] + 0];
s0 = rk[40] ^ tds[4 * ibox[GET_B0(t1)] + 3] ^ tds[4 * ibox[GET_B1(t2)] + 2] ^ tds[4 * ibox[GET_B2(t3)] + 1] ^ tds[4 * ibox[GET_B3(t0)] + 0];
s1 = rk[41] ^ tds[4 * ibox[GET_B0(t2)] + 3] ^ tds[4 * ibox[GET_B1(t3)] + 2] ^ tds[4 * ibox[GET_B2(t0)] + 1] ^ tds[4 * ibox[GET_B3(t1)] + 0];
s2 = rk[42] ^ tds[4 * ibox[GET_B0(t3)] + 3] ^ tds[4 * ibox[GET_B1(t0)] + 2] ^ tds[4 * ibox[GET_B2(t1)] + 1] ^ tds[4 * ibox[GET_B3(t2)] + 0];
s3 = rk[43] ^ tds[4 * ibox[GET_B0(t0)] + 3] ^ tds[4 * ibox[GET_B1(t1)] + 2] ^ tds[4 * ibox[GET_B2(t2)] + 1] ^ tds[4 * ibox[GET_B3(t3)] + 0];
t0 = rk[36] ^ tds[4 * ibox[GET_B0(s1)] + 3] ^ tds[4 * ibox[GET_B1(s2)] + 2] ^ tds[4 * ibox[GET_B2(s3)] + 1] ^ tds[4 * ibox[GET_B3(s0)] + 0];
t1 = rk[37] ^ tds[4 * ibox[GET_B0(s2)] + 3] ^ tds[4 * ibox[GET_B1(s3)] + 2] ^ tds[4 * ibox[GET_B2(s0)] + 1] ^ tds[4 * ibox[GET_B3(s1)] + 0];
t2 = rk[38] ^ tds[4 * ibox[GET_B0(s3)] + 3] ^ tds[4 * ibox[GET_B1(s0)] + 2] ^ tds[4 * ibox[GET_B2(s1)] + 1] ^ tds[4 * ibox[GET_B3(s2)] + 0];
t3 = rk[39] ^ tds[4 * ibox[GET_B0(s0)] + 3] ^ tds[4 * ibox[GET_B1(s1)] + 2] ^ tds[4 * ibox[GET_B2(s2)] + 1] ^ tds[4 * ibox[GET_B3(s3)] + 0];
s0 = rk[32] ^ tds[4 * ibox[GET_B0(t1)] + 3] ^ tds[4 * ibox[GET_B1(t2)] + 2] ^ tds[4 * ibox[GET_B2(t3)] + 1] ^ tds[4 * ibox[GET_B3(t0)] + 0];
s1 = rk[33] ^ tds[4 * ibox[GET_B0(t2)] + 3] ^ tds[4 * ibox[GET_B1(t3)] + 2] ^ tds[4 * ibox[GET_B2(t0)] + 1] ^ tds[4 * ibox[GET_B3(t1)] + 0];
s2 = rk[34] ^ tds[4 * ibox[GET_B0(t3)] + 3] ^ tds[4 * ibox[GET_B1(t0)] + 2] ^ tds[4 * ibox[GET_B2(t1)] + 1] ^ tds[4 * ibox[GET_B3(t2)] + 0];
s3 = rk[35] ^ tds[4 * ibox[GET_B0(t0)] + 3] ^ tds[4 * ibox[GET_B1(t1)] + 2] ^ tds[4 * ibox[GET_B2(t2)] + 1] ^ tds[4 * ibox[GET_B3(t3)] + 0];
t0 = rk[28] ^ tds[4 * ibox[GET_B0(s1)] + 3] ^ tds[4 * ibox[GET_B1(s2)] + 2] ^ tds[4 * ibox[GET_B2(s3)] + 1] ^ tds[4 * ibox[GET_B3(s0)] + 0];
t1 = rk[29] ^ tds[4 * ibox[GET_B0(s2)] + 3] ^ tds[4 * ibox[GET_B1(s3)] + 2] ^ tds[4 * ibox[GET_B2(s0)] + 1] ^ tds[4 * ibox[GET_B3(s1)] + 0];
t2 = rk[30] ^ tds[4 * ibox[GET_B0(s3)] + 3] ^ tds[4 * ibox[GET_B1(s0)] + 2] ^ tds[4 * ibox[GET_B2(s1)] + 1] ^ tds[4 * ibox[GET_B3(s2)] + 0];
t3 = rk[31] ^ tds[4 * ibox[GET_B0(s0)] + 3] ^ tds[4 * ibox[GET_B1(s1)] + 2] ^ tds[4 * ibox[GET_B2(s2)] + 1] ^ tds[4 * ibox[GET_B3(s3)] + 0];
s0 = rk[24] ^ tds[4 * ibox[GET_B0(t1)] + 3] ^ tds[4 * ibox[GET_B1(t2)] + 2] ^ tds[4 * ibox[GET_B2(t3)] + 1] ^ tds[4 * ibox[GET_B3(t0)] + 0];
s1 = rk[25] ^ tds[4 * ibox[GET_B0(t2)] + 3] ^ tds[4 * ibox[GET_B1(t3)] + 2] ^ tds[4 * ibox[GET_B2(t0)] + 1] ^ tds[4 * ibox[GET_B3(t1)] + 0];
s2 = rk[26] ^ tds[4 * ibox[GET_B0(t3)] + 3] ^ tds[4 * ibox[GET_B1(t0)] + 2] ^ tds[4 * ibox[GET_B2(t1)] + 1] ^ tds[4 * ibox[GET_B3(t2)] + 0];
s3 = rk[27] ^ tds[4 * ibox[GET_B0(t0)] + 3] ^ tds[4 * ibox[GET_B1(t1)] + 2] ^ tds[4 * ibox[GET_B2(t2)] + 1] ^ tds[4 * ibox[GET_B3(t3)] + 0];
t0 = rk[20] ^ tds[4 * ibox[GET_B0(s1)] + 3] ^ tds[4 * ibox[GET_B1(s2)] + 2] ^ tds[4 * ibox[GET_B2(s3)] + 1] ^ tds[4 * ibox[GET_B3(s0)] + 0];
t1 = rk[21] ^ tds[4 * ibox[GET_B0(s2)] + 3] ^ tds[4 * ibox[GET_B1(s3)] + 2] ^ tds[4 * ibox[GET_B2(s0)] + 1] ^ tds[4 * ibox[GET_B3(s1)] + 0];
t2 = rk[22] ^ tds[4 * ibox[GET_B0(s3)] + 3] ^ tds[4 * ibox[GET_B1(s0)] + 2] ^ tds[4 * ibox[GET_B2(s1)] + 1] ^ tds[4 * ibox[GET_B3(s2)] + 0];
t3 = rk[23] ^ tds[4 * ibox[GET_B0(s0)] + 3] ^ tds[4 * ibox[GET_B1(s1)] + 2] ^ tds[4 * ibox[GET_B2(s2)] + 1] ^ tds[4 * ibox[GET_B3(s3)] + 0];
s0 = rk[16] ^ tds[4 * ibox[GET_B0(t1)] + 3] ^ tds[4 * ibox[GET_B1(t2)] + 2] ^ tds[4 * ibox[GET_B2(t3)] + 1] ^ tds[4 * ibox[GET_B3(t0)] + 0];
s1 = rk[17] ^ tds[4 * ibox[GET_B0(t2)] + 3] ^ tds[4 * ibox[GET_B1(t3)] + 2] ^ tds[4 * ibox[GET_B2(t0)] + 1] ^ tds[4 * ibox[GET_B3(t1)] + 0];
s2 = rk[18] ^ tds[4 * ibox[GET_B0(t3)] + 3] ^ tds[4 * ibox[GET_B1(t0)] + 2] ^ tds[4 * ibox[GET_B2(t1)] + 1] ^ tds[4 * ibox[GET_B3(t2)] + 0];
s3 = rk[19] ^ tds[4 * ibox[GET_B0(t0)] + 3] ^ tds[4 * ibox[GET_B1(t1)] + 2] ^ tds[4 * ibox[GET_B2(t2)] + 1] ^ tds[4 * ibox[GET_B3(t3)] + 0];
t0 = rk[12] ^ tds[4 * ibox[GET_B0(s1)] + 3] ^ tds[4 * ibox[GET_B1(s2)] + 2] ^ tds[4 * ibox[GET_B2(s3)] + 1] ^ tds[4 * ibox[GET_B3(s0)] + 0];
t1 = rk[13] ^ tds[4 * ibox[GET_B0(s2)] + 3] ^ tds[4 * ibox[GET_B1(s3)] + 2] ^ tds[4 * ibox[GET_B2(s0)] + 1] ^ tds[4 * ibox[GET_B3(s1)] + 0];
t2 = rk[14] ^ tds[4 * ibox[GET_B0(s3)] + 3] ^ tds[4 * ibox[GET_B1(s0)] + 2] ^ tds[4 * ibox[GET_B2(s1)] + 1] ^ tds[4 * ibox[GET_B3(s2)] + 0];
t3 = rk[15] ^ tds[4 * ibox[GET_B0(s0)] + 3] ^ tds[4 * ibox[GET_B1(s1)] + 2] ^ tds[4 * ibox[GET_B2(s2)] + 1] ^ tds[4 * ibox[GET_B3(s3)] + 0];
s0 = rk[8 ] ^ tds[4 * ibox[GET_B0(t1)] + 3] ^ tds[4 * ibox[GET_B1(t2)] + 2] ^ tds[4 * ibox[GET_B2(t3)] + 1] ^ tds[4 * ibox[GET_B3(t0)] + 0];
s1 = rk[9 ] ^ tds[4 * ibox[GET_B0(t2)] + 3] ^ tds[4 * ibox[GET_B1(t3)] + 2] ^ tds[4 * ibox[GET_B2(t0)] + 1] ^ tds[4 * ibox[GET_B3(t1)] + 0];
s2 = rk[10] ^ tds[4 * ibox[GET_B0(t3)] + 3] ^ tds[4 * ibox[GET_B1(t0)] + 2] ^ tds[4 * ibox[GET_B2(t1)] + 1] ^ tds[4 * ibox[GET_B3(t2)] + 0];
s3 = rk[11] ^ tds[4 * ibox[GET_B0(t0)] + 3] ^ tds[4 * ibox[GET_B1(t1)] + 2] ^ tds[4 * ibox[GET_B2(t2)] + 1] ^ tds[4 * ibox[GET_B3(t3)] + 0];
t0 = rk[4 ] ^ tds[4 * ibox[GET_B0(s1)] + 3] ^ tds[4 * ibox[GET_B1(s2)] + 2] ^ tds[4 * ibox[GET_B2(s3)] + 1] ^ tds[4 * ibox[GET_B3(s0)] + 0];
t1 = rk[5 ] ^ tds[4 * ibox[GET_B0(s2)] + 3] ^ tds[4 * ibox[GET_B1(s3)] + 2] ^ tds[4 * ibox[GET_B2(s0)] + 1] ^ tds[4 * ibox[GET_B3(s1)] + 0];
t2 = rk[6 ] ^ tds[4 * ibox[GET_B0(s3)] + 3] ^ tds[4 * ibox[GET_B1(s0)] + 2] ^ tds[4 * ibox[GET_B2(s1)] + 1] ^ tds[4 * ibox[GET_B3(s2)] + 0];
t3 = rk[7 ] ^ tds[4 * ibox[GET_B0(s0)] + 3] ^ tds[4 * ibox[GET_B1(s1)] + 2] ^ tds[4 * ibox[GET_B2(s2)] + 1] ^ tds[4 * ibox[GET_B3(s3)] + 0];
buf[0 ] = GET_B3(rk[0]) ^ ibox[GET_B3(t0)];
buf[1 ] = GET_B2(rk[0]) ^ ibox[GET_B2(t3)];
buf[2 ] = GET_B1(rk[0]) ^ ibox[GET_B1(t2)];
buf[3 ] = GET_B0(rk[0]) ^ ibox[GET_B0(t1)];
buf[4 ] = GET_B3(rk[1]) ^ ibox[GET_B3(t1)];
buf[5 ] = GET_B2(rk[1]) ^ ibox[GET_B2(t0)];
buf[6 ] = GET_B1(rk[1]) ^ ibox[GET_B1(t3)];
buf[7 ] = GET_B0(rk[1]) ^ ibox[GET_B0(t2)];
buf[8 ] = GET_B3(rk[2]) ^ ibox[GET_B3(t2)];
buf[9 ] = GET_B2(rk[2]) ^ ibox[GET_B2(t1)];
buf[10] = GET_B1(rk[2]) ^ ibox[GET_B1(t0)];
buf[11] = GET_B0(rk[2]) ^ ibox[GET_B0(t3)];
buf[12] = GET_B3(rk[3]) ^ ibox[GET_B3(t3)];
buf[13] = GET_B2(rk[3]) ^ ibox[GET_B2(t2)];
buf[14] = GET_B1(rk[3]) ^ ibox[GET_B1(t1)];
buf[15] = GET_B0(rk[3]) ^ ibox[GET_B0(t0)];
}
/* **************************** */
s14aes_handle* s14aes_init() {
s14aes_handle* ctx = malloc(sizeof(s14aes_handle));
if (!ctx) goto fail;
aes_init_state(ctx);
return ctx;
fail:
return NULL;
}
void s14aes_close(s14aes_handle* ctx) {
free(ctx);
}
void s14aes_set_key(s14aes_handle* ctx, const uint8_t* key) {
aes_init_key(ctx, key);
}
void s14aes_decrypt(s14aes_handle* ctx, uint8_t* buf) {
aes_decrypt_block(ctx, buf);
}

View File

@ -0,0 +1,19 @@
#ifndef _G7221_DECODER_AES_H
#define _G7221_DECODER_AES_H
#include <stdint.h>
typedef struct s14aes_handle s14aes_handle;
/* init/close handle (AES-192 in ECB mode) */
s14aes_handle* s14aes_init();
void s14aes_close(s14aes_handle* ctx);
/* set new key (can be called multiple times) */
void s14aes_set_key(s14aes_handle* ctx, const uint8_t* key);
/* decrypt a single 0x10 block */
void s14aes_decrypt(s14aes_handle* ctx, uint8_t* buf);
#endif

View File

@ -3,6 +3,7 @@
#include <string.h>
#include "g7221_decoder_lib.h"
#include "g7221_decoder_aes.h"
/* Decodes Siren14 from Namco's BNSF, a mono MLT/DCT-based codec for speech/sound (low bandwidth).
@ -1109,6 +1110,8 @@ struct g7221_handle {
/* control */
int bit_rate;
int frame_size;
/* AES setup/state */
s14aes_handle* aes;
/* state */
int16_t mlt_coefs[MAX_DCT_LENGTH];
int16_t old_samples[MAX_DCT_LENGTH >> 1];
@ -1143,14 +1146,11 @@ int g7221_decode_frame(g7221_handle* handle, uint8_t* data, int16_t* out_samples
int res;
int mag_shift;
#if 0
/* first 0x10 bytes are encrypted with an unknown substitution key and a complex
* unXOR function. Original code also saves encrypted bytes, then re-crypts after
* unpacking, presumably to guard against memdumps. */
if (handle->flags & 0x02) {
decrypt_frame(data, key);
/* first 0x10 bytes may be encrypted with AES. Original code also saves encrypted bytes,
* then re-crypts after unpacking, presumably to guard against memdumps. */
if (handle->aes != NULL) {
s14aes_decrypt(handle->aes, data);
}
#endif
/* Namco's decoder is designed so that out_samples can be set in place of mlt_coefs,
* so we could avoid one extra buffer, but for clarity we'll leave as is */
@ -1215,5 +1215,43 @@ void g7221_free(g7221_handle* handle) {
if (!handle)
return;
s14aes_close(handle->aes);
free(handle);
}
int g7221_set_key(g7221_handle* handle, const uint8_t* key) {
const int key_size = 192 / 8; /* only 192 bit mode */
uint8_t temp_key[192 / 8];
const char* mod_key = "Ua#oK3P94vdxX,ft*k-mnjoO"; /* constant for all platform/games */
int i;
if (!handle)
goto fail;
/* disable, useful for testing? */
if (key == NULL) {
s14aes_close(handle->aes);
handle->aes = NULL;
return 1;
}
/* init AES state (tables) or reuse if already exists */
if (handle->aes == NULL) {
handle->aes = s14aes_init();
if (!handle->aes) goto fail;
}
/* Base key is XORed probably against memdumps, as plain key would be part of the final AES key. However
* roundkey is still in memdumps near AES state (~0x1310 from sbox table, that starts with 0x63,0x7c,0x77,0x7b...)
* so it isn't too effective. XORing was originally done inside aes_expand_key during S14/S22 init. */
for (i = 0; i < key_size; i++) {
temp_key[i] = key[i] ^ mod_key[i];
}
/* reset new key */
s14aes_set_key(handle->aes, temp_key);
return 1;
fail:
return 0;
}

View File

@ -1,8 +1,8 @@
/*
Interface to Namco G.722.1 decoder
*/
#ifndef G7221_H
#define G7221_H
#ifndef _G7221_DECODER_LIB_H
#define _G7221_DECODER_LIB_H
#include <stdint.h>
@ -26,4 +26,7 @@ void g7221_reset(g7221_handle* handle);
/* free resources */
void g7221_free(g7221_handle* handle);
/* set new key (ignores key on failure) */
int g7221_set_key(g7221_handle* handle, const uint8_t* key);
#endif

View File

@ -252,6 +252,10 @@
RelativePath=".\meta\bgw_streamfile.h"
>
</File>
<File
RelativePath=".\meta\bnsf_keys.h"
>
</File>
<File
RelativePath=".\meta\cri_utf.h"
>
@ -1894,6 +1898,10 @@
RelativePath=".\coding\ea_mt_decoder_utk.h"
>
</File>
<File
RelativePath=".\coding\g7221_decoder_aes.h"
>
</File>
<File
RelativePath=".\coding\g7221_decoder_lib.h"
>
@ -2010,6 +2018,10 @@
RelativePath=".\coding\g7221_decoder.c"
>
</File>
<File
RelativePath=".\coding\g7221_decoder_aes.c"
>
</File>
<File
RelativePath=".\coding\g7221_decoder_lib.c"
>

View File

@ -105,6 +105,7 @@
<ClInclude Include="meta\awc_xma_streamfile.h" />
<ClInclude Include="meta\bar_streamfile.h" />
<ClInclude Include="meta\bgw_streamfile.h" />
<ClInclude Include="meta\bnsf_keys.h" />
<ClInclude Include="meta\cri_utf.h" />
<ClInclude Include="meta\deblock_streamfile.h" />
<ClInclude Include="meta\ea_eaac_streamfile.h" />
@ -138,6 +139,7 @@
<ClInclude Include="coding\acm_decoder_libacm.h" />
<ClInclude Include="coding\coding.h" />
<ClInclude Include="coding\ea_mt_decoder_utk.h" />
<ClInclude Include="coding\g7221_decoder_aes.h" />
<ClInclude Include="coding\g7221_decoder_lib.h" />
<ClInclude Include="coding\g7221_decoder_lib_data.h" />
<ClInclude Include="coding\fsb_vorbis_data.h" />
@ -559,6 +561,7 @@
<ClCompile Include="coding\g719_decoder.c" />
<ClCompile Include="coding\g721_decoder.c" />
<ClCompile Include="coding\g7221_decoder.c" />
<ClCompile Include="coding\g7221_decoder_aes.c" />
<ClCompile Include="coding\g7221_decoder_lib.c" />
<ClCompile Include="coding\hca_decoder.c" />
<ClCompile Include="coding\ima_decoder.c" />

View File

@ -86,6 +86,9 @@
<ClInclude Include="meta\bgw_streamfile.h">
<Filter>meta\Header Files</Filter>
</ClInclude>
<ClInclude Include="meta\bnsf_keys.h">
<Filter>meta\Header Files</Filter>
</ClInclude>
<ClInclude Include="meta\cri_utf.h">
<Filter>meta\Header Files</Filter>
</ClInclude>
@ -185,7 +188,7 @@
<ClInclude Include="coding\ea_mt_decoder_utk.h">
<Filter>coding\Header Files</Filter>
</ClInclude>
<ClInclude Include="coding\g7221_decoder_lib.h">
<ClInclude Include="coding\g7221_decoder_aes.h">
<Filter>coding\Header Files</Filter>
</ClInclude>
<ClInclude Include="coding\g7221_decoder_lib_data.h">
@ -1192,6 +1195,9 @@
<ClCompile Include="coding\g7221_decoder.c">
<Filter>coding\Source Files</Filter>
</ClCompile>
<ClCompile Include="coding\g7221_decoder_aes.c">
<Filter>coding\Source Files</Filter>
</ClCompile>
<ClCompile Include="coding\g7221_decoder_lib.c">
<Filter>coding\Source Files</Filter>
</ClCompile>

View File

@ -1,8 +1,12 @@
#include "meta.h"
#include "../coding/coding.h"
#include "../util.h"
#include "bnsf_keys.h"
/* BNSF - Namco Bandai's Bandai Namco Sound Format/File [Tales of Graces (Wii), Tales of Berseria (PS4)] */
static void find_bnsf_key(g7221_codec_data *data, off_t start, STREAMFILE *sf, uint8_t *best_key);
/* BNSF - Bandai Namco Sound Format/File [Tales of Graces (Wii), Tales of Berseria (PS4)] */
VGMSTREAM * init_vgmstream_bnsf(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset = 0, first_offset = 0x0C;
@ -19,11 +23,10 @@ VGMSTREAM * init_vgmstream_bnsf(STREAMFILE *streamFile) {
if (read_32bitBE(0,streamFile) != 0x424E5346) /* "BNSF" */
goto fail;
bnsf_size = read_32bitBE(0x04,streamFile);
codec = read_32bitBE(0x08,streamFile);
/* check file size (siren22 uses full size) */
bnsf_size = read_32bitBE(0x04,streamFile);
if (bnsf_size + (codec == 0x49533232 ? 0x00 : 0x08) != get_streamfile_size(streamFile))
if (bnsf_size + (codec == 0x49533232 ? 0x00 : 0x08) != get_streamfile_size(streamFile)) /* IS22 uses full size */
goto fail;
if (!find_chunk_be(streamFile, 0x73666d74,first_offset,0, &sfmt_chunk,NULL)) /* "sfmt" */
@ -40,20 +43,19 @@ VGMSTREAM * init_vgmstream_bnsf(STREAMFILE *streamFile) {
channel_count = read_16bitBE(sfmt_chunk+0x02,streamFile);
sample_rate = read_32bitBE(sfmt_chunk+0x04,streamFile);
num_samples = read_32bitBE(sfmt_chunk+0x08,streamFile);
loop_adjust = read_32bitBE(sfmt_chunk+0x0c,streamFile); /* 0 if no looping */
loop_adjust = read_32bitBE(sfmt_chunk+0x0c,streamFile); /* 0 when no loop */
block_size = read_16bitBE(sfmt_chunk+0x10,streamFile);
block_samples = read_16bitBE(sfmt_chunk+0x12,streamFile);
//max_samples = sdat_size / block_size * block_samples /* num_samples is smaller */
//max_samples = sdat_size / block_size * block_samples;
start_offset = sdat_chunk;
/* shouldn't happen, plus decoder can't handle it */
if (loop_adjust >= block_samples)
if (loop_adjust >= block_samples) /* decoder can't handle this */
goto fail;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
vgmstream = allocate_vgmstream(channel_count, loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = sample_rate;
@ -63,11 +65,7 @@ VGMSTREAM * init_vgmstream_bnsf(STREAMFILE *streamFile) {
vgmstream->meta_type = meta_BNSF;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = block_size/channel_count;
/* Late IS14 set flag 0x02 = encrypted [Tales of Zestiria (PS3/PC) voices, The Idolm@ster 2 (PS3) voices] */
if (flags != 0)
goto fail;
vgmstream->interleave_block_size = block_size / channel_count;
switch (codec) {
#ifdef VGM_USE_G7221
@ -76,10 +74,29 @@ VGMSTREAM * init_vgmstream_bnsf(STREAMFILE *streamFile) {
vgmstream->codec_data = init_g7221(vgmstream->channels, vgmstream->interleave_block_size);
if (!vgmstream->codec_data) goto fail;
/* get decryption key in .bnsfkey file or list, for later games' voices
* [The Idolm@ster 2 (PS3/X360), Tales of Zestiria (PS3/PC)] */
if (flags != 0) { /* only known value is 0x02 though */
size_t keysize;
uint8_t key[24] = {0}; /* keystring 0-padded to 192-bit */
keysize = read_key_file(key, sizeof(key), streamFile);
if (keysize <= 0 || keysize > sizeof(key)) {
find_bnsf_key(vgmstream->codec_data, start_offset, streamFile, key);
}
set_key_g7221(vgmstream->codec_data, key);
}
break;
#endif
#ifdef VGM_USE_G719
case 0x49533232: /* "IS22" (interleaved Siren22) */
/* same encryption as IS14 but not seen */
if (flags != 0)
goto fail;
vgmstream->coding_type = coding_G719;
vgmstream->codec_data = init_g719(vgmstream->channels, vgmstream->interleave_block_size);
if (!vgmstream->codec_data) goto fail;
@ -99,3 +116,41 @@ fail:
close_vgmstream(vgmstream);
return NULL;
}
static void find_bnsf_key(g7221_codec_data* data, off_t start, STREAMFILE* sf, uint8_t* best_key) {
const size_t keys_length = sizeof(s14key_list) / sizeof(bnsfkey_info);
int score, best_score = -1;
int i;
uint8_t tmpkey[24];
for (i = 0; i < keys_length; i++) {
const char* key = s14key_list[i].key;
int keylen = strlen(key);
if (keylen > sizeof(tmpkey))
continue;
memcpy(tmpkey, key, keylen);
memset(tmpkey + keylen, 0, sizeof(tmpkey) - keylen);
//;VGM_LOG("BNSF: test key=%.24s\n", tmpkey);
set_key_g7221(data, tmpkey);
score = test_key_g7221(data, start, sf);
if (score < 0) continue;
if (best_score <= 0 || (score < best_score && score > 0)) {
best_score = score;
memcpy(best_key, key, keylen);
memset(best_key + keylen, 0, sizeof(tmpkey) - keylen);
}
if (best_score == 1) {
break;
}
}
VGM_ASSERT(best_score > 0, "BNSF: best key=%.24s (score=%i)\n", best_key, best_score);
VGM_ASSERT(best_score < 0, "BNSF: key not found\n"); /* defaults to all 0s */
}

16
src/meta/bnsf_keys.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef _BNSF_KEYS_H_
#define _BNSF_KEYS_H_
typedef struct {
const char* key;
} bnsfkey_info;
/* Known keys, extracted from games' exe */
static const bnsfkey_info s14key_list[] = {
/* THE iDOLM@STER 2 (PS3/X360) */
{"haruka17imas"},
};
#endif/*_BNSF_KEYS_H_*/

View File

@ -695,7 +695,7 @@ void reset_vgmstream(VGMSTREAM * vgmstream) {
#ifdef VGM_USE_G7221
if (vgmstream->coding_type == coding_G7221C) {
reset_g7221(vgmstream);
reset_g7221(vgmstream->codec_data);
}
#endif
@ -879,7 +879,7 @@ void close_vgmstream(VGMSTREAM * vgmstream) {
#ifdef VGM_USE_G7221
if (vgmstream->coding_type == coding_G7221C) {
free_g7221(vgmstream);
free_g7221(vgmstream->codec_data);
vgmstream->codec_data = NULL;
}
#endif