diff options
author | Paul B Mahol <onemda@gmail.com> | 2023-09-21 23:20:20 +0200 |
---|---|---|
committer | Paul B Mahol <onemda@gmail.com> | 2023-09-22 08:52:18 +0200 |
commit | 035d187c4d5d96cf6d15237df0c0a20be4e933f1 (patch) | |
tree | 2f32c9985c5dddf411f9c20dafe8360d1db7b4ce /libavcodec/hcadec.c | |
parent | 63371cde9dd5482b376578558d8e9b44506d4d95 (diff) | |
download | ffmpeg-035d187c4d5d96cf6d15237df0c0a20be4e933f1.tar.gz |
avformat/hca: add support for decryption
Diffstat (limited to 'libavcodec/hcadec.c')
-rw-r--r-- | libavcodec/hcadec.c | 120 |
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; |