/* * Copyright (C) 2021 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 "CryptoHalAidl" #include #include #include #include #include #include #include #include #include #include using ::aidl::android::hardware::drm::CryptoSchemes; using DestinationBufferAidl = ::aidl::android::hardware::drm::DestinationBuffer; using ::aidl::android::hardware::drm::Mode; using ::aidl::android::hardware::drm::Pattern; using SharedBufferAidl = ::aidl::android::hardware::drm::SharedBuffer; using ::aidl::android::hardware::drm::Status; using ::aidl::android::hardware::drm::SubSample; using ::aidl::android::hardware::drm::Uuid; using ::aidl::android::hardware::drm::SecurityLevel; using NativeHandleAidlCommon = ::aidl::android::hardware::common::NativeHandle; using ::aidl::android::hardware::drm::DecryptArgs; using ::android::sp; using ::android::DrmUtils::statusAidlToDrmStatus; using ::android::hardware::hidl_array; using ::android::hardware::hidl_handle; using ::android::hardware::hidl_memory; using ::android::hardware::hidl_string; using ::android::hardware::hidl_vec; using ::android::hardware::HidlMemory; using ::android::hardware::Return; using ::android::hardware::Void; using ::aidl::android::hardware::drm::Uuid; // -------Hidl interface related----------------- // TODO: replace before removing hidl interface using BufferTypeHidl = ::android::hardware::drm::V1_0::BufferType; using SharedBufferHidl = ::android::hardware::drm::V1_0::SharedBuffer; using DestinationBufferHidl = ::android::hardware::drm::V1_0::DestinationBuffer; // -------Hidl interface related end------------- namespace android { template static std::vector toStdVec(const Vector& vector) { auto v = reinterpret_cast(vector.array()); std::vector vec(v, v + vector.size()); return vec; } // -------Hidl interface related----------------- // TODO: replace before removing hidl interface status_t CryptoHalAidl::checkSharedBuffer(const SharedBufferHidl& buffer) { int32_t seqNum = static_cast(buffer.bufferId); // memory must be in one of the heaps that have been set if (mHeapSizes.indexOfKey(seqNum) < 0) { return UNKNOWN_ERROR; } // memory must be within the address space of the heap size_t heapSize = mHeapSizes.valueFor(seqNum); if (heapSize < buffer.offset + buffer.size || SIZE_MAX - buffer.offset < buffer.size) { android_errorWriteLog(0x534e4554, "76221123"); return UNKNOWN_ERROR; } return OK; } static SharedBufferAidl hidlSharedBufferToAidlSharedBuffer(const SharedBufferHidl& buffer) { SharedBufferAidl aidlsb; aidlsb.bufferId = buffer.bufferId; aidlsb.offset = buffer.offset; aidlsb.size = buffer.size; return aidlsb; } static DestinationBufferAidl hidlDestinationBufferToAidlDestinationBuffer( const DestinationBufferHidl& buffer) { DestinationBufferAidl aidldb; // skip negative convert check as count of enum elements are 2 switch(buffer.type) { case BufferTypeHidl::SHARED_MEMORY: aidldb.set( hidlSharedBufferToAidlSharedBuffer(buffer.nonsecureMemory)); break; default: auto handle = buffer.secureMemory.getNativeHandle(); if (handle) { aidldb.set( ::android::dupToAidl(handle)); } else { NativeHandleAidlCommon emptyhandle; aidldb.set( std::move(emptyhandle)); } break; } return aidldb; } static hidl_vec toHidlVec(const void* ptr, size_t size) { hidl_vec vec; vec.resize(size); memcpy(vec.data(), ptr, size); return vec; } static const Vector toVector(const std::vector& vec) { Vector vector; vector.appendArray(vec.data(), vec.size()); return *const_cast*>(&vector); } static String8 toString8(const std::string& string) { return String8(string.c_str()); } static std::vector toStdVec(const uint8_t* ptr, size_t n) { if (!ptr) { return std::vector(); } return std::vector(ptr, ptr + n); } // -------Hidl interface related end-------------- bool CryptoHalAidl::isCryptoSchemeSupportedInternal(const uint8_t uuid[16], int* factoryIdx) { Uuid uuidAidl = DrmUtils::toAidlUuid(uuid); for (size_t i = 0; i < mFactories.size(); i++) { CryptoSchemes schemes{}; if (mFactories[i]->getSupportedCryptoSchemes(&schemes).isOk()) { if (std::count(schemes.uuids.begin(), schemes.uuids.end(), uuidAidl)) { if (factoryIdx != NULL) *factoryIdx = i; return true; } } } return false; } CryptoHalAidl::CryptoHalAidl() : mFactories(DrmUtils::makeDrmFactoriesAidl()), mInitCheck((mFactories.size() == 0) ? ERROR_UNSUPPORTED : NO_INIT), mHeapSeqNum(0) {} CryptoHalAidl::~CryptoHalAidl() {} status_t CryptoHalAidl::initCheck() const { return mInitCheck; } bool CryptoHalAidl::isCryptoSchemeSupported(const uint8_t uuid[16]) { Mutex::Autolock autoLock(mLock); return isCryptoSchemeSupportedInternal(uuid, NULL); } status_t CryptoHalAidl::createPlugin(const uint8_t uuid[16], const void* data, size_t size) { Mutex::Autolock autoLock(mLock); Uuid uuidAidl = DrmUtils::toAidlUuid(uuid); std::vector dataAidl = toStdVec(toVector(toHidlVec(data, size))); int i = 0; if (isCryptoSchemeSupportedInternal(uuid, &i)) { mPlugin = makeCryptoPlugin(mFactories[i], uuidAidl, dataAidl); } if (mInitCheck == NO_INIT) { mInitCheck = mPlugin == NULL ? ERROR_UNSUPPORTED : OK; } return mInitCheck; } std::shared_ptr CryptoHalAidl::makeCryptoPlugin( const std::shared_ptr& factory, const Uuid& uuidAidl, const std::vector initData) { std::shared_ptr pluginAidl; if (factory->createCryptoPlugin(uuidAidl, initData, &pluginAidl).isOk()) { ALOGI("Create ICryptoPluginAidl. UUID:[%s]", uuidAidl.toString().c_str()); } else { mInitCheck = DEAD_OBJECT; ALOGE("Failed to create ICryptoPluginAidl. UUID:[%s]", uuidAidl.toString().c_str()); } return pluginAidl; } status_t CryptoHalAidl::destroyPlugin() { Mutex::Autolock autoLock(mLock); if (mInitCheck != OK) { return mInitCheck; } mPlugin.reset(); mInitCheck = NO_INIT; return OK; } bool CryptoHalAidl::requiresSecureDecoderComponent(const char* mime) const { Mutex::Autolock autoLock(mLock); if (mInitCheck != OK) { return false; } std::string mimeStr = std::string(mime); bool result; if (!mPlugin->requiresSecureDecoderComponent(mimeStr, &result).isOk()) { ALOGE("Failed to requiresSecureDecoderComponent. mime:[%s]", mime); return false; } return result; } void CryptoHalAidl::notifyResolution(uint32_t width, uint32_t height) { Mutex::Autolock autoLock(mLock); if (mInitCheck != OK) { return; } // Check negative width and height after type conversion // Log error and return if any is negative if ((int32_t)width < 0 || (int32_t)height < 0) { ALOGE("Negative width: %d or height %d in notifyResolution", width, height); return; } ::ndk::ScopedAStatus status = mPlugin->notifyResolution(width, height); if (!status.isOk()) { ALOGE("notifyResolution txn failed status code: %d", status.getServiceSpecificError()); } } DrmStatus CryptoHalAidl::setMediaDrmSession(const Vector& sessionId) { Mutex::Autolock autoLock(mLock); if (mInitCheck != OK) { return mInitCheck; } auto err = mPlugin->setMediaDrmSession(toStdVec(sessionId)); return statusAidlToDrmStatus(err); } ssize_t CryptoHalAidl::decrypt(const uint8_t keyId[16], const uint8_t iv[16], CryptoPlugin::Mode mode, const CryptoPlugin::Pattern& pattern, const SharedBufferHidl& hSource, size_t offset, const CryptoPlugin::SubSample* subSamples, size_t numSubSamples, const DestinationBufferHidl& hDestination, AString* errorDetailMsg) { Mutex::Autolock autoLock(mLock); if (mInitCheck != OK) { return mInitCheck; } Mode aMode; switch (mode) { case CryptoPlugin::kMode_Unencrypted: aMode = Mode::UNENCRYPTED; break; case CryptoPlugin::kMode_AES_CTR: aMode = Mode::AES_CTR; break; case CryptoPlugin::kMode_AES_WV: aMode = Mode::AES_CBC_CTS; break; case CryptoPlugin::kMode_AES_CBC: aMode = Mode::AES_CBC; break; default: return UNKNOWN_ERROR; } Pattern aPattern; aPattern.encryptBlocks = pattern.mEncryptBlocks; aPattern.skipBlocks = pattern.mSkipBlocks; std::vector stdSubSamples; for (size_t i = 0; i < numSubSamples; i++) { SubSample subSample; subSample.numBytesOfClearData = subSamples[i].mNumBytesOfClearData; subSample.numBytesOfEncryptedData = subSamples[i].mNumBytesOfEncryptedData; stdSubSamples.push_back(subSample); } bool secure; if (hDestination.type == BufferTypeHidl::SHARED_MEMORY) { status_t status = checkSharedBuffer(hDestination.nonsecureMemory); if (status != OK) { return status; } secure = false; } else if (hDestination.type == BufferTypeHidl::NATIVE_HANDLE) { secure = true; } else { android_errorWriteLog(0x534e4554, "70526702"); return UNKNOWN_ERROR; } status_t status = checkSharedBuffer(hSource); if (status != OK) { return status; } status_t err = UNKNOWN_ERROR; mLock.unlock(); std::vector keyIdAidl(toStdVec(keyId, 16)); std::vector ivAidl(toStdVec(iv, 16)); DecryptArgs args; args.secure = secure; args.keyId = keyIdAidl; args.iv = ivAidl; args.mode = aMode; args.pattern = aPattern; args.subSamples = std::move(stdSubSamples); args.source = hidlSharedBufferToAidlSharedBuffer(hSource); args.offset = offset; args.destination = hidlDestinationBufferToAidlDestinationBuffer(hDestination); int32_t result = 0; ::ndk::ScopedAStatus statusAidl = mPlugin->decrypt(args, &result); err = statusAidlToDrmStatus(statusAidl); std::string msgStr(statusAidl.getMessage()); if (errorDetailMsg != nullptr) { *errorDetailMsg = toString8(msgStr); } if (err != OK) { ALOGE("Failed on decrypt, error description:%s", statusAidl.getDescription().c_str()); return err; } return result; } int32_t CryptoHalAidl::setHeap(const sp& heap) { if (heap == NULL || mHeapSeqNum < 0) { ALOGE("setHeap(): heap %p mHeapSeqNum %d", heap.get(), mHeapSeqNum); return -1; } Mutex::Autolock autoLock(mLock); if (mInitCheck != OK) { return -1; } int32_t seqNum = mHeapSeqNum++; uint32_t bufferId = static_cast(seqNum); mHeapSizes.add(seqNum, heap->size()); SharedBufferAidl memAidl; memAidl.handle = ::android::dupToAidl(heap->handle()); memAidl.size = heap->size(); memAidl.bufferId = bufferId; auto status = mPlugin->setSharedBufferBase(memAidl); ALOGE_IF(!status.isOk(), "setSharedBufferBase(): remote call failed"); return seqNum; } void CryptoHalAidl::unsetHeap(int32_t seqNum) { Mutex::Autolock autoLock(mLock); /* * Clear the remote shared memory mapping by setting the shared * buffer base to a null hidl_memory. * * TODO: Add a releaseSharedBuffer method in a future DRM HAL * API version to make this explicit. */ ssize_t index = mHeapSizes.indexOfKey(seqNum); if (index >= 0) { if (mPlugin != NULL) { uint32_t bufferId = static_cast(seqNum); SharedBufferAidl memAidl{}; memAidl.bufferId = bufferId; auto status = mPlugin->setSharedBufferBase(memAidl); ALOGE_IF(!status.isOk(), "setSharedBufferBase(): remote call failed"); } mHeapSizes.removeItem(seqNum); } } status_t CryptoHalAidl::getLogMessages(Vector& logs) const { Mutex::Autolock autoLock(mLock); // Need to convert logmessage return DrmUtils::GetLogMessagesAidl(mPlugin, logs); } } // namespace android