diff mbox

[FFmpeg-devel,RFC] avcodec/avcodec.h: Add encryption info side data

Message ID CAO7y9i_3ZDgpuJRvwORzc577_x2t+h-CoA0hwkHmVGMQXJ4aKw@mail.gmail.com
State Superseded
Headers show

Commit Message

Jacob Trimble Dec. 20, 2017, 8:07 p.m. UTC
On Tue, Dec 19, 2017 at 3:05 PM, wm4 <nfxjfg@googlemail.com> wrote:
> On Tue, 19 Dec 2017 14:20:38 -0800
> Jacob Trimble <modmaker-at-google.com@ffmpeg.org> wrote:
>
>> > I don't think this is sane. So far, side data could simply be copied
>> > with memcpy, and having pointers to non-static data in side data would
>> > break this completely
>>
>> Then how about storing the data like it is now (the encryption info
>> followed by the subsample array), but not have a pointer?  Then there
>> will be several helper methods to get the subsample array from the
>> side-data.  For example,
>> av_encryption_info_get_subsamples(AVPacketEncryptionInfo*) or
>> av_encryption_info_get_subsamples(AVPacket*) (since there will only be
>> one instance of it).
>
> I guess that would work? Not particularly fond of the idea anyway. I
> think the functions would probably work on the side data byte array,
> maybe.

I'm not fond of this either, but I can't think of a way to allow a
dynamic number of elements while supporting memcpy and not requiring
the app to "parse" the side-data.

So here is may latest attempt.  This has a binary format inside the
side-data that allows memcpy to work, but there is a public struct
that apps will interact with.  There are two methods used to convert
between the two so the app doesn't have to.  Even though this is a
binary format, it is not actually a wire format since the subsamples
are stored as-is, so they are host byte ordered.  Also, as Michael
requested, this uses dynamic sized key ID and IV.

Comments

wm4 Dec. 20, 2017, 8:23 p.m. UTC | #1
On Wed, 20 Dec 2017 12:07:09 -0800
Jacob Trimble <modmaker-at-google.com@ffmpeg.org> wrote:

> On Tue, Dec 19, 2017 at 3:05 PM, wm4 <nfxjfg@googlemail.com> wrote:
> > On Tue, 19 Dec 2017 14:20:38 -0800
> > Jacob Trimble <modmaker-at-google.com@ffmpeg.org> wrote:
> >
> >> > I don't think this is sane. So far, side data could simply be copied
> >> > with memcpy, and having pointers to non-static data in side data would
> >> > break this completely
> >>
> >> Then how about storing the data like it is now (the encryption info
> >> followed by the subsample array), but not have a pointer?  Then there
> >> will be several helper methods to get the subsample array from the
> >> side-data.  For example,
> >> av_encryption_info_get_subsamples(AVPacketEncryptionInfo*) or
> >> av_encryption_info_get_subsamples(AVPacket*) (since there will only be
> >> one instance of it).
> >
> > I guess that would work? Not particularly fond of the idea anyway. I
> > think the functions would probably work on the side data byte array,
> > maybe.
> 
> I'm not fond of this either, but I can't think of a way to allow a
> dynamic number of elements while supporting memcpy and not requiring
> the app to "parse" the side-data.
> 
> So here is may latest attempt.  This has a binary format inside the
> side-data that allows memcpy to work, but there is a public struct
> that apps will interact with.  There are two methods used to convert
> between the two so the app doesn't have to.  Even though this is a
> binary format, it is not actually a wire format since the subsamples
> are stored as-is, so they are host byte ordered.  Also, as Michael
> requested, this uses dynamic sized key ID and IV.

I think your new patch idea is pretty sane. I'd explicitly document
that the encryption side data format is not part of the ABI, and that
applications must access it with the av_encryption_info_*() functions.

Also I think it would be better to avoid the memory allocation
messiness by copying all data, instead of implicitly referencing the
AVPacket data. Also provide a free function. (That is assuming you're
fine with a copy - creating AVPacket references copies all side data
anyway, so the whole code base assumes side data copies are cheap.)

If AVEncryptionInfo ever needs to be extended, it would also be better
to just let av_encryption_info_get() return a newly allocated struct,
and to exclude the size of the struct from the ABI. Then adding new
fields would not require waiting for an ABI bump.
diff mbox

