mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2024-12-23 12:43:46 +02:00
avformat/hca: add support for decryption
This commit is contained in:
parent
63371cde9d
commit
035d187c4d
@ -28,6 +28,7 @@
|
|||||||
#include "get_bits.h"
|
#include "get_bits.h"
|
||||||
#include "hca_data.h"
|
#include "hca_data.h"
|
||||||
|
|
||||||
|
#define HCA_MASK 0x7f7f7f7f
|
||||||
#define MAX_CHANNELS 16
|
#define MAX_CHANNELS 16
|
||||||
|
|
||||||
typedef struct ChannelContext {
|
typedef struct ChannelContext {
|
||||||
@ -50,8 +51,12 @@ typedef struct HCAContext {
|
|||||||
ChannelContext ch[MAX_CHANNELS];
|
ChannelContext ch[MAX_CHANNELS];
|
||||||
|
|
||||||
uint8_t ath[128];
|
uint8_t ath[128];
|
||||||
|
uint8_t cipher[256];
|
||||||
|
uint64_t key;
|
||||||
|
uint16_t subkey;
|
||||||
|
|
||||||
int ath_type;
|
int ath_type;
|
||||||
|
int ciph_type;
|
||||||
unsigned hfr_group_count;
|
unsigned hfr_group_count;
|
||||||
uint8_t track_count;
|
uint8_t track_count;
|
||||||
uint8_t channel_config;
|
uint8_t channel_config;
|
||||||
@ -65,6 +70,93 @@ typedef struct HCAContext {
|
|||||||
AVFloatDSPContext *fdsp;
|
AVFloatDSPContext *fdsp;
|
||||||
} HCAContext;
|
} 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)
|
static void ath_init1(uint8_t *ath, int sample_rate)
|
||||||
{
|
{
|
||||||
unsigned int index;
|
unsigned int index;
|
||||||
@ -124,13 +216,13 @@ static int init_hca(AVCodecContext *avctx, const uint8_t *extradata,
|
|||||||
|
|
||||||
c->ath_type = version >= 0x200 ? 0 : 1;
|
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;
|
return AVERROR_INVALIDDATA;
|
||||||
bytestream2_skipu(gb, 4);
|
bytestream2_skipu(gb, 4);
|
||||||
bytestream2_skipu(gb, 4);
|
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')) {
|
if (chunk == MKBETAG('c', 'o', 'm', 'p')) {
|
||||||
bytestream2_skipu(gb, 2);
|
bytestream2_skipu(gb, 2);
|
||||||
bytestream2_skipu(gb, 1);
|
bytestream2_skipu(gb, 1);
|
||||||
@ -160,7 +252,7 @@ static int init_hca(AVCodecContext *avctx, const uint8_t *extradata,
|
|||||||
return AVERROR_INVALIDDATA;
|
return AVERROR_INVALIDDATA;
|
||||||
|
|
||||||
while (bytestream2_get_bytes_left(gb) >= 4) {
|
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)) {
|
if (chunk == MKBETAG('v', 'b', 'r', 0)) {
|
||||||
bytestream2_skip(gb, 2 + 2);
|
bytestream2_skip(gb, 2 + 2);
|
||||||
} else if (chunk == MKBETAG('a', 't', 'h', 0)) {
|
} 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')) {
|
} else if (chunk == MKBETAG('c', 'o', 'm', 'm')) {
|
||||||
bytestream2_skip(gb, bytestream2_get_byte(gb) * 8);
|
bytestream2_skip(gb, bytestream2_get_byte(gb) * 8);
|
||||||
} else if (chunk == MKBETAG('c', 'i', 'p', 'h')) {
|
} 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')) {
|
} else if (chunk == MKBETAG('l', 'o', 'o', 'p')) {
|
||||||
bytestream2_skip(gb, 4 + 4 + 2 + 2);
|
bytestream2_skip(gb, 4 + 4 + 2 + 2);
|
||||||
} else if (chunk == MKBETAG('p', 'a', 'd', 0)) {
|
} 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);
|
ret = ath_init(c->ath, c->ath_type, avctx->sample_rate);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
@ -420,7 +520,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame,
|
|||||||
return AVERROR_INVALIDDATA;
|
return AVERROR_INVALIDDATA;
|
||||||
|
|
||||||
if (AV_RN16(avpkt->data) != 0xFFFF) {
|
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;
|
return AVERROR_INVALIDDATA;
|
||||||
} else if (AV_RB16(avpkt->data + 6) <= avpkt->size) {
|
} else if (AV_RB16(avpkt->data + 6) <= avpkt->size) {
|
||||||
ret = init_hca(avctx, avpkt->data, AV_RB16(avpkt->data + 6));
|
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 (avctx->err_recognition & AV_EF_CRCCHECK) {
|
||||||
if (av_crc(c->crc_table, 0, avpkt->data + offset, avpkt->size - offset))
|
if (av_crc(c->crc_table, 0, avpkt->data + offset, avpkt->size - offset))
|
||||||
return AVERROR_INVALIDDATA;
|
return AVERROR_INVALIDDATA;
|
||||||
|
@ -19,18 +19,28 @@
|
|||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "libavutil/opt.h"
|
||||||
#include "libavutil/intreadwrite.h"
|
#include "libavutil/intreadwrite.h"
|
||||||
#include "libavcodec/bytestream.h"
|
#include "libavcodec/bytestream.h"
|
||||||
|
|
||||||
#include "avformat.h"
|
#include "avformat.h"
|
||||||
#include "internal.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)
|
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;
|
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 0;
|
||||||
|
|
||||||
return AVPROBE_SCORE_MAX / 3;
|
return AVPROBE_SCORE_MAX / 3;
|
||||||
@ -38,6 +48,7 @@ static int hca_probe(const AVProbeData *p)
|
|||||||
|
|
||||||
static int hca_read_header(AVFormatContext *s)
|
static int hca_read_header(AVFormatContext *s)
|
||||||
{
|
{
|
||||||
|
HCADemuxContext *hca = s->priv_data;
|
||||||
AVCodecParameters *par;
|
AVCodecParameters *par;
|
||||||
GetByteContext gb;
|
GetByteContext gb;
|
||||||
AVIOContext *pb = s->pb;
|
AVIOContext *pb = s->pb;
|
||||||
@ -60,20 +71,23 @@ static int hca_read_header(AVFormatContext *s)
|
|||||||
return AVERROR(ENOMEM);
|
return AVERROR(ENOMEM);
|
||||||
|
|
||||||
par = st->codecpar;
|
par = st->codecpar;
|
||||||
ret = ff_alloc_extradata(par, data_offset);
|
ret = ff_alloc_extradata(par, data_offset + 10);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = avio_read(pb, par->extradata + 8, par->extradata_size - 8);
|
ret = avio_read(pb, par->extradata + 8, par->extradata_size - 8 - 10);
|
||||||
if (ret < par->extradata_size - 8)
|
if (ret < par->extradata_size - 8 - 10)
|
||||||
return AVERROR(EIO);
|
return AVERROR(EIO);
|
||||||
AV_WL32(par->extradata, MKTAG('H', 'C', 'A', 0));
|
AV_WL32(par->extradata, MKTAG('H', 'C', 'A', 0));
|
||||||
AV_WB16(par->extradata + 4, version);
|
AV_WB16(par->extradata + 4, version);
|
||||||
AV_WB16(par->extradata + 6, data_offset);
|
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);
|
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;
|
return AVERROR_INVALIDDATA;
|
||||||
|
|
||||||
par->codec_type = AVMEDIA_TYPE_AUDIO;
|
par->codec_type = AVMEDIA_TYPE_AUDIO;
|
||||||
@ -83,7 +97,7 @@ static int hca_read_header(AVFormatContext *s)
|
|||||||
par->sample_rate = bytestream2_get_be24(&gb);
|
par->sample_rate = bytestream2_get_be24(&gb);
|
||||||
block_count = bytestream2_get_be32(&gb);
|
block_count = bytestream2_get_be32(&gb);
|
||||||
bytestream2_skip(&gb, 4);
|
bytestream2_skip(&gb, 4);
|
||||||
chunk = bytestream2_get_le32(&gb);
|
chunk = bytestream2_get_le32(&gb) & HCA_MASK;
|
||||||
if (chunk == MKTAG('c', 'o', 'm', 'p')) {
|
if (chunk == MKTAG('c', 'o', 'm', 'p')) {
|
||||||
block_size = bytestream2_get_be16(&gb);
|
block_size = bytestream2_get_be16(&gb);
|
||||||
} else if (chunk == MKTAG('d', 'e', 'c', 0)) {
|
} else if (chunk == MKTAG('d', 'e', 'c', 0)) {
|
||||||
@ -113,9 +127,32 @@ static int hca_read_packet(AVFormatContext *s, AVPacket *pkt)
|
|||||||
return ret;
|
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 = {
|
const AVInputFormat ff_hca_demuxer = {
|
||||||
.name = "hca",
|
.name = "hca",
|
||||||
.long_name = NULL_IF_CONFIG_SMALL("CRI HCA"),
|
.long_name = NULL_IF_CONFIG_SMALL("CRI HCA"),
|
||||||
|
.priv_class = &hca_class,
|
||||||
|
.priv_data_size = sizeof(HCADemuxContext),
|
||||||
.read_probe = hca_probe,
|
.read_probe = hca_probe,
|
||||||
.read_header = hca_read_header,
|
.read_header = hca_read_header,
|
||||||
.read_packet = hca_read_packet,
|
.read_packet = hca_read_packet,
|
||||||
|
Loading…
Reference in New Issue
Block a user