| /* |
| * Copyright (C) 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| //#define LOG_NDEBUG 0 |
| #define LOG_TAG "ClearKeyFetcher" |
| |
| #include <algorithm> |
| #include <inttypes.h> |
| #include <string> |
| |
| #include "ClearKeyFetcher.h" |
| #include "ecm.h" |
| #include "LicenseFetcher.h" |
| |
| #include <media/stagefright/foundation/ADebug.h> |
| #include <utils/Log.h> |
| |
| namespace android { |
| namespace clearkeycas { |
| |
| ClearKeyFetcher::ClearKeyFetcher( |
| std::unique_ptr<LicenseFetcher> license_fetcher) : |
| initialized_(false), |
| license_fetcher_(std::move(license_fetcher)) { |
| CHECK(license_fetcher_); |
| } |
| |
| ClearKeyFetcher::~ClearKeyFetcher() {} |
| |
| // This is a no-op but other KeyFetcher subclasses require initialization |
| // so this is necessary to preserve the contract. |
| status_t ClearKeyFetcher::Init() { |
| initialized_ = true; |
| return OK; |
| } |
| |
| status_t ClearKeyFetcher::ObtainKey(const sp<ABuffer>& buffer, |
| uint64_t* asset_id, std::vector<KeyInfo>* keys) { |
| CHECK(asset_id); |
| CHECK(keys); |
| CHECK(initialized_); |
| *asset_id = 0; |
| keys->clear(); |
| |
| EcmContainer container; |
| status_t status = container.Parse(buffer); |
| if (status != OK) { |
| return status; |
| } |
| ALOGV("descriptor_size=%zu", container.descriptor_size()); |
| |
| // Validate that the BroadcastEncryptor is sending a properly formed |
| // EcmContainer. If it contains two Ecms, the ids should have different |
| // parity (one odd, one even). This does not necessarily affect decryption |
| // but indicates a problem with Ecm generation. |
| if (container.descriptor_size() == 2) { |
| // XOR the least significant bits to verify different parity. |
| bool same_parity = (((container.descriptor(0).id() & 0x01) ^ |
| (container.descriptor(1).id() & 0x01)) == 0); |
| if (same_parity) { |
| ALOGW("asset_id=%" PRIu64 ": malformed Ecm, " |
| "content keys have same parity, id0=%d, id1=%d", |
| container.descriptor(0).ecm().asset_id(), |
| container.descriptor(0).id(), |
| container.descriptor(1).id()); |
| } |
| } |
| |
| *asset_id = container.descriptor(0).ecm().asset_id(); |
| |
| // Detect asset_id change. This could be caused by a configuration change |
| // in the BroadcastEncryptor. This is unusual so log it in case it is an |
| // operational mistake. This invalidates the current asset_key causing a |
| // new license to be fetched. |
| // TODO(rkint): test against BroadcastEncryptor to verify what BE sends on |
| // asset_id change. If it sends an EcmContainer with 2 Ecms with different |
| // asset_ids (old and new) then it might be best to prefetch the Emm. |
| if ((asset_.id() != 0) && (*asset_id != asset_.id())) { |
| ALOGW("Asset_id change from %" PRIu64 " to %" PRIu64, asset_.id(), *asset_id); |
| asset_.Clear(); |
| } |
| |
| // Fetch license to get asset_id |
| if (!asset_.has_id()) { |
| status = license_fetcher_->FetchLicense(*asset_id, &asset_); |
| if (status != OK) { |
| *asset_id = 0; |
| return status; |
| } |
| ALOGV("FetchLicense succeeded, has_id=%d", asset_.has_id()); |
| } |
| keys->resize(container.descriptor_size()); |
| |
| for (size_t i = 0; i < container.descriptor_size(); ++i) { |
| status = container.mutable_descriptor(i)->mutable_ecm()->Decrypt( |
| container.descriptor(i).ecm().buffer(), asset_); |
| if (status != OK) { |
| *asset_id = 0; |
| keys->clear(); |
| return status; |
| } |
| // TODO: if 2 Ecms have same parity, key from Ecm with higher id |
| // should be keys[1]. |
| KeyInfo key; |
| key.key_id = container.descriptor(i).id(); |
| key.key_bytes = container.descriptor(i).ecm().content_key(); |
| |
| keys->at(key.key_id & 1) = key; |
| } |
| return OK; |
| } |
| |
| } // namespace clearkeycas |
| } // namespace android |