Patch

From 38c480a470d7f107945180968a5db237c671f7d0 Mon Sep 17 00:00:00 2001
From: Jacob Trimble <modmaker@google.com>
Date: Tue, 5 Dec 2017 14:52:22 -0800
Subject: [PATCH] avcodec/avcodec.h: Add encryption info side data.

This new side-data will contain info on how a packet is encrypted.
This allows the app to handle packet decryption.

Signed-off-by: Jacob Trimble <modmaker@google.com>
---
 libavcodec/Makefile          |   2 +
 libavcodec/avcodec.h         |  13 ++++++
 libavcodec/encryption_info.c |  70 +++++++++++++++++++++++++++++
 libavcodec/encryption_info.h | 105 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 190 insertions(+)
 create mode 100644 libavcodec/encryption_info.c
 create mode 100644 libavcodec/encryption_info.h

diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index ab7893f560..11ad642c6c 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -10,6 +10,7 @@  HEADERS = ac3_parser.h                                                  \
           dirac.h                                                       \
           dv_profile.h                                                  \
           dxva2.h                                                       \
+          encryption_info.h                                             \
           jni.h                                                         \
           mediacodec.h                                                  \
           qsv.h                                                         \
@@ -36,6 +37,7 @@  OBJS = ac3_parser.o                                                     \
        dirac.o                                                          \
        dv_profile.o                                                     \
        encode.o                                                         \
+       encryption_info.o                                                \
        imgconvert.o                                                     \
        jni.o                                                            \
        mathtables.o                                                     \
diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index 5db6a81320..7a5bba2687 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -1327,6 +1327,19 @@  enum AVPacketSideDataType {
      */
     AV_PKT_DATA_A53_CC,
 
+    /**
+     * This side data is encryption "initialization data".
+     * For MP4 this is the entire 'pssh' box.
+     * For WebM this is the key ID.
+     */
+    AV_PKT_DATA_ENCRYPTION_INIT_DATA,
+
+    /**
+     * This side data contains encryption info for how to decrypt the packet.
+     * Use av_encryption_info_get to get the info out of this side data.
+     */
+    AV_PKT_DATA_ENCRYPTION_INFO,
+
     /**
      * The number of side data types.
      * This is not part of the public API/ABI in the sense that it may
diff --git a/libavcodec/encryption_info.c b/libavcodec/encryption_info.c
new file mode 100644
index 0000000000..a2c11fe213
--- /dev/null
+++ b/libavcodec/encryption_info.c
@@ -0,0 +1,70 @@ 
+/**
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "encryption_info.h"
+#include "libavutil/avassert.h"
+#include "libavutil/intreadwrite.h"
+
+#define FF_ENCRYPTION_INFO_EXTRA 24
+
+int av_encryption_info_get(const AVPacket* packet, AVEncryptionInfo* info) {
+    uint8_t *buffer;
+
+    buffer = av_packet_get_side_data(packet, AV_PKT_DATA_ENCRYPTION_INFO, NULL);
+    if (!buffer)
+        return 0;
+
+    info->scheme = AV_RB32(buffer);
+    info->crypt_byte_block = AV_RB32(buffer + 4);
+    info->skip_byte_block = AV_RB32(buffer + 8);
+    info->key_id_size = AV_RB32(buffer + 12);
+    info->key_id = buffer + 16;
+    info->iv_size = AV_RB32(info->key_id + info->key_id_size);
+    info->iv = info->key_id + info->key_id_size + 4;
+    info->subsample_count = AV_RB32(info->iv + info->iv_size);
+    info->subsamples = (AVSubsampleEncryptionInfo*)(info->iv + info->iv_size + 4);
+    return 1;
+}
+
+int av_encryption_info_add_side_data(AVPacket* packet, const AVEncryptionInfo* info) {
+    uint8_t *buffer;
+    size_t size;
+
+    size = FF_ENCRYPTION_INFO_EXTRA + info->key_id_size + info->iv_size +
+           (sizeof(AVSubsampleEncryptionInfo) * info->subsample_count);
+    buffer = av_packet_new_side_data(packet, AV_PKT_DATA_ENCRYPTION_INFO, size);
+    if (!buffer)
+        return AVERROR(ENOMEM);
+
+    AV_WB32(buffer,      info->scheme);
+    AV_WB32(buffer +  4, info->crypt_byte_block);
+    AV_WB32(buffer +  8, info->skip_byte_block);
+    AV_WB32(buffer + 12, info->key_id_size);
+    buffer += 16;
+    memcpy(buffer, info->key_id, info->key_id_size);
+    buffer += info->key_id_size;
+    AV_WB32(buffer, info->iv_size);
+    buffer += 4;
+    memcpy(buffer, info->iv, info->iv_size);
+    buffer += info->iv_size;
+    AV_WB32(buffer, info->subsample_count);
+    buffer += 4;
+    memcpy(buffer, info->subsamples, info->subsample_count * sizeof(AVSubsampleEncryptionInfo));
+
+    return 0;
+}
diff --git a/libavcodec/encryption_info.h b/libavcodec/encryption_info.h
new file mode 100644
index 0000000000..03052bf551
--- /dev/null
+++ b/libavcodec/encryption_info.h
@@ -0,0 +1,105 @@ 
+/**
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVUTIL_ENCRYPTION_INFO_H
+#define AVUTIL_ENCRYPTION_INFO_H
+
+#include "avcodec.h"
+
+typedef struct AVSubsampleEncryptionInfo {
+    /** The number of bytes that are clear. */
+    unsigned int bytes_of_clear_data;
+
+    /**
+     * The number of bytes that are protected.  If using pattern encryption,
+     * the pattern applies to only the protected bytes; if not using pattern
+     * encryption, all these bytes are encrypted.
+     */
+    unsigned int bytes_of_protected_data;
+} AVSubsampleEncryptionInfo;
+
+/**
+ * This describes encryption info for a packet.  This contains frame-specific
+ * info for how to decrypt the packet before passing it to the decoder.
+ *
+ * This struct should be allocated on the stack by the app and filled by
+ * av_encryption_info_get().  The side-data contains a binary format of this
+ * struct.
+ */
+typedef struct AVEncryptionInfo {
+    /** The fourcc encryption scheme. */
+    uint32_t scheme;
+
+    /**
+     * Only used for pattern encryption.  This is the number of 16-byte blocks
+     * that are encrypted.
+     */
+    uint32_t crypt_byte_block;
+
+    /**
+     * Only used for pattern encryption.  This is the number of 16-byte blocks
+     * that are clear.
+     */
+    uint32_t skip_byte_block;
+
+    /**
+     * The ID of the key used to encrypt the packet.  This should always be
+     * 16 bytes long, but may be changed in the future.
+     */
+    uint8_t *key_id;
+    uint32_t key_id_size;
+
+    /**
+     * The initialization vector.  This may have been zero-filled to be the
+     * correct block size.  This should always be 16 bytes long, but may be
+     * changed in the future.
+     */
+    uint8_t *iv;
+    uint32_t iv_size;
+
+    /**
+     * An array of subsample encryption info specifying how parts of the sample
+     * are encrypted.  If there are no subsamples, then the whole sample is
+     * encrypted.
+     */
+    AVSubsampleEncryptionInfo* subsamples;
+    uint32_t subsample_count;
+} AVEncryptionInfo;
+
+/**
+ * Gets the encryption info out of the side data of the given packet and fills
+ * the given object with that info.  The pointers in this structure point inside
+ * the side data of this packet; this means they should NOT be freed and are
+ * only valid until the side data is freed.
+ *
+ * @param packet The packet to extract the encryption info out of.
+ * @param info The structure to fill with the encryption info.
+ *
+ * @return 1 if the packet is encrypted and the data was extracted; 0 if not.
+ */
+int av_encryption_info_get(const AVPacket* packet, AVEncryptionInfo* info);
+
+/**
+ * Adds a new side data to the given packet that holds a copy of the given
+ * encryption info.
+ *
+ * @return 0 on success, or a negative error code on error.
+ */
+int av_encryption_info_add_side_data(AVPacket* packet, const AVEncryptionInfo* info);
+
+#endif /* AVUTIL_ENCRYPTION_INFO_H */
-- 
2.15.1.620.gb9897f4670-goog