@@ -40,51 +40,14 @@ CClearKeyCencSingleSampleDecrypter::CClearKeyCencSingleSampleDecrypter(
40
40
return ;
41
41
}
42
42
43
- std::string licenseData;
44
- std::vector<uint8_t > uriData ;
43
+ // Make license request to get KID/KEY pairs
44
+ std::vector<uint8_t > licenseData ;
45
45
46
- if (URL::GetUriByteData (licenseUri, uriData)) // Provided license data in URI format
46
+ // Check if provided license data in URI format, otherwise make the license request
47
+ if (!URL::GetUriByteData (licenseUri, licenseData))
47
48
{
48
- licenseData.assign (uriData.begin (), uriData.end ());
49
- }
50
- else // Make the request to the server by using URL
51
- {
52
- const std::string postData = CreateLicenseRequest (defaultKeyId);
53
-
54
- if (CSrvBroker::GetSettings ().IsDebugLicense ())
55
- {
56
- const std::string debugFilePath =
57
- FILESYS::PathCombine (m_host->GetLibraryPath (), " ClearKey.init" );
58
- FILESYS::SaveFile (debugFilePath, postData.c_str (), true );
59
- }
60
-
61
- CURL::CUrl curl{std::string (licenseUri), postData};
62
- curl.AddHeader (" Accept" , " application/json" );
63
- curl.AddHeader (" Content-Type" , " application/json" );
64
- curl.AddHeaders (licenseHeaders);
65
-
66
- std::string response;
67
- int statusCode = curl.Open ();
68
- if (statusCode == -1 || statusCode >= 400 )
69
- {
70
- LOG::Log (LOGERROR, " License server returned failure (HTTP error %i)" , statusCode);
71
- return ;
72
- }
73
-
74
- if (curl.Read (response) != CURL::ReadStatus::IS_EOF)
75
- {
76
- LOG::LogF (LOGERROR, " Could not read the license server response" );
49
+ if (!MakeLicenseRequest (std::string (licenseUri), licenseHeaders, defaultKeyId, licenseData))
77
50
return ;
78
- }
79
-
80
- if (CSrvBroker::GetSettings ().IsDebugLicense ())
81
- {
82
- const std::string debugFilePath =
83
- FILESYS::PathCombine (m_host->GetLibraryPath (), " ClearKey.response" );
84
- FILESYS::SaveFile (debugFilePath, response, true );
85
- }
86
-
87
- licenseData = response;
88
51
}
89
52
90
53
if (!ParseLicenseResponse (licenseData))
@@ -93,16 +56,14 @@ CClearKeyCencSingleSampleDecrypter::CClearKeyCencSingleSampleDecrypter(
93
56
return ;
94
57
}
95
58
96
- const std::string b64DefaultKeyId = BASE64::Encode (defaultKeyId);
97
- if (!STRING::KeyExists (m_keyPairs, b64DefaultKeyId))
59
+ if (!STRING::KeyExists (m_kidPairs, defaultKeyId))
98
60
{
99
- LOG::LogF (LOGERROR, " Key not found on license data" );
61
+ LOG::LogF (LOGERROR, " License data does not have the required KID" );
62
+ m_kidPairs.clear ();
100
63
return ;
101
64
}
102
65
103
- const std::vector<uint8_t > keyBytes = BASE64::Decode (m_keyPairs[b64DefaultKeyId]);
104
-
105
- InitDecrypter (defaultKeyId, keyBytes);
66
+ InitDecrypter ();
106
67
}
107
68
108
69
CClearKeyCencSingleSampleDecrypter::CClearKeyCencSingleSampleDecrypter (
@@ -111,62 +72,138 @@ CClearKeyCencSingleSampleDecrypter::CClearKeyCencSingleSampleDecrypter(
111
72
CClearKeyDecrypter* host)
112
73
: m_host(host)
113
74
{
114
- std::vector<uint8_t > hexKey;
115
75
// Currently HLS manifest only support this
116
- // and the init data should contain only the key
117
- hexKey = initData;
76
+ // the initData should contain only the key
77
+ m_kidPairs. emplace (defaultKeyId, initData) ;
118
78
119
- InitDecrypter (defaultKeyId, hexKey );
79
+ InitDecrypter ();
120
80
}
121
81
122
- void CClearKeyCencSingleSampleDecrypter::InitDecrypter (const std::vector<uint8_t >& defaultKeyId,
123
- const std::vector<uint8_t >& key)
82
+ void CClearKeyCencSingleSampleDecrypter::InitDecrypter ()
124
83
{
125
- AP4_CencSingleSampleDecrypter::Create (AP4_CENC_CIPHER_AES_128_CTR, key.data (),
126
- static_cast <AP4_Size>(key.size ()), 0 , 0 , nullptr , false ,
127
- m_singleSampleDecrypter);
128
84
SetParentIsOwner (false );
129
- AddSessionKey (defaultKeyId);
130
85
131
86
// Define a session id
132
87
m_sessionId = " ck_" + std::to_string (g_sessionIdCount++);
133
88
}
134
89
135
- void CClearKeyCencSingleSampleDecrypter::AddSessionKey (const std::vector<uint8_t >& keyId )
90
+ bool CClearKeyCencSingleSampleDecrypter::HasKeyId (const std::vector<uint8_t >& keyid )
136
91
{
137
- if (std::find (m_keyIds.begin (), m_keyIds.end (), keyId) == m_keyIds.end ())
138
- m_keyIds.emplace_back (keyId);
92
+ return STRING::KeyExists (m_kidPairs, keyid);
139
93
}
140
94
141
- bool CClearKeyCencSingleSampleDecrypter::HasKeyId ( const std::vector< uint8_t >& keyid )
95
+ AP4_UI32 CClearKeyCencSingleSampleDecrypter::AddPool ( )
142
96
{
143
- if (!keyid.empty ())
97
+ const AP4_UI32 poolId = static_cast <AP4_UI32>(m_pool.size ());
98
+
99
+ m_pool.emplace (poolId, PINFO ());
100
+
101
+ return poolId;
102
+ }
103
+
104
+ void CClearKeyCencSingleSampleDecrypter::RemovePool (AP4_UI32 poolId)
105
+ {
106
+ m_pool.erase (poolId);
107
+ }
108
+
109
+ AP4_Result CClearKeyCencSingleSampleDecrypter::SetFragmentInfo (AP4_UI32 poolId,
110
+ const std::vector<uint8_t >& keyId,
111
+ const AP4_UI08 nalLengthSize,
112
+ AP4_DataBuffer& annexbSpsPps,
113
+ AP4_UI32 flags,
114
+ CryptoInfo cryptoInfo)
115
+ {
116
+ if (!STRING::KeyExists (m_pool, poolId))
144
117
{
145
- for (const std::vector<uint8_t >& key : m_keyIds)
146
- {
147
- if (key == keyid)
148
- return true ;
149
- }
118
+ LOG::LogF (LOGERROR, " Cannot set fragment info, the pool id %u dont exist" , poolId);
119
+ return AP4_ERROR_INVALID_PARAMETERS;
150
120
}
151
- return false ;
121
+
122
+ if (cryptoInfo.m_mode != CryptoMode::NONE && cryptoInfo.m_mode != CryptoMode::AES_CTR &&
123
+ cryptoInfo.m_mode != CryptoMode::AES_CBC)
124
+ {
125
+ LOG::LogF (LOGERROR, " Cannot set fragment info, unsupported crypto mode (%i)" ,
126
+ static_cast <int >(cryptoInfo.m_mode ));
127
+ return AP4_ERROR_INVALID_PARAMETERS;
128
+ }
129
+
130
+ PINFO& pInfo = m_pool[poolId];
131
+ FINFO& fInfo = pInfo.fInfo ;
132
+
133
+ // Compare the encryption info from the previous fragment to see if it has been changed,
134
+ // if so, the decrypter will have to be recreated
135
+ pInfo.isChanged = fInfo .kid != keyId || fInfo .cryptoInfo .m_mode != cryptoInfo.m_mode ||
136
+ fInfo .cryptoInfo .m_cryptBlocks != cryptoInfo.m_cryptBlocks ||
137
+ fInfo .cryptoInfo .m_skipBlocks != cryptoInfo.m_skipBlocks ;
138
+
139
+ // Update with the current fragment info
140
+ fInfo .kid = keyId;
141
+ fInfo .cryptoInfo = cryptoInfo;
142
+
143
+ return AP4_SUCCESS;
152
144
}
153
145
154
146
AP4_Result CClearKeyCencSingleSampleDecrypter::DecryptSampleData (
155
- AP4_UI32 pool_id ,
156
- AP4_DataBuffer& data_in ,
157
- AP4_DataBuffer& data_out ,
147
+ AP4_UI32 poolId ,
148
+ AP4_DataBuffer& dataIn ,
149
+ AP4_DataBuffer& dataOut ,
158
150
const AP4_UI08* iv,
159
- unsigned int subsample_count ,
160
- const AP4_UI16* bytes_of_cleartext_data ,
161
- const AP4_UI32* bytes_of_encrypted_data )
151
+ unsigned int subsampleCount ,
152
+ const AP4_UI16* bytesOfCleartextData ,
153
+ const AP4_UI32* bytesOfEncryptedData )
162
154
{
163
- if (!m_singleSampleDecrypter)
155
+ if (m_pool.empty ())
156
+ {
157
+ LOG::LogF (LOGERROR, " Cannot decrypt data, the pool is empty" );
158
+ return AP4_ERROR_INTERNAL;
159
+ }
160
+ if (!STRING::KeyExists (m_pool, poolId))
164
161
{
165
- return AP4_FAILURE;
162
+ LOG::LogF (LOGERROR, " Cannot decrypt data, the pool id %u dont exist" , poolId);
163
+ return AP4_ERROR_INVALID_PARAMETERS;
166
164
}
167
- return (m_singleSampleDecrypter)
168
- ->DecryptSampleData (data_in, data_out, iv, subsample_count, bytes_of_cleartext_data,
169
- bytes_of_encrypted_data);
165
+
166
+ PINFO& pInfo = m_pool[poolId];
167
+ auto & decrypter = pInfo.decrypter ;
168
+
169
+ if (!decrypter || pInfo.isChanged )
170
+ {
171
+ const auto & fInfo = pInfo.fInfo ;
172
+
173
+ if (!STRING::KeyExists (m_kidPairs, fInfo .kid ))
174
+ {
175
+ // In theory when there is a license server url, could be possible to request new KID/KEY pair
176
+ // making a new HTTP license request e.g. the case of key rotation, but it needs to be tested properly
177
+ LOG::LogF (LOGERROR, " Cannot decrypt data, due to missing KID/KEY from the license data" );
178
+ return AP4_ERROR_INVALID_STATE;
179
+ }
180
+
181
+ AP4_CencSingleSampleDecrypter* pDecrypter{nullptr };
182
+ const std::vector<uint8_t >& key = m_kidPairs[fInfo .kid ];
183
+
184
+ AP4_UI32 cypherType{AP4_CENC_CIPHER_NONE};
185
+ bool resetIvEachSubsample{false };
186
+
187
+ if (fInfo .cryptoInfo .m_mode == CryptoMode::AES_CTR)
188
+ {
189
+ cypherType = AP4_CENC_CIPHER_AES_128_CTR;
190
+ }
191
+ else if (fInfo .cryptoInfo .m_mode == CryptoMode::AES_CBC)
192
+ {
193
+ cypherType = AP4_CENC_CIPHER_AES_128_CBC;
194
+ // CBCS reset the IV at each subsample, see https://github.com/axiomatic-systems/Bento4/commit/ab07e3acc7befc821554bc5e271df1201681e954
195
+ resetIvEachSubsample = true ;
196
+ }
197
+
198
+ AP4_CencSingleSampleDecrypter::Create (
199
+ cypherType, key.data (), static_cast <AP4_Size>(key.size ()), fInfo .cryptoInfo .m_cryptBlocks ,
200
+ fInfo .cryptoInfo .m_skipBlocks , nullptr , resetIvEachSubsample, pDecrypter);
201
+
202
+ decrypter = std::unique_ptr<AP4_CencSingleSampleDecrypter>{std::exchange (pDecrypter, nullptr )};
203
+ }
204
+
205
+ return decrypter->DecryptSampleData (dataIn, dataOut, iv, subsampleCount, bytesOfCleartextData,
206
+ bytesOfEncryptedData);
170
207
}
171
208
172
209
std::string CClearKeyCencSingleSampleDecrypter::CreateLicenseRequest (
@@ -189,7 +226,54 @@ std::string CClearKeyCencSingleSampleDecrypter::CreateLicenseRequest(
189
226
return jData.dump (-1 , ' ' , false , njson::error_handler_t ::ignore);
190
227
}
191
228
192
- bool CClearKeyCencSingleSampleDecrypter::ParseLicenseResponse (std::string data)
229
+ bool CClearKeyCencSingleSampleDecrypter::MakeLicenseRequest (
230
+ const std::string& url,
231
+ const std::map<std::string, std::string>& headers,
232
+ const std::vector<uint8_t >& kid,
233
+ std::vector<uint8_t >& licenseData)
234
+ {
235
+ const std::string postData = CreateLicenseRequest (kid);
236
+
237
+ if (CSrvBroker::GetSettings ().IsDebugLicense ())
238
+ {
239
+ const std::string debugFilePath =
240
+ FILESYS::PathCombine (m_host->GetLibraryPath (), " ClearKey.init" );
241
+ FILESYS::SaveFile (debugFilePath, postData.c_str (), true );
242
+ }
243
+
244
+ CURL::CUrl curl{url, postData};
245
+ curl.AddHeader (" Accept" , " application/json" );
246
+ curl.AddHeader (" Content-Type" , " application/json" );
247
+ curl.AddHeaders (headers);
248
+
249
+ int statusCode = curl.Open ();
250
+ if (statusCode == -1 || statusCode >= 400 )
251
+ {
252
+ LOG::Log (LOGERROR, " License server returned failure (HTTP error %i)" , statusCode);
253
+ return false ;
254
+ }
255
+
256
+ std::string responseData;
257
+
258
+ if (curl.Read (responseData) != CURL::ReadStatus::IS_EOF)
259
+ {
260
+ LOG::LogF (LOGERROR, " Could not read the license server response" );
261
+ return false ;
262
+ }
263
+
264
+ if (CSrvBroker::GetSettings ().IsDebugLicense ())
265
+ {
266
+ const std::string debugFilePath =
267
+ FILESYS::PathCombine (m_host->GetLibraryPath (), " ClearKey.response" );
268
+ FILESYS::SaveFile (debugFilePath, responseData, true );
269
+ }
270
+
271
+ licenseData.assign (responseData.begin (), responseData.end ());
272
+
273
+ return true ;
274
+ }
275
+
276
+ bool CClearKeyCencSingleSampleDecrypter::ParseLicenseResponse (const std::vector<uint8_t >& data)
193
277
{
194
278
/* Expected JSON structure for license response:
195
279
* { "keys": [
@@ -219,36 +303,47 @@ bool CClearKeyCencSingleSampleDecrypter::ParseLicenseResponse(std::string data)
219
303
return false ;
220
304
}
221
305
222
- for (const auto & item : jData[" keys" ]) // Iterate array
306
+ for (const auto & jwkValue : jData[" keys" ]) // Iterate array
223
307
{
224
- if (!item .is_object ())
308
+ if (!jwkValue .is_object ())
225
309
{
226
310
LOG::LogF (LOGERROR, " Unexpected JSON format in the license response" );
227
311
continue ;
228
312
}
229
313
314
+ if (jwkValue.contains (" kty" ) && jwkValue[" kty" ].is_string () &&
315
+ jwkValue[" kty" ].get <std::string>() != " oct" )
316
+ {
317
+ LOG::LogF (LOGWARNING, " Ignored JWK set, due to unsupported \" %s\" key type" ,
318
+ jwkValue[" kty" ].get <std::string>().c_str ());
319
+ continue ;
320
+ }
321
+
230
322
std::string b64Key;
231
323
std::string b64KeyId;
232
324
233
- if (item .contains (" k" ) && item [" k" ].is_string ())
234
- b64Key = item [" k" ].get <std::string>();
325
+ if (jwkValue .contains (" k" ) && jwkValue [" k" ].is_string ())
326
+ b64Key = jwkValue [" k" ].get <std::string>();
235
327
236
- if (item.contains (" kid" ) && item[" kid" ].is_string ())
237
- b64KeyId = item[" kid" ].get <std::string>();
238
-
239
- if (b64Key.empty () || b64KeyId.empty ())
240
- {
241
- LOG::LogF (LOGERROR, " Malformed key pair value in the license response" );
242
- continue ;
243
- }
328
+ if (jwkValue.contains (" kid" ) && jwkValue[" kid" ].is_string ())
329
+ b64KeyId = jwkValue[" kid" ].get <std::string>();
244
330
245
331
b64Key = BASE64::UrlSafeDecode (b64Key);
246
332
BASE64::AddPadding (b64Key);
247
333
248
334
b64KeyId = BASE64::UrlSafeDecode (b64KeyId);
249
335
BASE64::AddPadding (b64KeyId);
250
336
251
- m_keyPairs.emplace (b64KeyId, b64Key);
337
+ const std::vector<uint8_t > kidBytes = BASE64::Decode (b64KeyId);
338
+ const std::vector<uint8_t > keyBytes = BASE64::Decode (b64Key);
339
+
340
+ if (kidBytes.empty () || keyBytes.empty ())
341
+ {
342
+ LOG::LogF (LOGERROR, " Malformed key pair value in the license response" );
343
+ continue ;
344
+ }
345
+
346
+ m_kidPairs.insert_or_assign (kidBytes, keyBytes);
252
347
}
253
348
254
349
return true ;
0 commit comments