diff mbox series

[FFmpeg-devel,2/4] libavformat/mov: add support for 'cbcs' encryption scheme specified in Common Encryption (CENC) standard

Message ID 20210411190629.17642-1-nachiket.programmer@gmail.com
State New
Headers show
Series [FFmpeg-devel,1/4] libavcodec/adts_header: add frame_length field and avpriv function to parse AAC ADTS header
Related show

Checks

Context Check Description
andriy/x86_make success Make finished
andriy/x86_make_fate success Make fate finished
andriy/PPC64_make success Make finished
andriy/PPC64_make_fate success Make fate finished

Commit Message

Nachiket Tarate April 11, 2021, 7:06 p.m. UTC
https://www.iso.org/standard/68042.html

Signed-off-by: Nachiket Tarate <nachiket.programmer@gmail.com>
---
 libavformat/isom.h |  1 +
 libavformat/mov.c  | 86 ++++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 81 insertions(+), 6 deletions(-)
diff mbox series

Patch

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;