|
|
@@ -37,6 +37,8 @@ |
|
|
|
#include "libavutil/dict.h" |
|
|
|
#include "libavutil/display.h" |
|
|
|
#include "libavutil/opt.h" |
|
|
|
#include "libavutil/aes.h" |
|
|
|
#include "libavutil/sha.h" |
|
|
|
#include "libavutil/timecode.h" |
|
|
|
#include "libavcodec/ac3tab.h" |
|
|
|
#include "avformat.h" |
|
|
@@ -807,6 +809,120 @@ static int mov_read_mdat(MOVContext *c, AVIOContext *pb, MOVAtom atom) |
|
|
|
return 0; /* now go for moov */ |
|
|
|
} |
|
|
|
|
|
|
|
#define DRM_BLOB_SIZE 56 |
|
|
|
|
|
|
|
static int mov_read_adrm(MOVContext *c, AVIOContext *pb, MOVAtom atom) |
|
|
|
{ |
|
|
|
uint8_t intermediate_key[20]; |
|
|
|
uint8_t intermediate_iv[20]; |
|
|
|
uint8_t input[64]; |
|
|
|
uint8_t output[64]; |
|
|
|
uint8_t file_checksum[20]; |
|
|
|
uint8_t calculated_checksum[20]; |
|
|
|
struct AVSHA *sha; |
|
|
|
int i; |
|
|
|
int ret = 0; |
|
|
|
uint8_t *activation_bytes = c->activation_bytes; |
|
|
|
uint8_t *fixed_key = c->audible_fixed_key; |
|
|
|
|
|
|
|
c->aax_mode = 1; |
|
|
|
|
|
|
|
sha = av_sha_alloc(); |
|
|
|
if (!sha) |
|
|
|
return AVERROR(ENOMEM); |
|
|
|
c->aes_decrypt = av_aes_alloc(); |
|
|
|
if (!c->aes_decrypt) { |
|
|
|
ret = AVERROR(ENOMEM); |
|
|
|
goto fail; |
|
|
|
} |
|
|
|
|
|
|
|
/* drm blob processing */ |
|
|
|
avio_read(pb, output, 8); // go to offset 8, absolute postion 0x251 |
|
|
|
avio_read(pb, input, DRM_BLOB_SIZE); |
|
|
|
avio_read(pb, output, 4); // go to offset 4, absolute postion 0x28d |
|
|
|
avio_read(pb, file_checksum, 20); |
|
|
|
|
|
|
|
av_log(c->fc, AV_LOG_INFO, "[aax] file checksum == "); // required by external tools |
|
|
|
for (i = 0; i < 20; i++) |
|
|
|
av_log(sha, AV_LOG_INFO, "%02x", file_checksum[i]); |
|
|
|
av_log(c->fc, AV_LOG_INFO, "\n"); |
|
|
|
|
|
|
|
/* verify activation data */ |
|
|
|
if (!activation_bytes || c->activation_bytes_size != 4) { |
|
|
|
av_log(c->fc, AV_LOG_FATAL, "[aax] activation_bytes option is missing!\n"); |
|
|
|
ret = AVERROR(EINVAL); |
|
|
|
goto fail; |
|
|
|
} |
|
|
|
if (c->activation_bytes_size != 4) { |
|
|
|
av_log(c->fc, AV_LOG_FATAL, "[aax] activation_bytes value needs to be 4 bytes!\n"); |
|
|
|
ret = AVERROR(EINVAL); |
|
|
|
goto fail; |
|
|
|
} |
|
|
|
|
|
|
|
/* verify fixed key */ |
|
|
|
if (c->audible_fixed_key_size != 16) { |
|
|
|
av_log(c->fc, AV_LOG_FATAL, "[aax] audible_fixed_key value needs to be 16 bytes!\n"); |
|
|
|
ret = AVERROR(EINVAL); |
|
|
|
goto fail; |
|
|
|
} |
|
|
|
|
|
|
|
/* AAX (and AAX+) key derivation */ |
|
|
|
av_sha_init(sha, 160); |
|
|
|
av_sha_update(sha, fixed_key, 16); |
|
|
|
av_sha_update(sha, activation_bytes, 4); |
|
|
|
av_sha_final(sha, intermediate_key); |
|
|
|
av_sha_init(sha, 160); |
|
|
|
av_sha_update(sha, fixed_key, 16); |
|
|
|
av_sha_update(sha, intermediate_key, 20); |
|
|
|
av_sha_update(sha, activation_bytes, 4); |
|
|
|
av_sha_final(sha, intermediate_iv); |
|
|
|
av_sha_init(sha, 160); |
|
|
|
av_sha_update(sha, intermediate_key, 16); |
|
|
|
av_sha_update(sha, intermediate_iv, 16); |
|
|
|
av_sha_final(sha, calculated_checksum); |
|
|
|
if (memcmp(calculated_checksum, file_checksum, 20)) { // critical error |
|
|
|
av_log(c->fc, AV_LOG_ERROR, "[aax] mismatch in checksums!\n"); |
|
|
|
ret = AVERROR_INVALIDDATA; |
|
|
|
goto fail; |
|
|
|
} |
|
|
|
av_aes_init(c->aes_decrypt, intermediate_key, 128, 1); |
|
|
|
av_aes_crypt(c->aes_decrypt, output, input, DRM_BLOB_SIZE >> 4, intermediate_iv, 1); |
|
|
|
for (i = 0; i < 4; i++) { |
|
|
|
// file data (in output) is stored in big-endian mode |
|
|
|
if (activation_bytes[i] != output[3 - i]) { // critical error |
|
|
|
av_log(c->fc, AV_LOG_ERROR, "[aax] error in drm blob decryption!\n"); |
|
|
|
ret = AVERROR_INVALIDDATA; |
|
|
|
goto fail; |
|
|
|
} |
|
|
|
} |
|
|
|
memcpy(c->file_key, output + 8, 16); |
|
|
|
memcpy(input, output + 26, 16); |
|
|
|
av_sha_init(sha, 160); |
|
|
|
av_sha_update(sha, input, 16); |
|
|
|
av_sha_update(sha, c->file_key, 16); |
|
|
|
av_sha_update(sha, fixed_key, 16); |
|
|
|
av_sha_final(sha, c->file_iv); |
|
|
|
|
|
|
|
fail: |
|
|
|
av_free(sha); |
|
|
|
|
|
|
|
return ret; |
|
|
|
} |
|
|
|
|
|
|
|
// Audible AAX (and AAX+) bytestream decryption |
|
|
|
static int aax_filter(uint8_t *input, int size, MOVContext *c) |
|
|
|
{ |
|
|
|
int blocks = 0; |
|
|
|
unsigned char iv[16]; |
|
|
|
|
|
|
|
memcpy(iv, c->file_iv, 16); // iv is overwritten |
|
|
|
blocks = size >> 4; // trailing bytes are not encrypted! |
|
|
|
av_aes_init(c->aes_decrypt, c->file_key, 128, 1); |
|
|
|
av_aes_crypt(c->aes_decrypt, input, input, blocks, iv, 1); |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
/* read major brand, minor version and compatible brands and store them as metadata */ |
|
|
|
static int mov_read_ftyp(MOVContext *c, AVIOContext *pb, MOVAtom atom) |
|
|
|
{ |
|
|
@@ -3637,6 +3753,7 @@ static const MOVParseTableEntry mov_default_parse_table[] = { |
|
|
|
{ MKTAG('e','l','s','t'), mov_read_elst }, |
|
|
|
{ MKTAG('e','n','d','a'), mov_read_enda }, |
|
|
|
{ MKTAG('f','i','e','l'), mov_read_fiel }, |
|
|
|
{ MKTAG('a','d','r','m'), mov_read_adrm }, |
|
|
|
{ MKTAG('f','t','y','p'), mov_read_ftyp }, |
|
|
|
{ MKTAG('g','l','b','l'), mov_read_glbl }, |
|
|
|
{ MKTAG('h','d','l','r'), mov_read_hdlr }, |
|
|
@@ -4058,6 +4175,8 @@ static int mov_read_close(AVFormatContext *s) |
|
|
|
} |
|
|
|
av_freep(&mov->fragment_index_data); |
|
|
|
|
|
|
|
av_freep(&mov->aes_decrypt); |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
@@ -4477,6 +4596,9 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt) |
|
|
|
pkt->flags |= sample->flags & AVINDEX_KEYFRAME ? AV_PKT_FLAG_KEY : 0; |
|
|
|
pkt->pos = sample->pos; |
|
|
|
|
|
|
|
if (mov->aax_mode) |
|
|
|
aax_filter(pkt->data, pkt->size, mov); |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
@@ -4590,6 +4712,12 @@ static const AVOption mov_options[] = { |
|
|
|
AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, .flags = FLAGS }, |
|
|
|
{ "export_xmp", "Export full XMP metadata", OFFSET(export_xmp), |
|
|
|
AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, .flags = FLAGS }, |
|
|
|
{ "activation_bytes", "Secret bytes for Audible AAX files", OFFSET(activation_bytes), |
|
|
|
AV_OPT_TYPE_BINARY, .flags = AV_OPT_FLAG_DECODING_PARAM }, |
|
|
|
{ "audible_fixed_key", // extracted from libAAX_SDK.so and AAXSDKWin.dll files! |
|
|
|
"Fixed key used for handling Audible AAX files", OFFSET(audible_fixed_key), |
|
|
|
AV_OPT_TYPE_BINARY, {.str="77214d4b196a87cd520045fd20a51d67"}, |
|
|
|
.flags = AV_OPT_FLAG_DECODING_PARAM }, |
|
|
|
{ NULL }, |
|
|
|
}; |
|
|
|
|
|
|
|