6c4250df051b0adad5b892091855b31d3e8930e5
[WebKit.git] / Source / WebCore / platform / graphics / gstreamer / eme / CDMProxyClearKey.cpp
1 /*
2  * Copyright (C) 2020 Metrological Group B.V.
3  * Copyright (C) 2020 Igalia S.L.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above
12  *    copyright notice, this list of conditions and the following
13  *    disclaimer in the documentation and/or other materials provided
14  *    with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include "config.h"
30 #include "CDMProxyClearKey.h"
31
32 #if ENABLE(ENCRYPTED_MEDIA)
33
34 #include "Logging.h"
35 #include <wtf/ByteOrder.h>
36
37 namespace WebCore {
38
39 namespace {
40
41 static bool readUInt32(const uint8_t* buffer, size_t bufferSize, size_t& offset, uint32_t& value)
42 {
43     ASSERT_ARG(offset, offset <= bufferSize);
44     if (bufferSize - offset < sizeof(value))
45         return false;
46
47     value = ntohl(*reinterpret_cast_ptr<const uint32_t*>(buffer + offset));
48     offset += sizeof(value);
49
50     return true;
51 }
52
53 static bool readUInt16(const uint8_t* buffer, size_t bufferSize, size_t& offset, uint16_t& value)
54 {
55     ASSERT_ARG(offset, offset <= bufferSize);
56     if (bufferSize - offset < sizeof(value))
57         return false;
58
59     value = ntohs(*reinterpret_cast_ptr<const uint16_t*>(buffer + offset));
60     offset += sizeof(value);
61
62     return true;
63 }
64
65 } // namespace {}
66
67 CDMProxyClearKey::~CDMProxyClearKey()
68 {
69     gcry_cipher_close(m_gcryHandle);
70 }
71
72 bool CDMProxyClearKey::cencSetCounterVector(const cencDecryptContext& input)
73 {
74     uint8_t ctr[ClearKey::AES128CTRBlockSizeInBytes];
75     if (input.ivSizeInBytes == 8) {
76         // ISO/IEC 23001-7:2016 Section 9.3
77
78         // When an 8-byte IV is indicated, the least significant 8
79         // bytes of the 16 byte IV (bytes 8 to 15) SHALL be set to
80         // zero.
81         memset(ctr + 8, 0, 8);
82         memcpy(ctr, input.iv, 8);
83     } else
84         memcpy(ctr, input.iv, ClearKey::IVSizeInBytes);
85
86     if (gcry_error_t cipherError = gcry_cipher_setctr(m_gcryHandle, ctr, ClearKey::IVSizeInBytes)) {
87 #if !LOG_DISABLED
88         LOG(EME, "EME - CDMProxyClearKey - ERROR(gcry_cipher_setctr): %s", gpg_strerror(cipherError));
89 #else
90         UNUSED_VARIABLE(cipherError);
91 #endif
92         return false;
93     }
94
95     return true;
96 }
97
98 bool CDMProxyClearKey::cencSetDecryptionKey(const cencDecryptContext& in)
99 {
100     // FIXME: Unnecessary copy, can we avoid this while still exposing
101     // a non-GStreamer-specific DecryptInput API? These buffers are
102     // small (16 bytes), so not a huge deal, I guess.
103     Vector<uint8_t> keyIDVec;
104     keyIDVec.append(in.keyID, in.keyIDSizeInBytes);
105
106     auto keyData = getOrWaitForKey(keyIDVec);
107     if (!keyData)
108         return false;
109
110     if (gcry_error_t cipherError = gcry_cipher_setkey(m_gcryHandle, keyData->data(), keyData->size())) {
111 #if !LOG_DISABLED
112         LOG(EME, "EME - CDMProxyClearKey - ERROR(gcry_cipher_setkey): %s", gpg_strerror(cipherError));
113 #else
114         UNUSED_VARIABLE(cipherError);
115 #endif
116         return false;
117     }
118
119     return true;
120 }
121
122 bool CDMProxyClearKey::cencDecryptFullSample(cencDecryptContext& in)
123 {
124     if (!in.encryptedBufferSizeInBytes)
125         return true;
126
127     LOG(EME, "EME - CDMProxyClearKey - full-sample decryption: %zu encrypted bytes", in.encryptedBufferSizeInBytes);
128
129     if (gcry_error_t cipherError = gcry_cipher_decrypt(m_gcryHandle, in.encryptedBuffer, in.encryptedBufferSizeInBytes, 0, 0)) {
130 #if !LOG_DISABLED
131         LOG(EME, "EME - CDMProxyClearKey - ERROR(gcry_cipher_decrypt): %s", gpg_strerror(cipherError));
132 #else
133         UNUSED_VARIABLE(cipherError);
134 #endif
135         return false;
136     }
137
138     return true;
139 }
140
141 bool CDMProxyClearKey::cencDecryptSubsampled(cencDecryptContext& input)
142 {
143     unsigned encryptedBufferByteOffset = 0;
144     size_t subSampleBufferByteOffset = 0;
145     unsigned subsampleIndex = 0;
146     while (encryptedBufferByteOffset < input.encryptedBufferSizeInBytes) {
147         uint16_t subsampleNumClearBytes = 0;
148         uint32_t subsampleNumEncryptedBytes = 0;
149
150         if (subsampleIndex < input.numSubsamples) {
151             if (!readUInt16(input.subsamplesBuffer, input.subsamplesBufferSizeInBytes, subSampleBufferByteOffset, subsampleNumClearBytes)) {
152                 LOG(EME, "EME - CDMProxyClearKey - could not read number of clear bytes in subsample at index %u", subsampleIndex);
153                 return false;
154             }
155             if (!readUInt32(input.subsamplesBuffer, input.subsamplesBufferSizeInBytes, subSampleBufferByteOffset, subsampleNumEncryptedBytes)) {
156                 LOG(EME, "EME - CDMProxyClearKey - could not read number of encrypted bytes in subsample at index %u", subsampleIndex);
157                 return false;
158             }
159             subsampleIndex++;
160         } else {
161             subsampleNumClearBytes = 0;
162             subsampleNumEncryptedBytes = input.encryptedBufferSizeInBytes - encryptedBufferByteOffset;
163         }
164
165         // FIXME: These are high-frequency messages, not sure if there's a better logging lib in WebCore.
166         LOG(EME, "EME - subsample index %u - %u bytes clear (%lu bytes left to decrypt)", subsampleIndex, subsampleNumClearBytes, input.encryptedBufferSizeInBytes - encryptedBufferByteOffset);
167
168         encryptedBufferByteOffset += subsampleNumClearBytes;
169
170         if (subsampleNumEncryptedBytes) {
171             LOG(EME, "EME - subsample index %u - %u bytes encrypted (%lu bytes left to decrypt)", subsampleIndex, subsampleNumEncryptedBytes, input.encryptedBufferSizeInBytes - encryptedBufferByteOffset);
172
173             if (gcry_error_t cipherError = gcry_cipher_decrypt(m_gcryHandle, input.encryptedBuffer + encryptedBufferByteOffset, subsampleNumEncryptedBytes, 0, 0)) {
174                 LOG(EME, "EME - CDMProxyClearKey - ERROR(gcry_cipher_decrypt): %s", gpg_strerror(cipherError));
175                 return false;
176             }
177
178             encryptedBufferByteOffset += subsampleNumEncryptedBytes;
179         }
180     }
181
182     return true;
183 }
184
185 bool CDMProxyClearKey::cencDecrypt(CDMProxyClearKey::cencDecryptContext& input)
186 {
187     if (!cencSetCounterVector(input) || !cencSetDecryptionKey(input))
188         return false;
189
190     return input.isSubsampled() ? cencDecryptSubsampled(input) : cencDecryptFullSample(input);
191 }
192
193 void CDMProxyClearKey::initializeGcrypt()
194 {
195     if (gcry_error_t error = gcry_cipher_open(&m_gcryHandle, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CTR, GCRY_CIPHER_SECURE)) {
196         LOG(EME, "EME - CDMProxyClearKey - ERROR(gcry_cipher_open): %s", gpg_strerror(error));
197         RELEASE_ASSERT(false && "Should not get this far with a functional GCrypt!");
198     }
199 }
200
201 } // namespace WebCore
202
203 #endif // ENABLE(ENCRYPTED_MEDIA)