You've already forked FFmpeg
							
							
				mirror of
				https://github.com/FFmpeg/FFmpeg.git
				synced 2025-10-30 23:18:11 +02:00 
			
		
		
		
	avformat/hca: add support for decryption
This commit is contained in:
		| @@ -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; | ||||
|   | ||||
| @@ -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, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user