31 bool ParseKeyIds(
const Data& data, std::vector<std::string>* base64_keyids) {
32 LocalVar<JsValue> data_val =
35 LOG(ERROR) <<
"Init data is not valid JSON.";
38 LocalVar<JsObject> data_obj = UnsafeJsCast<JsObject>(data_val);
41 LOG(ERROR) <<
"Init data doesn't have valid 'kids' member.";
45 LocalVar<JsObject> kids_obj = UnsafeJsCast<JsObject>(kids);
47 base64_keyids->reserve(kid_count);
48 for (
size_t i = 0; i < kid_count; i++) {
51 LOG(ERROR) <<
"Init data doesn't have valid 'kids' member.";
59 bool ParsePssh(
const Data& data, std::vector<std::string>* base64_keyids) {
70 constexpr
const size_t kSystemIdSize = 16;
71 constexpr
const size_t kKeyIdSize = 16;
72 constexpr
const uint8_t kCommonSystemId[] = {
73 0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02,
74 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b};
76 util::BufferReader reader(data.data(), data.size());
77 while (!reader.empty()) {
78 const size_t box_start_remaining = reader.BytesRemaining();
79 const uint32_t box_size = reader.ReadUint32();
81 if (reader.ReadUint32() != 0x70737368) {
82 LOG(ERROR) <<
"Init data is not a PSSH box";
86 const uint8_t version = reader.ReadUint8();
89 uint8_t system_id[kSystemIdSize];
90 if (reader.Read(system_id, kSystemIdSize) != kSystemIdSize) {
91 LOG(ERROR) <<
"Truncated init data";
95 if (memcmp(system_id, kCommonSystemId, kSystemIdSize) != 0) {
96 VLOG(1) <<
"Ignoring non-common PSSH box";
97 const size_t bytes_read = box_start_remaining - reader.BytesRemaining();
98 reader.Skip(box_size - bytes_read);
103 LOG(ERROR) <<
"PSSH version 0 is not supported for clear-key";
107 const uint32_t key_id_count = reader.ReadUint32();
108 if (reader.BytesRemaining() / kKeyIdSize < key_id_count) {
109 LOG(ERROR) <<
"Truncated init data";
113 base64_keyids->reserve(key_id_count);
114 for (uint32_t i = 0; i < key_id_count; i++) {
115 uint8_t key_id[kKeyIdSize];
116 if (reader.Read(key_id, kKeyIdSize) != kKeyIdSize) {
117 LOG(ERROR) <<
"Truncated init data";
120 base64_keyids->emplace_back(
126 LOG(ERROR) <<
"No PSSH box with common system ID found";
132 std::vector<std::string> key_ids;
135 if (!ParseKeyIds(data, &key_ids))
139 if (!ParsePssh(data, &key_ids))
144 LOG(ERROR) <<
"Init data type not supported.";
148 std::string ids_json;
149 for (
const std::string&
id : key_ids) {
150 if (ids_json.empty())
151 ids_json =
'"' +
id +
'"';
153 ids_json += R
"(,")" + id +
'"';
155 *message = R
"({"kids":[)" + ids_json + R"(],"type":"temporary"})"; 160 template <
typename KeyType>
161 bool ParseResponse(
const Data& data, std::list<KeyType>* keys) {
162 LocalVar<JsValue> data_val =
165 LOG(ERROR) <<
"License response is not valid JSON.";
168 LocalVar<JsObject> data_obj = UnsafeJsCast<JsObject>(data_val);
169 LocalVar<JsValue> keys_val =
GetMemberRaw(data_obj,
"keys");
171 LOG(ERROR) <<
"License response doesn't contain a valid 'keys' member.";
175 LocalVar<JsObject> keys_array = UnsafeJsCast<JsObject>(keys_val);
177 for (
size_t i = 0; i < key_count; i++) {
180 LOG(ERROR) <<
"License response doesn't contain a valid 'keys' member.";
183 LocalVar<JsObject> entry_obj = UnsafeJsCast<JsObject>(entry);
186 LocalVar<JsValue> kid_val =
GetMemberRaw(entry_obj,
"kid");
189 LOG(ERROR) <<
"License response contains invalid key object.";
194 ExceptionOr<ByteString> kid =
196 if (holds_alternative<js::JsError>(k) ||
197 holds_alternative<js::JsError>(kid)) {
198 LOG(ERROR) <<
"License response contains invalid base-64 encoding.";
201 if (get<ByteString>(k).size() != 16 || get<ByteString>(kid).size() != 16) {
202 LOG(ERROR) <<
"Key or key ID is not correct size.";
205 keys->emplace_back(std::move(get<ByteString>(kid)),
206 std::move(get<ByteString>(k)));
215 : helper_(helper), cur_session_id_(0) {}
219 int64_t* expiration)
const {
221 return sessions_.count(session_id) != 0;
225 const std::string& session_id, std::vector<KeyStatusInfo>* statuses)
const {
226 std::unique_lock<std::mutex> lock(mutex_);
227 if (sessions_.count(session_id) == 0)
231 for (
auto& key : sessions_.at(session_id).keys)
242 EmePromise promise, std::function<
void(
const std::string&)> set_session_id,
247 std::unique_lock<std::mutex> lock(mutex_);
251 Session* session = &sessions_[session_id];
252 DCHECK(!session->callable);
255 if (!ParseAndGenerateRequest(init_data_type, data, &message)) {
257 "Invalid initialization data given.");
261 session->callable =
true;
262 set_session_id(session_id);
264 reinterpret_cast<const uint8_t*>(message.c_str()),
272 "Clear-key doesn't support persistent licences.");
277 std::unique_lock<std::mutex> lock(mutex_);
279 if (sessions_.count(session_id) == 0) {
281 "Unable to find given session ID.");
284 Session* session = &sessions_.at(session_id);
286 if (!session->callable) {
291 std::list<Session::Key> keys;
292 if (!ParseResponse(data, &keys)) {
297 session->callable =
false;
299 session->keys.splice(session->keys.end(), std::move(keys));
306 std::unique_lock<std::mutex> lock(mutex_);
307 sessions_.erase(session_id);
314 "Clear-key doesn't support persistent licences.");
320 uint8_t*
dest)
const {
321 std::unique_lock<std::mutex> lock(mutex_);
323 const std::vector<uint8_t>* key =
nullptr;
324 for (
auto& session_pair : sessions_) {
325 for (
auto& cur_key : session_pair.second.keys) {
326 if (cur_key.key_id == info->
key_id) {
333 LOG(ERROR) <<
"Unable to find key ID: " 340 return DecryptBlock(info, data, data_size, 0, dest, &decryptor);
342 size_t block_offset = 0;
343 for (
const auto& subsample : info->
subsamples) {
344 if (data_size < subsample.clear_bytes ||
345 data_size - subsample.clear_bytes < subsample.protected_bytes) {
346 LOG(ERROR) <<
"Input data not large enough for subsamples";
351 memcpy(dest, data, subsample.clear_bytes);
352 data += subsample.clear_bytes;
353 dest += subsample.clear_bytes;
354 data_size -= subsample.clear_bytes;
357 const auto ret = DecryptBlock(info, data, subsample.protected_bytes,
358 block_offset, dest, &decryptor);
361 data += subsample.protected_bytes;
362 dest += subsample.protected_bytes;
363 data_size -= subsample.protected_bytes;
369 if (data_size != 0) {
370 LOG(ERROR) <<
"Data remaining after subsample handling";
380 size_t num_bytes_read = 0;
381 if (block_offset != 0) {
383 LOG(ERROR) <<
"Cannot have block offset when using pattern encryption";
387 num_bytes_read = std::min<size_t>(data_size,
AES_BLOCK_SIZE - block_offset);
395 const size_t protected_size =
398 const size_t pattern_size_in_blocks =
401 for (
size_t i = 0; i < data_size_in_blocks / pattern_size_in_blocks; i++) {
402 if (!decryptor->
Decrypt(data + num_bytes_read, protected_size,
403 dest + num_bytes_read)) {
406 num_bytes_read += protected_size;
408 memcpy(dest + num_bytes_read, data + num_bytes_read, clear_size);
409 num_bytes_read += clear_size;
414 if (data_size_in_blocks % pattern_size_in_blocks >=
416 if (!decryptor->
Decrypt(data + num_bytes_read, protected_size,
417 dest + num_bytes_read)) {
420 num_bytes_read += protected_size;
423 memcpy(dest + num_bytes_read, data + num_bytes_read,
424 data_size - num_bytes_read);
426 if (!decryptor->
Decrypt(data + num_bytes_read, data_size - num_bytes_read,
427 dest + num_bytes_read)) {
435 void ClearKeyImplementation::LoadKeyForTesting(std::vector<uint8_t> key_id,
436 std::vector<uint8_t> key) {
437 std::unique_lock<std::mutex> lock(mutex_);
439 sessions_.emplace(session_id, Session());
440 sessions_.at(session_id).keys.emplace_back(std::move(key_id), std::move(key));
444 std::vector<uint8_t> key)
445 : key_id(
std::move(key_id)), key(
std::move(key)) {}
448 ClearKeyImplementation::Session::Session() {}
449 ClearKeyImplementation::Session::~Session() {}
451 ClearKeyImplementation::Session::Session(Session&&) =
default;
452 ClearKeyImplementation::Session& ClearKeyImplementation::Session::operator=(
453 Session&&) =
default;
void CreateSessionAndGenerateRequest(EmePromise promise, std::function< void(const std::string &)> set_session_id, MediaKeySessionType session_type, MediaKeyInitDataType init_data_type, Data data) override
void Load(const std::string &session_id, EmePromise promise) override
ReturnVal< JsValue > ParseJsonString(const std::string &json)
const EncryptionPattern pattern
std::string ToHexString(const uint8_t *data, size_t data_size)
ClearKeyImplementation(ImplementationHelper *helper)
bool IsObject(Handle< JsValue > value)
ReturnVal< JsValue > GetArrayIndexRaw(Handle< JsObject > object, size_t index, LocalVar< JsValue > *exception=nullptr)
void SetServerCertificate(EmePromise promise, Data cert) override
static std::string EncodeUrl(ByteString input)
static ExceptionOr< ByteString > DecodeUrl(const std::string &input)
void Remove(const std::string &session_id, EmePromise promise) override
void Close(const std::string &session_id, EmePromise promise) override
virtual void OnMessage(const std::string &session_id, MediaKeyMessageType message_type, const uint8_t *data, size_t data_size) const =0
bool DecryptPartialBlock(const uint8_t *data, size_t data_size, uint32_t block_offset, uint8_t *dest)
~ClearKeyImplementation() override
void Update(const std::string &session_id, EmePromise promise, Data data) override
DecryptStatus Decrypt(const FrameEncryptionInfo *info, const uint8_t *data, size_t data_size, uint8_t *dest) const override
proto::ValueType GetValueType(Handle< JsValue > value)
virtual void OnKeyStatusChange(const std::string &session_id) const =0
Key(std::vector< uint8_t > key_id, std::vector< uint8_t > key)
void ResolveWith(bool value)
const std::vector< uint8_t > iv
std::string ConvertToString(Handle< JsValue > value)
uint32_t encrypted_blocks
size_t ArrayLength(Handle< JsObject > value)
bool Decrypt(const uint8_t *data, size_t data_size, uint8_t *dest)
void Reject(ExceptionType except_type, const std::string &message)
bool GetKeyStatuses(const std::string &session_id, std::vector< KeyStatusInfo > *statuses) const override
const EncryptionScheme scheme
ReturnVal< JsValue > GetMemberRaw(Handle< JsObject > object, const std::string &name, LocalVar< JsValue > *exception=nullptr)
const std::vector< SubsampleInfo > subsamples
const std::vector< uint8_t > key_id
bool GetExpiration(const std::string &session_id, int64_t *expiration) const override