aboutsummaryrefslogtreecommitdiffstats
path: root/libavcodec/hcadec.c
diff options
context:
space:
mode:
authorPaul B Mahol <onemda@gmail.com>2023-09-21 23:20:20 +0200
committerPaul B Mahol <onemda@gmail.com>2023-09-22 08:52:18 +0200
commit035d187c4d5d96cf6d15237df0c0a20be4e933f1 (patch)
tree2f32c9985c5dddf411f9c20dafe8360d1db7b4ce /libavcodec/hcadec.c
parent63371cde9dd5482b376578558d8e9b44506d4d95 (diff)
downloadffmpeg-035d187c4d5d96cf6d15237df0c0a20be4e933f1.tar.gz
avformat/hca: add support for decryption
Diffstat (limited to 'libavcodec/hcadec.c')
-rw-r--r--libavcodec/hcadec.c120
1 files changed, 115 insertions, 5 deletions
diff --git a/libavcodec/hcadec.c b/libavcodec/hcadec.c
index ebc9d8a8cd..6f277afb96 100644
--- a/libavcodec/hcadec.c
+++ b/libavcodec/hcadec.c
@@ -28,6 +28,7 @@
#include "get_bits.h"
#include "hca_data.h"
+#define HCA_MASK 0x7f7f7f7f
#define MAX_CHANNELS 16
typedef struct ChannelContext {
@@ -50,8 +51,12 @@ typedef struct HCAContext {
ChannelContext ch[MAX_CHANNELS];
uint8_t ath[128];
+ uint8_t cipher[256];
+ uint64_t key;
+ uint16_t subkey;
int ath_type;
+ int ciph_type;
unsigned hfr_group_count;
uint8_t track_count;
uint8_t channel_config;
@@ -65,6 +70,93 @@ typedef struct HCAContext {
AVFloatDSPContext *fdsp;
} HCAContext;
+static void cipher_init56_create_table(uint8_t *r, uint8_t key)
+{
+ const int mul = ((key & 1) << 3) | 5;
+ const int add = (key & 0xE) | 1;
+
+ key >>= 4;
+ for (int i = 0; i < 16; i++) {
+ key = (key * mul + add) & 0xF;
+ r[i] = key;
+ }
+}
+
+static void cipher_init56(uint8_t *cipher, uint64_t keycode)
+{
+ uint8_t base[256], base_r[16], base_c[16], kc[8], seed[16];
+
+ /* 56bit keycode encryption (given as a uint64_t number, but upper 8b aren't used) */
+ /* keycode = keycode - 1 */
+ if (keycode != 0)
+ keycode--;
+
+ /* init keycode table */
+ for (int r = 0; r < (8-1); r++) {
+ kc[r] = keycode & 0xFF;
+ keycode = keycode >> 8;
+ }
+
+ /* init seed table */
+ seed[ 0] = kc[1];
+ seed[ 1] = kc[1] ^ kc[6];
+ seed[ 2] = kc[2] ^ kc[3];
+ seed[ 3] = kc[2];
+ seed[ 4] = kc[2] ^ kc[1];
+ seed[ 5] = kc[3] ^ kc[4];
+ seed[ 6] = kc[3];
+ seed[ 7] = kc[3] ^ kc[2];
+ seed[ 8] = kc[4] ^ kc[5];
+ seed[ 9] = kc[4];
+ seed[10] = kc[4] ^ kc[3];
+ seed[11] = kc[5] ^ kc[6];
+ seed[12] = kc[5];
+ seed[13] = kc[5] ^ kc[4];
+ seed[14] = kc[6] ^ kc[1];
+ seed[15] = kc[6];
+
+ /* init base table */
+ cipher_init56_create_table(base_r, kc[0]);
+ for (int r = 0; r < 16; r++) {
+ uint8_t nb;
+ cipher_init56_create_table(base_c, seed[r]);
+ nb = base_r[r] << 4;
+ for (int c = 0; c < 16; c++)
+ base[r*16 + c] = nb | base_c[c]; /* combine nibbles */
+ }
+
+ /* final shuffle table */
+ {
+ unsigned x = 0;
+ unsigned pos = 1;
+
+ for (int i = 0; i < 256; i++) {
+ x = (x + 17) & 0xFF;
+ if (base[x] != 0 && base[x] != 0xFF)
+ cipher[pos++] = base[x];
+ }
+ cipher[0] = 0;
+ cipher[0xFF] = 0xFF;
+ }
+}
+
+static void cipher_init(uint8_t *cipher, int type, uint64_t keycode, uint16_t subkey)
+{
+ switch (type) {
+ case 56:
+ if (keycode) {
+ if (subkey)
+ keycode = keycode * (((uint64_t)subkey<<16u)|((uint16_t)~subkey+2u));
+ cipher_init56(cipher, keycode);
+ }
+ break;
+ case 0:
+ for (int i = 0; i < 256; i++)
+ cipher[i] = i;
+ break;
+ }
+}
+
static void ath_init1(uint8_t *ath, int sample_rate)
{
unsigned int index;
@@ -124,13 +216,13 @@ static int init_hca(AVCodecContext *avctx, const uint8_t *extradata,
c->ath_type = version >= 0x200 ? 0 : 1;
- if (bytestream2_get_be32u(gb) != MKBETAG('f', 'm', 't', 0))
+ if ((bytestream2_get_be32u(gb) & HCA_MASK) != MKBETAG('f', 'm', 't', 0))
return AVERROR_INVALIDDATA;
bytestream2_skipu(gb, 4);
bytestream2_skipu(gb, 4);
bytestream2_skipu(gb, 4);
- chunk = bytestream2_get_be32u(gb);
+ chunk = bytestream2_get_be32u(gb) & HCA_MASK;
if (chunk == MKBETAG('c', 'o', 'm', 'p')) {
bytestream2_skipu(gb, 2);
bytestream2_skipu(gb, 1);
@@ -160,7 +252,7 @@ static int init_hca(AVCodecContext *avctx, const uint8_t *extradata,
return AVERROR_INVALIDDATA;
while (bytestream2_get_bytes_left(gb) >= 4) {
- chunk = bytestream2_get_be32u(gb);
+ chunk = bytestream2_get_be32u(gb) & HCA_MASK;
if (chunk == MKBETAG('v', 'b', 'r', 0)) {
bytestream2_skip(gb, 2 + 2);
} else if (chunk == MKBETAG('a', 't', 'h', 0)) {
@@ -170,7 +262,7 @@ static int init_hca(AVCodecContext *avctx, const uint8_t *extradata,
} else if (chunk == MKBETAG('c', 'o', 'm', 'm')) {
bytestream2_skip(gb, bytestream2_get_byte(gb) * 8);
} else if (chunk == MKBETAG('c', 'i', 'p', 'h')) {
- bytestream2_skip(gb, 2);
+ c->ciph_type = bytestream2_get_be16(gb);
} else if (chunk == MKBETAG('l', 'o', 'o', 'p')) {
bytestream2_skip(gb, 4 + 4 + 2 + 2);
} else if (chunk == MKBETAG('p', 'a', 'd', 0)) {
@@ -180,6 +272,14 @@ static int init_hca(AVCodecContext *avctx, const uint8_t *extradata,
}
}
+ if (bytestream2_get_bytes_left(gb) >= 10) {
+ bytestream2_skip(gb, bytestream2_get_bytes_left(gb) - 10);
+ c->key = bytestream2_get_be64u(gb);
+ c->subkey = bytestream2_get_be16u(gb);
+ }
+
+ cipher_init(c->cipher, c->ciph_type, c->key, c->subkey);
+
ret = ath_init(c->ath, c->ath_type, avctx->sample_rate);
if (ret < 0)
return ret;
@@ -420,7 +520,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame,
return AVERROR_INVALIDDATA;
if (AV_RN16(avpkt->data) != 0xFFFF) {
- if (AV_RL32(avpkt->data) != MKTAG('H','C','A',0)) {
+ if ((AV_RL32(avpkt->data)) != MKTAG('H','C','A',0)) {
return AVERROR_INVALIDDATA;
} else if (AV_RB16(avpkt->data + 6) <= avpkt->size) {
ret = init_hca(avctx, avpkt->data, AV_RB16(avpkt->data + 6));
@@ -434,6 +534,16 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame,
}
}
+ if (c->key || c->subkey) {
+ uint8_t *data, *cipher = c->cipher;
+
+ if ((ret = av_packet_make_writable(avpkt)) < 0)
+ return ret;
+ data = avpkt->data;
+ for (int n = 0; n < avpkt->size; n++)
+ data[n] = cipher[data[n]];
+ }
+
if (avctx->err_recognition & AV_EF_CRCCHECK) {
if (av_crc(c->crc_table, 0, avpkt->data + offset, avpkt->size - offset))
return AVERROR_INVALIDDATA;