Add new HCA key derivation

Thanks to FZFalzar, Thealexbarney and hozuki for the key and algorithm
This commit is contained in:
bnnm 2018-10-13 19:53:25 +02:00
parent ec0043bf6b
commit cca676bb0f
7 changed files with 4191 additions and 33 deletions

View File

@ -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

View File

@ -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"
> >

View File

@ -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" />

View File

@ -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">

View File

@ -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;
} }

View File

@ -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

File diff suppressed because it is too large Load Diff