@@ -40,51 +40,14 @@ CClearKeyCencSingleSampleDecrypter::CClearKeyCencSingleSampleDecrypter(
4040 return ;
4141 }
4242
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 ;
4545
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))
4748 {
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))
7750 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;
8851 }
8952
9053 if (!ParseLicenseResponse (licenseData))
@@ -93,16 +56,14 @@ CClearKeyCencSingleSampleDecrypter::CClearKeyCencSingleSampleDecrypter(
9356 return ;
9457 }
9558
96- const std::string b64DefaultKeyId = BASE64::Encode (defaultKeyId);
97- if (!STRING::KeyExists (m_keyPairs, b64DefaultKeyId))
59+ if (!STRING::KeyExists (m_kidPairs, defaultKeyId))
9860 {
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 ();
10063 return ;
10164 }
10265
103- const std::vector<uint8_t > keyBytes = BASE64::Decode (m_keyPairs[b64DefaultKeyId]);
104-
105- InitDecrypter (defaultKeyId, keyBytes);
66+ InitDecrypter ();
10667}
10768
10869CClearKeyCencSingleSampleDecrypter::CClearKeyCencSingleSampleDecrypter (
@@ -111,62 +72,138 @@ CClearKeyCencSingleSampleDecrypter::CClearKeyCencSingleSampleDecrypter(
11172 CClearKeyDecrypter* host)
11273 : m_host(host)
11374{
114- std::vector<uint8_t > hexKey;
11575 // 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) ;
11878
119- InitDecrypter (defaultKeyId, hexKey );
79+ InitDecrypter ();
12080}
12181
122- void CClearKeyCencSingleSampleDecrypter::InitDecrypter (const std::vector<uint8_t >& defaultKeyId,
123- const std::vector<uint8_t >& key)
82+ void CClearKeyCencSingleSampleDecrypter::InitDecrypter ()
12483{
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);
12884 SetParentIsOwner (false );
129- AddSessionKey (defaultKeyId);
13085
13186 // Define a session id
13287 m_sessionId = " ck_" + std::to_string (g_sessionIdCount++);
13388}
13489
135- void CClearKeyCencSingleSampleDecrypter::AddSessionKey (const std::vector<uint8_t >& keyId )
90+ bool CClearKeyCencSingleSampleDecrypter::HasKeyId (const std::vector<uint8_t >& keyid )
13691{
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);
13993}
14094
141- bool CClearKeyCencSingleSampleDecrypter::HasKeyId ( const std::vector< uint8_t >& keyid )
95+ AP4_UI32 CClearKeyCencSingleSampleDecrypter::AddPool ( )
14296{
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))
144117 {
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;
150120 }
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;
152144}
153145
154146AP4_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 ,
158150 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 )
162154{
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))
164161 {
165- return AP4_FAILURE;
162+ LOG::LogF (LOGERROR, " Cannot decrypt data, the pool id %u dont exist" , poolId);
163+ return AP4_ERROR_INVALID_PARAMETERS;
166164 }
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);
170207}
171208
172209std::string CClearKeyCencSingleSampleDecrypter::CreateLicenseRequest (
@@ -189,7 +226,54 @@ std::string CClearKeyCencSingleSampleDecrypter::CreateLicenseRequest(
189226 return jData.dump (-1 , ' ' , false , njson::error_handler_t ::ignore);
190227}
191228
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)
193277{
194278 /* Expected JSON structure for license response:
195279 * { "keys": [
@@ -219,36 +303,47 @@ bool CClearKeyCencSingleSampleDecrypter::ParseLicenseResponse(std::string data)
219303 return false ;
220304 }
221305
222- for (const auto & item : jData[" keys" ]) // Iterate array
306+ for (const auto & jwkValue : jData[" keys" ]) // Iterate array
223307 {
224- if (!item .is_object ())
308+ if (!jwkValue .is_object ())
225309 {
226310 LOG::LogF (LOGERROR, " Unexpected JSON format in the license response" );
227311 continue ;
228312 }
229313
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+
230322 std::string b64Key;
231323 std::string b64KeyId;
232324
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>();
235327
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>();
244330
245331 b64Key = BASE64::UrlSafeDecode (b64Key);
246332 BASE64::AddPadding (b64Key);
247333
248334 b64KeyId = BASE64::UrlSafeDecode (b64KeyId);
249335 BASE64::AddPadding (b64KeyId);
250336
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);
252347 }
253348
254349 return true ;
0 commit comments