1/* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17//#define LOG_NDEBUG 0 18#define LOG_TAG "ClearKeyFetcher" 19 20#include <algorithm> 21#include <inttypes.h> 22#include <string> 23 24#include "ClearKeyFetcher.h" 25#include "ecm.h" 26#include "LicenseFetcher.h" 27 28#include <media/stagefright/foundation/ADebug.h> 29#include <utils/Log.h> 30 31namespace android { 32namespace clearkeycas { 33 34ClearKeyFetcher::ClearKeyFetcher( 35 std::unique_ptr<LicenseFetcher> license_fetcher) : 36 initialized_(false), 37 license_fetcher_(std::move(license_fetcher)) { 38 CHECK(license_fetcher_); 39} 40 41ClearKeyFetcher::~ClearKeyFetcher() {} 42 43// This is a no-op but other KeyFetcher subclasses require initialization 44// so this is necessary to preserve the contract. 45status_t ClearKeyFetcher::Init() { 46 initialized_ = true; 47 return OK; 48} 49 50status_t ClearKeyFetcher::ObtainKey(const sp<ABuffer>& buffer, 51 uint64_t* asset_id, std::vector<KeyInfo>* keys) { 52 CHECK(asset_id); 53 CHECK(keys); 54 CHECK(initialized_); 55 *asset_id = 0; 56 keys->clear(); 57 58 EcmContainer container; 59 status_t status = container.Parse(buffer); 60 if (status != OK) { 61 return status; 62 } 63 ALOGV("descriptor_size=%zu", container.descriptor_size()); 64 65 // Sanity check to verify that the BroadcastEncryptor is sending a properly 66 // formed EcmContainer. If it contains two Ecms, the ids should have different 67 // parity (one odd, one even). This does not necessarily affect decryption 68 // but indicates a problem with Ecm generation. 69 if (container.descriptor_size() == 2) { 70 // XOR the least significant bits to verify different parity. 71 bool same_parity = (((container.descriptor(0).id() & 0x01) ^ 72 (container.descriptor(1).id() & 0x01)) == 0); 73 if (same_parity) { 74 ALOGW("asset_id=%" PRIu64 ": malformed Ecm, " 75 "content keys have same parity, id0=%d, id1=%d", 76 container.descriptor(0).ecm().asset_id(), 77 container.descriptor(0).id(), 78 container.descriptor(1).id()); 79 } 80 } 81 82 *asset_id = container.descriptor(0).ecm().asset_id(); 83 84 // Detect asset_id change. This could be caused by a configuration change 85 // in the BroadcastEncryptor. This is unusual so log it in case it is an 86 // operational mistake. This invalidates the current asset_key causing a 87 // new license to be fetched. 88 // TODO(rkint): test against BroadcastEncryptor to verify what BE sends on 89 // asset_id change. If it sends an EcmContainer with 2 Ecms with different 90 // asset_ids (old and new) then it might be best to prefetch the Emm. 91 if ((asset_.id() != 0) && (*asset_id != asset_.id())) { 92 ALOGW("Asset_id change from %llu to %" PRIu64, asset_.id(), *asset_id); 93 asset_.Clear(); 94 } 95 96 // Fetch license to get asset_id 97 if (!asset_.has_id()) { 98 status = license_fetcher_->FetchLicense(*asset_id, &asset_); 99 if (status != OK) { 100 *asset_id = 0; 101 return status; 102 } 103 ALOGV("FetchLicense succeeded, has_id=%d", asset_.has_id()); 104 } 105 keys->resize(container.descriptor_size()); 106 107 for (size_t i = 0; i < container.descriptor_size(); ++i) { 108 status = container.mutable_descriptor(i)->mutable_ecm()->Decrypt( 109 container.descriptor(i).ecm().buffer(), asset_); 110 if (status != OK) { 111 *asset_id = 0; 112 keys->clear(); 113 return status; 114 } 115 // TODO: if 2 Ecms have same parity, key from Ecm with higher id 116 // should be keys[1]. 117 KeyInfo key; 118 key.key_id = container.descriptor(i).id(); 119 key.key_bytes = container.descriptor(i).ecm().content_key(); 120 121 keys->at(key.key_id & 1) = key; 122 } 123 return OK; 124} 125 126} // namespace clearkeycas 127} // namespace android 128