mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-01-31 12:23:44 +01:00
Add new HCA key derivation
Thanks to FZFalzar, Thealexbarney and hozuki for the key and algorithm
This commit is contained in:
parent
ec0043bf6b
commit
cca676bb0f
@ -162,6 +162,7 @@ a companion file:
|
||||
- .adx: .adxkey (derived 6 byte key, in start/mult/add format)
|
||||
- .ahx: .ahxkey (derived 6 byte key, in start/mult/add format)
|
||||
- .hca: .hcakey (8 byte decryption key, a 64-bit number)
|
||||
- May be followed by 2 byte AWB derivation value for newer HCA
|
||||
- .fsb: .fsbkey (decryption key, in hex)
|
||||
|
||||
The key file can be ".(ext)key" (for the whole folder), or "(name).(ext)key"
|
||||
@ -240,7 +241,7 @@ are used in few games.
|
||||
- AAC
|
||||
- Bink
|
||||
- AC3/SPDIF
|
||||
- Xiph Opus (Ogg, Switch)
|
||||
- Xiph Opus (Ogg, Switch, EA, UE4)
|
||||
- Xiph CELT (FSB)
|
||||
- Musepack
|
||||
- FLAC
|
||||
|
@ -204,6 +204,10 @@
|
||||
RelativePath=".\meta\hca_keys.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\hca_keys_awb.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\fsb_keys.h"
|
||||
>
|
||||
|
@ -107,9 +107,11 @@
|
||||
<ClInclude Include="meta\ps2_psh_streamfile.h" />
|
||||
<ClInclude Include="meta\opus_interleave_streamfile.h" />
|
||||
<ClInclude Include="meta\sqex_scd_streamfile.h" />
|
||||
<ClInclude Include="meta\xvag_streamfile.h" />
|
||||
<ClInclude Include="meta\sqex_scd_streamfile.h" />
|
||||
<ClInclude Include="meta\ubi_lyn_ogg_streamfile.h" />
|
||||
<ClInclude Include="meta\meta.h" />
|
||||
<ClInclude Include="meta\hca_keys.h" />
|
||||
<ClInclude Include="meta\hca_keys_awb.h" />
|
||||
<ClInclude Include="meta\fsb_keys.h" />
|
||||
<ClInclude Include="coding\acm_decoder_libacm.h" />
|
||||
<ClInclude Include="coding\coding.h" />
|
||||
|
@ -113,6 +113,9 @@
|
||||
<ClInclude Include="meta\hca_keys.h">
|
||||
<Filter>meta\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="meta\hca_keys_awb.h">
|
||||
<Filter>meta\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="meta\fsb_keys.h">
|
||||
<Filter>meta\Header Files</Filter>
|
||||
</ClInclude>
|
||||
@ -1306,7 +1309,7 @@
|
||||
<ClCompile Include="meta\sqex_scd.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\sqex_scd_sscfs.c">
|
||||
<ClCompile Include="meta\sqex_scd_sscf.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\sqex_sead.c">
|
||||
|
@ -21,10 +21,19 @@ VGMSTREAM * init_vgmstream_hca(STREAMFILE *streamFile) {
|
||||
|
||||
/* find decryption key in external file or preloaded list */
|
||||
if (hca_data->info.encryptionEnabled) {
|
||||
uint8_t keybuf[8];
|
||||
if (read_key_file(keybuf, 8, streamFile) == 8) {
|
||||
uint8_t keybuf[0x08+0x02];
|
||||
size_t keysize;
|
||||
|
||||
keysize = read_key_file(keybuf, 0x08+0x04, streamFile);
|
||||
if (keysize == 0x08) { /* standard */
|
||||
keycode = (uint64_t)get_64bitBE(keybuf+0x00);
|
||||
} else {
|
||||
}
|
||||
else if (keysize == 0x08+0x02) { /* seed key + AWB subkey */
|
||||
uint64_t key = (uint64_t)get_64bitBE(keybuf+0x00);
|
||||
uint16_t sub = (uint16_t)get_16bitBE(keybuf+0x08);
|
||||
keycode = key * ( ((uint64_t)sub << 16u) | ((uint16_t)~sub + 2u) );
|
||||
}
|
||||
else {
|
||||
find_hca_key(hca_data, &keycode);
|
||||
}
|
||||
|
||||
@ -63,46 +72,61 @@ fail:
|
||||
}
|
||||
|
||||
|
||||
/* Try to find the decryption key from a list. */
|
||||
static void find_hca_key(hca_codec_data * hca_data, unsigned long long * out_keycode) {
|
||||
const size_t keys_length = sizeof(hcakey_list) / sizeof(hcakey_info);
|
||||
unsigned long long best_keycode;
|
||||
int best_score = -1;
|
||||
int i;
|
||||
|
||||
best_keycode = 0xCC55463930DBE1AB; /* defaults to PSO2 key, most common */
|
||||
|
||||
|
||||
/* find a candidate key */
|
||||
for (i = 0; i < keys_length; i++) {
|
||||
static inline void test_key(hca_codec_data * hca_data, uint64_t key, uint16_t subkey, int *best_score, uint64_t *best_keycode) {
|
||||
int score;
|
||||
unsigned long long keycode = (unsigned long long)hcakey_list[i].key;
|
||||
|
||||
score = test_hca_key(hca_data, keycode);
|
||||
if (subkey) {
|
||||
key = key * ( ((uint64_t)subkey << 16u) | ((uint16_t)~subkey + 2u) );
|
||||
}
|
||||
|
||||
//;VGM_LOG("HCA: test key=%08x%08x, score=%i\n",
|
||||
// (uint32_t)((keycode >> 32) & 0xFFFFFFFF), (uint32_t)(keycode & 0xFFFFFFFF), score);
|
||||
score = test_hca_key(hca_data, (unsigned long long)key);
|
||||
|
||||
//;VGM_LOG("HCA: test key=%08x%08x, subkey=%04x, score=%i\n",
|
||||
// (uint32_t)((key >> 32) & 0xFFFFFFFF), (uint32_t)(key & 0xFFFFFFFF), subkey, score);
|
||||
|
||||
/* wrong key */
|
||||
if (score < 0)
|
||||
continue;
|
||||
return;
|
||||
|
||||
/* score 0 is not trustable, update too if something better is found */
|
||||
if (best_score < 0 || score < best_score || (best_score == 0 && score == 1)) {
|
||||
best_score = score;
|
||||
best_keycode = keycode;
|
||||
}
|
||||
|
||||
/* best possible score */
|
||||
if (score == 1) {
|
||||
break;
|
||||
if (*best_score < 0 || (score < *best_score && score > 0) || (*best_score == 0 && score == 1)) {
|
||||
*best_score = score;
|
||||
*best_keycode = key;
|
||||
}
|
||||
}
|
||||
|
||||
/* Try to find the decryption key from a list. */
|
||||
static void find_hca_key(hca_codec_data * hca_data, unsigned long long * out_keycode) {
|
||||
const size_t keys_length = sizeof(hcakey_list) / sizeof(hcakey_info);
|
||||
int best_score = -1;
|
||||
int i,j;
|
||||
|
||||
*out_keycode = 0xCC55463930DBE1AB; /* defaults to PSO2 key, most common */
|
||||
|
||||
/* find a candidate key */
|
||||
for (i = 0; i < keys_length; i++) {
|
||||
uint64_t key = hcakey_list[i].key;
|
||||
size_t subkeys_size = hcakey_list[i].subkeys_size;
|
||||
const uint16_t *subkeys = hcakey_list[i].subkeys;
|
||||
|
||||
if (subkeys_size > 0) {
|
||||
for (j = 0; j < subkeys_size; j++) {
|
||||
test_key(hca_data, key, subkeys[j], &best_score, out_keycode);
|
||||
if (best_score == 1) /* best possible score */
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
else {
|
||||
test_key(hca_data, key, 0, &best_score, out_keycode);
|
||||
if (best_score == 1) /* best possible score */
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
//;VGM_LOG("HCA: best key=%08x%08x (score=%i)\n",
|
||||
// (uint32_t)((best_keycode >> 32) & 0xFFFFFFFF), (uint32_t)(best_keycode & 0xFFFFFFFF), best_score);
|
||||
// (uint32_t)((*out_keycode >> 32) & 0xFFFFFFFF), (uint32_t)(*out_keycode & 0xFFFFFFFF), best_score);
|
||||
|
||||
VGM_ASSERT(best_score > 1, "HCA: best key=%08x%08x (score=%i)\n",
|
||||
(uint32_t)((best_keycode >> 32) & 0xFFFFFFFF), (uint32_t)(best_keycode & 0xFFFFFFFF), best_score);
|
||||
*out_keycode = best_keycode;
|
||||
(uint32_t)((*out_keycode >> 32) & 0xFFFFFFFF), (uint32_t)(*out_keycode & 0xFFFFFFFF), best_score);
|
||||
}
|
||||
|
@ -1,14 +1,23 @@
|
||||
#ifndef _HCA_KEYS_H_
|
||||
#define _HCA_KEYS_H_
|
||||
|
||||
#include "hca_keys_awb.h"
|
||||
|
||||
typedef struct {
|
||||
uint64_t key;
|
||||
uint64_t key; /* hca key or seed key */
|
||||
const uint16_t *subkeys; /* derivation subkey table for seed key */
|
||||
size_t subkeys_size; /* size of the derivation subkey table */
|
||||
} hcakey_info;
|
||||
|
||||
|
||||
/**
|
||||
* List of known keys, extracted from the game files (mostly found in 2ch.net).
|
||||
* CRI's tools expect an unsigned 64 bit number string, but keys are commonly found online in hex form.
|
||||
* Keys only use 56 bits though, so the upper 8 bits can be ignored.
|
||||
*
|
||||
* ACB+AWB after mid 2018 use a master seed key + a derivation subkey in the AWB (normally 16b LE at 0x0e)
|
||||
* to create the final HCA key, which means there is one key per AWB (so most HCA have a unique key).
|
||||
* vgmstream derives the key if subkey table is provided.
|
||||
*/
|
||||
static const hcakey_info hcakey_list[] = {
|
||||
|
||||
@ -249,6 +258,9 @@ static const hcakey_info hcakey_list[] = {
|
||||
// Onsen Musume: Yunohana Kore Kushon (Android) voices
|
||||
{6667}, // 0000000000001A0B
|
||||
|
||||
/* Dragalia Lost (Android) */
|
||||
{2967411924141, subkeys_dgl, sizeof(subkeys_dgl) / sizeof(subkeys_dgl[0]) }, // 000002B2E7889CAD
|
||||
|
||||
};
|
||||
|
||||
#endif/*_HCA_KEYS_H_*/
|
||||
|
4112
src/meta/hca_keys_awb.h
Normal file
4112
src/meta/hca_keys_awb.h
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user