[FFmpeg-devel] [PATCH 2/4] libavformat/mov: add support for 'cbcs' encryption scheme specified in Common Encryption (CENC) standard
Nachiket Tarate
nachiket.programmer at gmail.com
Sun Apr 11 22:06:29 EEST 2021
https://www.iso.org/standard/68042.html
Signed-off-by: Nachiket Tarate <nachiket.programmer at gmail.com>
---
libavformat/isom.h | 1 +
libavformat/mov.c | 86 ++++++++++++++++++++++++++++++++++++++++++----
2 files changed, 81 insertions(+), 6 deletions(-)
diff --git a/libavformat/isom.h b/libavformat/isom.h
index 5a6d504090..5a17cb9c07 100644
--- a/libavformat/isom.h
+++ b/libavformat/isom.h
@@ -237,6 +237,7 @@ typedef struct MOVStreamContext {
int has_sidx; // If there is an sidx entry for this stream.
struct {
struct AVAESCTR* aes_ctr;
+ struct AVAES *aes_ctx;
unsigned int per_sample_iv_size; // Either 0, 8, or 16.
AVEncryptionInfo *default_encrypted_sample;
MOVEncryptionIndex *encryption_index;
diff --git a/libavformat/mov.c b/libavformat/mov.c
index 43c55ef4a4..5eda5f3822 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -6623,15 +6623,10 @@ static int mov_read_dfla(MOVContext *c, AVIOContext *pb, MOVAtom atom)
return 0;
}
-static int cenc_decrypt(MOVContext *c, MOVStreamContext *sc, AVEncryptionInfo *sample, uint8_t *input, int size)
+static int cenc_scheme_decrypt(MOVContext *c, MOVStreamContext *sc, AVEncryptionInfo *sample, uint8_t *input, int size)
{
int i, ret;
- if (sample->scheme != MKBETAG('c','e','n','c') || sample->crypt_byte_block != 0 || sample->skip_byte_block != 0) {
- av_log(c->fc, AV_LOG_ERROR, "Only the 'cenc' encryption scheme is supported\n");
- return AVERROR_PATCHWELCOME;
- }
-
if (!sc->cenc.aes_ctr) {
/* initialize the cipher */
sc->cenc.aes_ctr = av_aes_ctr_alloc();
@@ -6654,6 +6649,7 @@ static int cenc_decrypt(MOVContext *c, MOVStreamContext *sc, AVEncryptionInfo *s
}
for (i = 0; i < sample->subsample_count; i++) {
+
if (sample->subsamples[i].bytes_of_clear_data + sample->subsamples[i].bytes_of_protected_data > size) {
av_log(c->fc, AV_LOG_ERROR, "subsample size exceeds the packet size left\n");
return AVERROR_INVALIDDATA;
@@ -6677,6 +6673,84 @@ static int cenc_decrypt(MOVContext *c, MOVStreamContext *sc, AVEncryptionInfo *s
return 0;
}
+static int cbcs_scheme_decrypt(MOVContext *c, MOVStreamContext *sc, AVEncryptionInfo *sample, uint8_t *input, int size)
+{
+ int i, ret;
+ int encrypted_blocks;
+ uint8_t iv[16];
+
+ if (!sc->cenc.aes_ctx) {
+ /* initialize the cipher */
+ sc->cenc.aes_ctx = av_aes_alloc();
+ if (!sc->cenc.aes_ctx) {
+ return AVERROR(ENOMEM);
+ }
+
+ ret = av_aes_init(sc->cenc.aes_ctx, c->decryption_key, 16 * 8, 1);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
+ /* whole-block full sample encryption */
+ if (!sample->subsample_count) {
+ /* decrypt the whole packet */
+ memcpy(iv, sample->iv, 16);
+ av_aes_crypt(sc->cenc.aes_ctx, input, input, size/16, iv, 1);
+ return 0;
+ }
+
+ for (i = 0; i < sample->subsample_count; i++) {
+
+ if (sample->subsamples[i].bytes_of_clear_data + sample->subsamples[i].bytes_of_protected_data > size) {
+ av_log(c->fc, AV_LOG_ERROR, "subsample size exceeds the packet size left\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ /* skip the clear bytes */
+ input += sample->subsamples[i].bytes_of_clear_data;
+ size -= sample->subsamples[i].bytes_of_clear_data;
+
+ /* decrypt the encrypted bytes */
+ memcpy(iv, sample->iv, 16);
+ encrypted_blocks = sample->subsamples[i].bytes_of_protected_data/16;
+ if (encrypted_blocks > 0) {
+ /* pattern encryption */
+ if (sample->crypt_byte_block > 0 && sample->skip_byte_block > 0) {
+ uint8_t *data = input;
+ int rem_bytes = sample->subsamples[i].bytes_of_protected_data;
+ while (rem_bytes > 0) {
+ if (rem_bytes > 16*sample->crypt_byte_block) {
+ av_aes_crypt(sc->cenc.aes_ctx, data, data, sample->crypt_byte_block, iv, 1);
+ data += 16*sample->crypt_byte_block;
+ rem_bytes -= 16*sample->crypt_byte_block;
+ }
+ data += FFMIN(16*sample->skip_byte_block, rem_bytes);
+ rem_bytes -= FFMIN(16*sample->skip_byte_block, rem_bytes);
+ }
+ } else {
+ av_aes_crypt(sc->cenc.aes_ctx, input, input, encrypted_blocks, iv, 1);
+ }
+ }
+ input += sample->subsamples[i].bytes_of_protected_data;
+ size -= sample->subsamples[i].bytes_of_protected_data;
+ }
+
+ return 0;
+}
+
+static int cenc_decrypt(MOVContext *c, MOVStreamContext *sc, AVEncryptionInfo *sample, uint8_t *input, int size)
+{
+ if (sample->scheme == MKBETAG('c','e','n','c') && sample->crypt_byte_block == 0 && sample->skip_byte_block == 0) {
+ return cenc_scheme_decrypt(c, sc, sample, input, size);
+ } else if (sample->scheme == MKBETAG('c','b','c','s')) {
+ return cbcs_scheme_decrypt(c, sc, sample, input, size);
+ } else {
+ av_log(c->fc, AV_LOG_ERROR, "Only 'cenc' and 'cbcs' encryption schemes are supported\n");
+ return AVERROR_PATCHWELCOME;
+ }
+}
+
static int cenc_filter(MOVContext *mov, AVStream* st, MOVStreamContext *sc, AVPacket *pkt, int current_index)
{
MOVFragmentStreamInfo *frag_stream_info;
--
2.17.1
More information about the ffmpeg-devel
mailing list