mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-02-07 15:01:24 +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)
|
- .adx: .adxkey (derived 6 byte key, in start/mult/add format)
|
||||||
- .ahx: .ahxkey (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)
|
- .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)
|
- .fsb: .fsbkey (decryption key, in hex)
|
||||||
|
|
||||||
The key file can be ".(ext)key" (for the whole folder), or "(name).(ext)key"
|
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
|
- AAC
|
||||||
- Bink
|
- Bink
|
||||||
- AC3/SPDIF
|
- AC3/SPDIF
|
||||||
- Xiph Opus (Ogg, Switch)
|
- Xiph Opus (Ogg, Switch, EA, UE4)
|
||||||
- Xiph CELT (FSB)
|
- Xiph CELT (FSB)
|
||||||
- Musepack
|
- Musepack
|
||||||
- FLAC
|
- FLAC
|
||||||
|
@ -204,6 +204,10 @@
|
|||||||
RelativePath=".\meta\hca_keys.h"
|
RelativePath=".\meta\hca_keys.h"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\meta\hca_keys_awb.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath=".\meta\fsb_keys.h"
|
RelativePath=".\meta\fsb_keys.h"
|
||||||
>
|
>
|
||||||
|
@ -107,9 +107,11 @@
|
|||||||
<ClInclude Include="meta\ps2_psh_streamfile.h" />
|
<ClInclude Include="meta\ps2_psh_streamfile.h" />
|
||||||
<ClInclude Include="meta\opus_interleave_streamfile.h" />
|
<ClInclude Include="meta\opus_interleave_streamfile.h" />
|
||||||
<ClInclude Include="meta\sqex_scd_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\meta.h" />
|
||||||
<ClInclude Include="meta\hca_keys.h" />
|
<ClInclude Include="meta\hca_keys.h" />
|
||||||
|
<ClInclude Include="meta\hca_keys_awb.h" />
|
||||||
<ClInclude Include="meta\fsb_keys.h" />
|
<ClInclude Include="meta\fsb_keys.h" />
|
||||||
<ClInclude Include="coding\acm_decoder_libacm.h" />
|
<ClInclude Include="coding\acm_decoder_libacm.h" />
|
||||||
<ClInclude Include="coding\coding.h" />
|
<ClInclude Include="coding\coding.h" />
|
||||||
|
@ -113,6 +113,9 @@
|
|||||||
<ClInclude Include="meta\hca_keys.h">
|
<ClInclude Include="meta\hca_keys.h">
|
||||||
<Filter>meta\Header Files</Filter>
|
<Filter>meta\Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="meta\hca_keys_awb.h">
|
||||||
|
<Filter>meta\Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="meta\fsb_keys.h">
|
<ClInclude Include="meta\fsb_keys.h">
|
||||||
<Filter>meta\Header Files</Filter>
|
<Filter>meta\Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
@ -1306,7 +1309,7 @@
|
|||||||
<ClCompile Include="meta\sqex_scd.c">
|
<ClCompile Include="meta\sqex_scd.c">
|
||||||
<Filter>meta\Source Files</Filter>
|
<Filter>meta\Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="meta\sqex_scd_sscfs.c">
|
<ClCompile Include="meta\sqex_scd_sscf.c">
|
||||||
<Filter>meta\Source Files</Filter>
|
<Filter>meta\Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="meta\sqex_sead.c">
|
<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 */
|
/* find decryption key in external file or preloaded list */
|
||||||
if (hca_data->info.encryptionEnabled) {
|
if (hca_data->info.encryptionEnabled) {
|
||||||
uint8_t keybuf[8];
|
uint8_t keybuf[0x08+0x02];
|
||||||
if (read_key_file(keybuf, 8, streamFile) == 8) {
|
size_t keysize;
|
||||||
|
|
||||||
|
keysize = read_key_file(keybuf, 0x08+0x04, streamFile);
|
||||||
|
if (keysize == 0x08) { /* standard */
|
||||||
keycode = (uint64_t)get_64bitBE(keybuf+0x00);
|
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);
|
find_hca_key(hca_data, &keycode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,46 +72,61 @@ fail:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
if (subkey) {
|
||||||
|
key = key * ( ((uint64_t)subkey << 16u) | ((uint16_t)~subkey + 2u) );
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* score 0 is not trustable, update too if something better is found */
|
||||||
|
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. */
|
/* Try to find the decryption key from a list. */
|
||||||
static void find_hca_key(hca_codec_data * hca_data, unsigned long long * out_keycode) {
|
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);
|
const size_t keys_length = sizeof(hcakey_list) / sizeof(hcakey_info);
|
||||||
unsigned long long best_keycode;
|
|
||||||
int best_score = -1;
|
int best_score = -1;
|
||||||
int i;
|
int i,j;
|
||||||
|
|
||||||
best_keycode = 0xCC55463930DBE1AB; /* defaults to PSO2 key, most common */
|
|
||||||
|
|
||||||
|
*out_keycode = 0xCC55463930DBE1AB; /* defaults to PSO2 key, most common */
|
||||||
|
|
||||||
/* find a candidate key */
|
/* find a candidate key */
|
||||||
for (i = 0; i < keys_length; i++) {
|
for (i = 0; i < keys_length; i++) {
|
||||||
int score;
|
uint64_t key = hcakey_list[i].key;
|
||||||
unsigned long long keycode = (unsigned long long)hcakey_list[i].key;
|
size_t subkeys_size = hcakey_list[i].subkeys_size;
|
||||||
|
const uint16_t *subkeys = hcakey_list[i].subkeys;
|
||||||
|
|
||||||
score = test_hca_key(hca_data, keycode);
|
if (subkeys_size > 0) {
|
||||||
|
for (j = 0; j < subkeys_size; j++) {
|
||||||
//;VGM_LOG("HCA: test key=%08x%08x, score=%i\n",
|
test_key(hca_data, key, subkeys[j], &best_score, out_keycode);
|
||||||
// (uint32_t)((keycode >> 32) & 0xFFFFFFFF), (uint32_t)(keycode & 0xFFFFFFFF), score);
|
if (best_score == 1) /* best possible score */
|
||||||
|
goto done;
|
||||||
/* wrong key */
|
}
|
||||||
if (score < 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/* 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;
|
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
/* best possible score */
|
test_key(hca_data, key, 0, &best_score, out_keycode);
|
||||||
if (score == 1) {
|
if (best_score == 1) /* best possible score */
|
||||||
break;
|
goto done;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
//;VGM_LOG("HCA: best key=%08x%08x (score=%i)\n",
|
//;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",
|
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);
|
(uint32_t)((*out_keycode >> 32) & 0xFFFFFFFF), (uint32_t)(*out_keycode & 0xFFFFFFFF), best_score);
|
||||||
*out_keycode = best_keycode;
|
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,23 @@
|
|||||||
#ifndef _HCA_KEYS_H_
|
#ifndef _HCA_KEYS_H_
|
||||||
#define _HCA_KEYS_H_
|
#define _HCA_KEYS_H_
|
||||||
|
|
||||||
|
#include "hca_keys_awb.h"
|
||||||
|
|
||||||
typedef struct {
|
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;
|
} hcakey_info;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of known keys, extracted from the game files (mostly found in 2ch.net).
|
* 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.
|
* 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.
|
* 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[] = {
|
static const hcakey_info hcakey_list[] = {
|
||||||
|
|
||||||
@ -249,6 +258,9 @@ static const hcakey_info hcakey_list[] = {
|
|||||||
// Onsen Musume: Yunohana Kore Kushon (Android) voices
|
// Onsen Musume: Yunohana Kore Kushon (Android) voices
|
||||||
{6667}, // 0000000000001A0B
|
{6667}, // 0000000000001A0B
|
||||||
|
|
||||||
|
/* Dragalia Lost (Android) */
|
||||||
|
{2967411924141, subkeys_dgl, sizeof(subkeys_dgl) / sizeof(subkeys_dgl[0]) }, // 000002B2E7889CAD
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif/*_HCA_KEYS_H_*/
|
#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