/* * 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 "ecm" #include #include "ecm.h" #include "ecm_generator.h" #include "protos/license_protos.pb.h" #include #include #include namespace android { namespace clearkeycas { Ecm::Ecm() : asset_id_(0), asset_id_set_(false), system_id_(0), system_id_set_(false) {} Ecm::~Ecm() {} status_t Ecm::Parse(const sp& buffer_as_binary) { if (buffer_as_binary->size() < kSizeBytes) { ALOGE("Short Ecm buffer: expected %zu, received %zu.", kSizeBytes, buffer_as_binary->size()); return BAD_VALUE; } Asset asset; ecm_generator::DefaultEcmFields default_fields; status_t status = ecm_generator::DecodeECMClearFields( buffer_as_binary, &asset, &default_fields); if (status != OK) { ALOGE("DecodeECMClearFields failed with status %d", status); return status; } set_asset_id(asset.id()); set_system_id(default_fields.system_id); // Save a copy of the buffer_as_binary for a future DecryptEcm call. set_buffer(buffer_as_binary); return OK; } status_t Ecm::Decrypt( const sp& buffer_as_binary, const Asset& asset_from_emm) { // Invariant: asset has id. These are postconditions for Emm::Decrypt(). CHECK(asset_from_emm.has_id()); // DecodeEcm fills in |asset|.id() with the asset_id from the encoded Ecm. Asset asset(asset_from_emm); ecm_generator::DefaultEcmFields default_fields; sp content_key; status_t status = ecm_generator::DecodeECM( buffer_as_binary, &asset, &content_key, &default_fields); if (status != OK) { ALOGE("DecodeECM failed with status %d", status); return status; } if (asset.id() != asset_from_emm.id()) { ALOGE("Asset_id from Emm (%" PRIu64 ") does not match asset_id from Ecm (%" PRIu64 ").", asset_from_emm.id(), asset.id()); return CLEARKEY_STATUS_INVALID_PARAMETER; } set_asset_id(asset.id()); set_system_id(default_fields.system_id); set_content_key(content_key); return status; } EcmDescriptor::EcmDescriptor() : ecm_set_(false), id_(0), id_set_(false) {} EcmDescriptor::EcmDescriptor(uint16_t id, const Ecm& ecm) : ecm_(ecm), ecm_set_(true), id_(id), id_set_(true) {} EcmDescriptor::~EcmDescriptor() {} status_t EcmDescriptor::Parse(const sp& buffer_as_binary) { if (buffer_as_binary->size() < kSizeBytes) { ALOGE("Short EcmDescriptor buffer: expected %zu, received %zu.", kSizeBytes, buffer_as_binary->size()); return BAD_VALUE; } sp id_buffer = new ABuffer(buffer_as_binary->data(), kIdSizeBytes); const uint8_t *id_bytes = id_buffer->data(); uint16_t id = (id_bytes[0] << 8) | id_bytes[1]; set_id(id); // Unmarshall the contained Ecm. sp ecm_buffer = new ABuffer( buffer_as_binary->data() + kIdSizeBytes, Ecm::kSizeBytes); status_t status = mutable_ecm()->Parse(ecm_buffer); if (status != OK) { return status; } return OK; } EcmContainer::EcmContainer() : count_(0), count_set_(false) {} EcmContainer::~EcmContainer() {} status_t EcmContainer::Add(const EcmDescriptor& descriptor) { switch (count_) { case 0: descriptor_[0] = descriptor; count_ = 1; break; case 1: descriptor_[1] = descriptor; count_ = 2; break; case 2: descriptor_[0] = descriptor_[1]; descriptor_[1] = descriptor; break; default: ALOGE("Bad state."); return INVALID_OPERATION; } count_set_ = true; return OK; } status_t EcmContainer::Parse(const sp& buffer_as_binary) { // EcmContainer can contain 1 or 2 EcmDescriptors so this is a check for // minimum size. if (buffer_as_binary->size() < kMinimumSizeBytes) { ALOGE("Short EcmContainer buffer: expected >= %zu, received %zu.", kMinimumSizeBytes, buffer_as_binary->size()); return BAD_VALUE; } sp count_buffer = new ABuffer( buffer_as_binary->data(), kCountSizeBytes); const uint8_t *count_bytes = count_buffer->data(); size_t count = (count_bytes[0] << 8) | count_bytes[1]; // Check that count is a legal value. if (!CountLegal(count)) { ALOGE("Invalid descriptor count: expected %zu <= count <= %zu, received %zu.", kMinDescriptorCount, kMaxDescriptorCount, count); return ERROR_OUT_OF_RANGE; } // If needed, check that buffer_as_binary can hold 2 EcmDescriptors. if (count > kMinDescriptorCount) { size_t expected_bytes = kCountSizeBytes + (count * EcmDescriptor::kSizeBytes); if (buffer_as_binary->size() < expected_bytes) { ALOGE("Short EcmContainer buffer: expected %zu, received %zu.", expected_bytes, buffer_as_binary->size()); return BAD_VALUE; } } set_count(count); // Unmarshall the contained EcmDescriptors. size_t offset = kCountSizeBytes; for (size_t i = 0; i < count_; ++i) { sp descriptor_buffer = new ABuffer( buffer_as_binary->data() + offset, EcmDescriptor::kSizeBytes); status_t status = mutable_descriptor(i)->Parse(descriptor_buffer); if (status != OK) { return status; } offset += EcmDescriptor::kSizeBytes; } return OK; } } // namespace clearkeycas } // namespace android