[GStreamer][EME] waitingforkey event should consider decryptors' waiting status
[WebKit-https.git] / Source / WebCore / platform / graphics / gstreamer / eme / WebKitClearKeyDecryptorGStreamer.cpp
1 /* GStreamer ClearKey common encryption decryptor
2  *
3  * Copyright (C) 2016 Metrological
4  * Copyright (C) 2016 Igalia S.L
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin Street, Suite 500,
19  * Boston, MA 02110-1335, USA.
20  */
21
22 #include "config.h"
23 #include "WebKitClearKeyDecryptorGStreamer.h"
24
25 #if ENABLE(ENCRYPTED_MEDIA) && USE(GSTREAMER)
26
27 #include "GStreamerCommon.h"
28 #include "GStreamerEMEUtilities.h"
29 #include <gcrypt.h>
30 #include <gst/base/gstbytereader.h>
31 #include <wtf/RunLoop.h>
32
33 #define CLEARKEY_SIZE 16
34
35 struct Key {
36     GRefPtr<GstBuffer> keyID;
37     GRefPtr<GstBuffer> keyValue;
38 };
39
40 #define WEBKIT_MEDIA_CK_DECRYPT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), WEBKIT_TYPE_MEDIA_CK_DECRYPT, WebKitMediaClearKeyDecryptPrivate))
41 struct _WebKitMediaClearKeyDecryptPrivate {
42     Vector<Key> keys;
43     gcry_cipher_hd_t handle;
44 };
45
46 static void webKitMediaClearKeyDecryptorFinalize(GObject*);
47 static gboolean webKitMediaClearKeyDecryptorHandleKeyResponse(WebKitMediaCommonEncryptionDecrypt* self, GstEvent*);
48 static gboolean webKitMediaClearKeyDecryptorDecrypt(WebKitMediaCommonEncryptionDecrypt*, GstBuffer* iv, GstBuffer* keyid, GstBuffer* sample, unsigned subSamplesCount, GstBuffer* subSamples);
49
50 GST_DEBUG_CATEGORY_STATIC(webkit_media_clear_key_decrypt_debug_category);
51 #define GST_CAT_DEFAULT webkit_media_clear_key_decrypt_debug_category
52
53 static GstStaticPadTemplate sinkTemplate = GST_STATIC_PAD_TEMPLATE("sink",
54     GST_PAD_SINK,
55     GST_PAD_ALWAYS,
56     GST_STATIC_CAPS("application/x-cenc, original-media-type=(string)video/x-h264, protection-system=(string)" WEBCORE_GSTREAMER_EME_UTILITIES_CLEARKEY_UUID "; "
57     "application/x-cenc, original-media-type=(string)audio/mpeg, protection-system=(string)" WEBCORE_GSTREAMER_EME_UTILITIES_CLEARKEY_UUID";"
58     "application/x-webm-enc, original-media-type=(string)video/x-vp8;"
59     "application/x-webm-enc, original-media-type=(string)video/x-vp9;"));
60
61 static GstStaticPadTemplate srcTemplate = GST_STATIC_PAD_TEMPLATE("src",
62     GST_PAD_SRC,
63     GST_PAD_ALWAYS,
64     GST_STATIC_CAPS("video/x-h264; audio/mpeg; video/x-vp8; video/x-vp9"));
65
66 #define webkit_media_clear_key_decrypt_parent_class parent_class
67 G_DEFINE_TYPE(WebKitMediaClearKeyDecrypt, webkit_media_clear_key_decrypt, WEBKIT_TYPE_MEDIA_CENC_DECRYPT);
68
69 static void webkit_media_clear_key_decrypt_class_init(WebKitMediaClearKeyDecryptClass* klass)
70 {
71     GObjectClass* gobjectClass = G_OBJECT_CLASS(klass);
72     gobjectClass->finalize = webKitMediaClearKeyDecryptorFinalize;
73
74     GstElementClass* elementClass = GST_ELEMENT_CLASS(klass);
75     gst_element_class_add_pad_template(elementClass, gst_static_pad_template_get(&sinkTemplate));
76     gst_element_class_add_pad_template(elementClass, gst_static_pad_template_get(&srcTemplate));
77
78     gst_element_class_set_static_metadata(elementClass,
79         "Decrypt content encrypted using ISOBMFF ClearKey Common Encryption",
80         GST_ELEMENT_FACTORY_KLASS_DECRYPTOR,
81         "Decrypts media that has been encrypted using ISOBMFF ClearKey Common Encryption.",
82         "Philippe Normand <philn@igalia.com>");
83
84     GST_DEBUG_CATEGORY_INIT(webkit_media_clear_key_decrypt_debug_category,
85         "webkitclearkey", 0, "ClearKey decryptor");
86
87     WebKitMediaCommonEncryptionDecryptClass* cencClass = WEBKIT_MEDIA_CENC_DECRYPT_CLASS(klass);
88     cencClass->protectionSystemId = WebCore::GStreamerEMEUtilities::s_ClearKeyUUID;
89     cencClass->handleKeyResponse = GST_DEBUG_FUNCPTR(webKitMediaClearKeyDecryptorHandleKeyResponse);
90     cencClass->decrypt = GST_DEBUG_FUNCPTR(webKitMediaClearKeyDecryptorDecrypt);
91
92     g_type_class_add_private(klass, sizeof(WebKitMediaClearKeyDecryptPrivate));
93 }
94
95 static void webkit_media_clear_key_decrypt_init(WebKitMediaClearKeyDecrypt* self)
96 {
97     WebKitMediaClearKeyDecryptPrivate* priv = WEBKIT_MEDIA_CK_DECRYPT_GET_PRIVATE(self);
98
99     self->priv = priv;
100     new (priv) WebKitMediaClearKeyDecryptPrivate();
101     if (gcry_error_t error = gcry_cipher_open(&(priv->handle), GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CTR, GCRY_CIPHER_SECURE)) {
102         GST_ERROR_OBJECT(self, "Failed to create AES 128 CTR cipher handle: %s", gpg_strerror(error));
103         ASSERT(!error);
104     }
105 }
106
107 static void webKitMediaClearKeyDecryptorFinalize(GObject* object)
108 {
109     WebKitMediaClearKeyDecrypt* self = WEBKIT_MEDIA_CK_DECRYPT(object);
110     WebKitMediaClearKeyDecryptPrivate* priv = self->priv;
111     gcry_cipher_close(priv->handle);
112     priv->~WebKitMediaClearKeyDecryptPrivate();
113
114     GST_CALL_PARENT(G_OBJECT_CLASS, finalize, (object));
115 }
116
117 static gboolean webKitMediaClearKeyDecryptorHandleKeyResponse(WebKitMediaCommonEncryptionDecrypt* self, GstEvent* event)
118 {
119     WebKitMediaClearKeyDecryptPrivate* priv = WEBKIT_MEDIA_CK_DECRYPT_GET_PRIVATE(WEBKIT_MEDIA_CK_DECRYPT(self));
120     const GstStructure* structure = gst_event_get_structure(event);
121
122     // Demand the `drm-cipher-clearkey` GstStructure.
123     if (!gst_structure_has_name(structure, "drm-cipher-clearkey"))
124         return FALSE;
125
126     // Retrieve the `key-ids` GStreamer value list.
127     const GValue* keyIDsList = gst_structure_get_value(structure, "key-ids");
128     ASSERT(keyIDsList && GST_VALUE_HOLDS_LIST(keyIDsList));
129     unsigned keyIDsListSize = gst_value_list_get_size(keyIDsList);
130
131     // Retrieve the `key-values` GStreamer value list.
132     const GValue* keyValuesList = gst_structure_get_value(structure, "key-values");
133     ASSERT(keyValuesList && GST_VALUE_HOLDS_LIST(keyValuesList));
134     unsigned keyValuesListSize = gst_value_list_get_size(keyValuesList);
135
136     // Bail if somehow the two lists don't match in size.
137     if (keyIDsListSize != keyValuesListSize)
138         return FALSE;
139
140     // Clear out the previous list of keys.
141     priv->keys.clear();
142
143     // Append the retrieved GstBuffer objects containing each key's ID and value to the list of Key objects.
144     for (unsigned i = 0; i < keyIDsListSize; ++i) {
145         GRefPtr<GstBuffer> keyIDBuffer(gst_value_get_buffer(gst_value_list_get_value(keyIDsList, i)));
146         GRefPtr<GstBuffer> keyValueBuffer(gst_value_get_buffer(gst_value_list_get_value(keyValuesList, i)));
147         priv->keys.append(Key { WTFMove(keyIDBuffer), WTFMove(keyValueBuffer) });
148     }
149
150     return TRUE;
151 }
152
153 static gboolean webKitMediaClearKeyDecryptorFindAndSetKey(WebKitMediaClearKeyDecryptPrivate* priv, const WebCore::GstMappedBuffer& keyIDBuffer)
154 {
155     GRefPtr<GstBuffer> keyBuffer;
156     for (auto& key : priv->keys) {
157         if (key.keyID.get() == keyIDBuffer) {
158             keyBuffer = key.keyValue;
159             break;
160         }
161     }
162
163     if (!keyBuffer) {
164         GST_ERROR_OBJECT(priv, "Failed to find an appropriate key buffer");
165         return false;
166     }
167
168     WebCore::GstMappedBuffer mappedKeyValueBuffer(keyBuffer.get(), GST_MAP_READ);
169     if (!mappedKeyValueBuffer) {
170         GST_ERROR_OBJECT(priv, "Failed to map decryption key");
171         return false;
172     }
173
174     ASSERT(mappedKeyValueBuffer.size() == CLEARKEY_SIZE);
175     if (gcry_error_t error = gcry_cipher_setkey(priv->handle, mappedKeyValueBuffer.data(), mappedKeyValueBuffer.size())) {
176         GST_ERROR_OBJECT(priv, "gcry_cipher_setkey failed: %s", gpg_strerror(error));
177         return false;
178     }
179
180     return true;
181 }
182
183 static gboolean webKitMediaClearKeyDecryptorDecrypt(WebKitMediaCommonEncryptionDecrypt* self, GstBuffer* ivBuffer, GstBuffer* keyIDBuffer, GstBuffer* buffer, unsigned subSampleCount, GstBuffer* subSamplesBuffer)
184 {
185     // Check ivBuffer isn't null.
186     if (!ivBuffer) {
187         GST_ERROR_OBJECT(self, "Error, the ivBuffer is null");
188         return false;
189     }
190
191     WebCore::GstMappedBuffer mappedIVBuffer(ivBuffer, GST_MAP_READ);
192     if (!mappedIVBuffer) {
193         GST_ERROR_OBJECT(self, "Failed to map IV");
194         return false;
195     }
196
197     uint8_t ctr[CLEARKEY_SIZE];
198     if (mappedIVBuffer.size() == 8) {
199         memset(ctr + 8, 0, 8);
200         memcpy(ctr, mappedIVBuffer.data(), 8);
201     } else {
202         ASSERT(mappedIVBuffer.size() == CLEARKEY_SIZE);
203         memcpy(ctr, mappedIVBuffer.data(), CLEARKEY_SIZE);
204     }
205
206     WebKitMediaClearKeyDecryptPrivate* priv = WEBKIT_MEDIA_CK_DECRYPT_GET_PRIVATE(WEBKIT_MEDIA_CK_DECRYPT(self));
207     gcry_error_t cipherError = gcry_cipher_setctr(priv->handle, ctr, CLEARKEY_SIZE);
208     if (cipherError) {
209         GST_ERROR_OBJECT(self, "gcry_cipher_setctr failed: %s", gpg_strerror(cipherError));
210         return false;
211     }
212
213     // Check buffer isn't null.
214     if (!buffer) {
215         GST_ERROR_OBJECT(self, "No buffer to decrypt");
216         return false;
217     }
218
219     WebCore::GstMappedBuffer mappedKeyIdBuffer(keyIDBuffer, GST_MAP_READ);
220     if (!mappedKeyIdBuffer) {
221         GST_ERROR_OBJECT(self, "Failed to map key id buffer");
222         return false;
223     }
224
225     WebCore::GstMappedBuffer mappedBuffer(buffer, GST_MAP_READWRITE);
226     if (!mappedBuffer) {
227         GST_ERROR_OBJECT(self, "Failed to map buffer");
228         return false;
229     }
230
231     webKitMediaClearKeyDecryptorFindAndSetKey(priv, mappedKeyIdBuffer);
232
233     unsigned position = 0;
234     unsigned sampleIndex = 0;
235
236     if (!subSampleCount) {
237         // Full sample encryption.
238         GST_TRACE_OBJECT(self, "full sample encryption: %zu encrypted bytes", mappedBuffer.size());
239
240         // Check if the buffer is empty.
241         if (mappedBuffer.size()) {
242             cipherError = gcry_cipher_decrypt(priv->handle, mappedBuffer.data(), mappedBuffer.size(), 0, 0);
243             if (cipherError) {
244                 GST_ERROR_OBJECT(self, "full sample decryption failed: %s", gpg_strerror(cipherError));
245                 return false;
246             }
247         }
248         return true;
249     }
250
251     // Check subSamplesBuffer isn't null.
252     if (!subSamplesBuffer) {
253         GST_ERROR_OBJECT(self, "Error, the subSampleBuffer is null");
254         return false;
255     }
256
257     // Subsample encryption.
258     WebCore::GstMappedBuffer mappedSubSamplesBuffer(subSamplesBuffer, GST_MAP_READ);
259     if (!mappedSubSamplesBuffer) {
260         GST_ERROR_OBJECT(self, "Failed to map subsample buffer");
261         return false;
262     }
263
264     GUniquePtr<GstByteReader> reader(gst_byte_reader_new(mappedSubSamplesBuffer.data(), mappedSubSamplesBuffer.size()));
265     GST_DEBUG_OBJECT(self, "position: %d, size: %zu", position, mappedBuffer.size());
266
267     while (position < mappedBuffer.size()) {
268         guint16 nBytesClear = 0;
269         guint32 nBytesEncrypted = 0;
270
271         if (sampleIndex < subSampleCount) {
272             if (!gst_byte_reader_get_uint16_be(reader.get(), &nBytesClear)
273                 || !gst_byte_reader_get_uint32_be(reader.get(), &nBytesEncrypted)) {
274                 GST_DEBUG_OBJECT(self, "unsupported");
275                 return false;
276             }
277             sampleIndex++;
278         } else {
279             nBytesClear = 0;
280             nBytesEncrypted = mappedBuffer.size() - position;
281         }
282
283         GST_TRACE_OBJECT(self, "subsample index %u - %hu bytes clear (todo=%zu)", sampleIndex, nBytesClear, mappedBuffer.size() - position);
284         position += nBytesClear;
285         if (nBytesEncrypted) {
286             GST_TRACE_OBJECT(self, "subsample index %u - %u bytes encrypted (todo=%zu)", sampleIndex, nBytesEncrypted, mappedBuffer.size() - position);
287             cipherError = gcry_cipher_decrypt(priv->handle, mappedBuffer.data() + position, nBytesEncrypted, 0, 0);
288             if (cipherError) {
289                 GST_ERROR_OBJECT(self, "sub sample index %u decryption failed: %s", sampleIndex, gpg_strerror(cipherError));
290                 return false;
291             }
292             position += nBytesEncrypted;
293         }
294     }
295
296     return true;
297 }
298
299 #endif // ENABLE(ENCRYPTED_MEDIA) && USE(GSTREAMER)