From 035d187c4d5d96cf6d15237df0c0a20be4e933f1 Mon Sep 17 00:00:00 2001 From: Paul B Mahol Date: Thu, 21 Sep 2023 23:20:20 +0200 Subject: [PATCH] avformat/hca: add support for decryption --- libavcodec/hcadec.c | 120 ++++++++++++++++++++++++++++++++++++++++++-- libavformat/hca.c | 51 ++++++++++++++++--- 2 files changed, 159 insertions(+), 12 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; diff --git a/libavformat/hca.c b/libavformat/hca.c index 74ac00acc0..e796512a62 100644 --- a/libavformat/hca.c +++ b/libavformat/hca.c @@ -19,18 +19,28 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "libavutil/opt.h" #include "libavutil/intreadwrite.h" #include "libavcodec/bytestream.h" #include "avformat.h" #include "internal.h" +#define HCA_MASK 0x7f7f7f7f + +typedef struct HCADemuxContext { + AVClass *class; + int64_t keyl; + int64_t keyh; + int subkey; +} HCADemuxContext; + static int hca_probe(const AVProbeData *p) { - if (AV_RL32(p->buf) != MKTAG('H', 'C', 'A', 0)) + if ((AV_RL32(p->buf) & HCA_MASK) != MKTAG('H', 'C', 'A', 0)) return 0; - if (AV_RL32(p->buf + 8) != MKTAG('f', 'm', 't', 0)) + if ((AV_RL32(p->buf + 8) & HCA_MASK) != MKTAG('f', 'm', 't', 0)) return 0; return AVPROBE_SCORE_MAX / 3; @@ -38,6 +48,7 @@ static int hca_probe(const AVProbeData *p) static int hca_read_header(AVFormatContext *s) { + HCADemuxContext *hca = s->priv_data; AVCodecParameters *par; GetByteContext gb; AVIOContext *pb = s->pb; @@ -60,20 +71,23 @@ static int hca_read_header(AVFormatContext *s) return AVERROR(ENOMEM); par = st->codecpar; - ret = ff_alloc_extradata(par, data_offset); + ret = ff_alloc_extradata(par, data_offset + 10); if (ret < 0) return ret; - ret = avio_read(pb, par->extradata + 8, par->extradata_size - 8); - if (ret < par->extradata_size - 8) + ret = avio_read(pb, par->extradata + 8, par->extradata_size - 8 - 10); + if (ret < par->extradata_size - 8 - 10) return AVERROR(EIO); AV_WL32(par->extradata, MKTAG('H', 'C', 'A', 0)); AV_WB16(par->extradata + 4, version); AV_WB16(par->extradata + 6, data_offset); + AV_WB32(par->extradata + par->extradata_size - 10, hca->keyh); + AV_WB32(par->extradata + par->extradata_size - 6, hca->keyl); + AV_WB16(par->extradata + par->extradata_size - 2, hca->subkey); bytestream2_init(&gb, par->extradata + 8, par->extradata_size - 8); - if (bytestream2_get_le32(&gb) != MKTAG('f', 'm', 't', 0)) + if ((bytestream2_get_le32(&gb) & HCA_MASK) != MKTAG('f', 'm', 't', 0)) return AVERROR_INVALIDDATA; par->codec_type = AVMEDIA_TYPE_AUDIO; @@ -83,7 +97,7 @@ static int hca_read_header(AVFormatContext *s) par->sample_rate = bytestream2_get_be24(&gb); block_count = bytestream2_get_be32(&gb); bytestream2_skip(&gb, 4); - chunk = bytestream2_get_le32(&gb); + chunk = bytestream2_get_le32(&gb) & HCA_MASK; if (chunk == MKTAG('c', 'o', 'm', 'p')) { block_size = bytestream2_get_be16(&gb); } else if (chunk == MKTAG('d', 'e', 'c', 0)) { @@ -113,9 +127,32 @@ static int hca_read_packet(AVFormatContext *s, AVPacket *pkt) return ret; } +#define OFFSET(x) offsetof(HCADemuxContext, x) +static const AVOption hca_options[] = { + { "hca_lowkey", + "Low key used for handling CRI HCA files", OFFSET(keyl), + AV_OPT_TYPE_INT64, {.i64=0}, .min = 0, .max = UINT32_MAX, .flags = AV_OPT_FLAG_DECODING_PARAM, }, + { "hca_highkey", + "High key used for handling CRI HCA files", OFFSET(keyh), + AV_OPT_TYPE_INT64, {.i64=0}, .min = 0, .max = UINT32_MAX, .flags = AV_OPT_FLAG_DECODING_PARAM, }, + { "hca_subkey", + "Subkey used for handling CRI HCA files", OFFSET(subkey), + AV_OPT_TYPE_INT, {.i64=0}, .min = 0, .max = UINT16_MAX, .flags = AV_OPT_FLAG_DECODING_PARAM }, + { NULL }, +}; + +static const AVClass hca_class = { + .class_name = "hca", + .item_name = av_default_item_name, + .option = hca_options, + .version = LIBAVUTIL_VERSION_INT, +}; + const AVInputFormat ff_hca_demuxer = { .name = "hca", .long_name = NULL_IF_CONFIG_SMALL("CRI HCA"), + .priv_class = &hca_class, + .priv_data_size = sizeof(HCADemuxContext), .read_probe = hca_probe, .read_header = hca_read_header, .read_packet = hca_read_packet,