From 7684054d78cb27fbb3f5b0c19b09cd80356bba88 Mon Sep 17 00:00:00 2001 From: Lu Chen Date: Mon, 24 Jan 2022 11:06:22 -0800 Subject: [PATCH] Widvine remote provisioning HAL implementation This HAL implementation should be included in the factory image only. BUG: 213415013 Test: manual Change-Id: Icc0cc7f767a647238ce319623e0408ec22531f58 --- libwvdrmengine/factory_upload_tool/Android.bp | 52 ++++ libwvdrmengine/factory_upload_tool/README | 1 + ...dware.security.keymint-service.widevine.rc | 4 + ...ware.security.keymint-service.widevine.xml | 6 + .../include/WidevineOemcryptoInterface.h | 52 ++++ .../include/WidevineProvisioner.h | 50 ++++ .../WidevineRemotelyProvisionedComponent.h | 55 ++++ .../factory_upload_tool/include/properties.h | 34 +++ .../factory_upload_tool/service.cpp | 48 ++++ .../src/WidevineOemcryptoInterface.cpp | 143 ++++++++++ .../src/WidevineProvisioner.cpp | 267 ++++++++++++++++++ .../WidevineRemotelyProvisionedComponent.cpp | 115 ++++++++ .../factory_upload_tool/src/log.cpp | 91 ++++++ .../src/properties_android.cpp | 93 ++++++ 14 files changed, 1011 insertions(+) create mode 100644 libwvdrmengine/factory_upload_tool/Android.bp create mode 100644 libwvdrmengine/factory_upload_tool/README create mode 100644 libwvdrmengine/factory_upload_tool/android.hardware.security.keymint-service.widevine.rc create mode 100644 libwvdrmengine/factory_upload_tool/android.hardware.security.keymint-service.widevine.xml create mode 100644 libwvdrmengine/factory_upload_tool/include/WidevineOemcryptoInterface.h create mode 100644 libwvdrmengine/factory_upload_tool/include/WidevineProvisioner.h create mode 100644 libwvdrmengine/factory_upload_tool/include/WidevineRemotelyProvisionedComponent.h create mode 100644 libwvdrmengine/factory_upload_tool/include/properties.h create mode 100644 libwvdrmengine/factory_upload_tool/service.cpp create mode 100644 libwvdrmengine/factory_upload_tool/src/WidevineOemcryptoInterface.cpp create mode 100644 libwvdrmengine/factory_upload_tool/src/WidevineProvisioner.cpp create mode 100644 libwvdrmengine/factory_upload_tool/src/WidevineRemotelyProvisionedComponent.cpp create mode 100644 libwvdrmengine/factory_upload_tool/src/log.cpp create mode 100644 libwvdrmengine/factory_upload_tool/src/properties_android.cpp diff --git a/libwvdrmengine/factory_upload_tool/Android.bp b/libwvdrmengine/factory_upload_tool/Android.bp new file mode 100644 index 00000000..03a017f0 --- /dev/null +++ b/libwvdrmengine/factory_upload_tool/Android.bp @@ -0,0 +1,52 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "vendor_widevine_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + // legacy_by_exception_only (by exception only) + default_applicable_licenses: ["vendor_widevine_license"], +} + + + +cc_binary { + name: "android.hardware.security.keymint-service.widevine", + relative_install_path: "hw", + init_rc: ["android.hardware.security.keymint-service.widevine.rc"], + vintf_fragments: [ + "android.hardware.security.keymint-service.widevine.xml", + ], + vendor: true, + cflags: [ + "-Wall", + "-Wextra", + ], + shared_libs: [ + "android.hardware.security.keymint-V2-ndk", + "libbase", + "libbinder_ndk", + "libcppbor_external", + "libcrypto", + "libkeymaster_portable", + "libkeymint", + "liblog", + "libpuresoftkeymasterdevice", + "libutils", + "libcppcose_rkp", + ], + srcs: [ + "service.cpp", + "src/log.cpp", + "src/properties_android.cpp", + "src/WidevineRemotelyProvisionedComponent.cpp", + "src/WidevineProvisioner.cpp", + "src/WidevineOemcryptoInterface.cpp", + ], + include_dirs: [ + "vendor/widevine/libwvdrmengine/oemcrypto/include", + "vendor/widevine/libwvdrmengine/cdm/util/include", + "vendor/widevine/libwvdrmengine/factory_upload_tool/include", + ], +} + diff --git a/libwvdrmengine/factory_upload_tool/README b/libwvdrmengine/factory_upload_tool/README new file mode 100644 index 00000000..af5b1353 --- /dev/null +++ b/libwvdrmengine/factory_upload_tool/README @@ -0,0 +1 @@ +This folder contains Widevine's implementation of Android Remote Provisioning HAL, which is used as part of provisioning 4.0 process. It is intented for factory usage only, and should not be present on user devices. \ No newline at end of file diff --git a/libwvdrmengine/factory_upload_tool/android.hardware.security.keymint-service.widevine.rc b/libwvdrmengine/factory_upload_tool/android.hardware.security.keymint-service.widevine.rc new file mode 100644 index 00000000..43d0d5f5 --- /dev/null +++ b/libwvdrmengine/factory_upload_tool/android.hardware.security.keymint-service.widevine.rc @@ -0,0 +1,4 @@ +service vendor.keymint-widevine /vendor/bin/hw/android.hardware.security.keymint-service.widevine + class early_hal + user nobody + group drmrpc \ No newline at end of file diff --git a/libwvdrmengine/factory_upload_tool/android.hardware.security.keymint-service.widevine.xml b/libwvdrmengine/factory_upload_tool/android.hardware.security.keymint-service.widevine.xml new file mode 100644 index 00000000..723026ae --- /dev/null +++ b/libwvdrmengine/factory_upload_tool/android.hardware.security.keymint-service.widevine.xml @@ -0,0 +1,6 @@ + + + android.hardware.security.keymint + IRemotelyProvisionedComponent/widevine + + diff --git a/libwvdrmengine/factory_upload_tool/include/WidevineOemcryptoInterface.h b/libwvdrmengine/factory_upload_tool/include/WidevineOemcryptoInterface.h new file mode 100644 index 00000000..be355532 --- /dev/null +++ b/libwvdrmengine/factory_upload_tool/include/WidevineOemcryptoInterface.h @@ -0,0 +1,52 @@ +// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine License +// Agreement. + +#ifndef WIDEVINE_OEMCRYPTO_INTERFACE_H_ +#define WIDEVINE_OEMCRYPTO_INTERFACE_H_ + +#include +#include +#include + +#include "OEMCryptoCENC.h" + +namespace widevine { + +class OEMCryptoInterface { + public: + OEMCryptoInterface() = default; + OEMCryptoInterface(const OEMCryptoInterface&) = delete; + OEMCryptoInterface& operator=(const OEMCryptoInterface&) = delete; + virtual ~OEMCryptoInterface(); + + // Initializes this interface by providing path to the OEMCrypto library. + bool Init(const std::string& oemcrypto_path); + + // Retrieves the boot certificate chain from OEMCrypto implementation. + OEMCryptoResult GetBcc(std::vector& bcc); + + // Retrieves the build information of the OEMCrypto library from OEMCrypto + // implementation. + OEMCryptoResult GetOEMCryptoBuildInfo(std::string& build_info); + + private: + typedef OEMCryptoResult (*Initialize_t)(); + typedef OEMCryptoResult (*Terminate_t)(); + typedef OEMCryptoResult (*GetBootCertificateChain_t)( + uint8_t* bcc, size_t* bcc_size, uint8_t* additional_signature, + size_t* additional_signature_size); + typedef OEMCryptoResult (*BuildInformation_t)(char* buffer, + size_t* buffer_length); + + Initialize_t Initialize = nullptr; + Terminate_t Terminate = nullptr; + GetBootCertificateChain_t GetBootCertificateChain = nullptr; + BuildInformation_t BuildInformation = nullptr; + + void* handle_ = nullptr; +}; + +} // namespace widevine + +#endif // WIDEVINE_OEMCRYPTO_INTERFACE_H_ \ No newline at end of file diff --git a/libwvdrmengine/factory_upload_tool/include/WidevineProvisioner.h b/libwvdrmengine/factory_upload_tool/include/WidevineProvisioner.h new file mode 100644 index 00000000..41014642 --- /dev/null +++ b/libwvdrmengine/factory_upload_tool/include/WidevineProvisioner.h @@ -0,0 +1,50 @@ +// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine License +// Agreement. + +#ifndef WIDEVINE_PROVISIONER_H_ +#define WIDEVINE_PROVISIONER_H_ + +#include + +#include +#include +#include +#include +#include + +#include "WidevineOemcryptoInterface.h" + +namespace widevine { + +class WidevineProvisioner { + public: + WidevineProvisioner(); + WidevineProvisioner(const WidevineProvisioner&) = delete; + WidevineProvisioner& operator=(const WidevineProvisioner&) = delete; + virtual ~WidevineProvisioner() = default; + + bool GenerateCertificateRequest( + bool testMode, const std::vector& endpointEncCertChain, + std::vector& deviceInfo, std::vector& protectedData); + + private: + bool GetDeviceInfo(std::vector& device_info); + bool GenerateProtectedData( + bool test_mode, + const std::vector& endpoint_encryption_cert_chain, + std::vector bcc, std::vector& protected_data) const; + bool ValidateAndExtractEekPubAndId( + bool test_mode, + const std::vector& endpoint_encryption_cert_chain, + std::vector* eek_pub, std::vector* eek_id) const; + cppbor::Array BuildCertReqRecipients(const std::vector& pubkey, + const std::vector& kid) const; + void InitializeCryptoInterface(); + + std::unique_ptr crypto_interface_; +}; + +} // namespace widevine + +#endif // WIDEVINE_PROVISIONER_H_ diff --git a/libwvdrmengine/factory_upload_tool/include/WidevineRemotelyProvisionedComponent.h b/libwvdrmengine/factory_upload_tool/include/WidevineRemotelyProvisionedComponent.h new file mode 100644 index 00000000..2a107b58 --- /dev/null +++ b/libwvdrmengine/factory_upload_tool/include/WidevineRemotelyProvisionedComponent.h @@ -0,0 +1,55 @@ +/* + * Copyright 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. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "WidevineProvisioner.h" + +namespace aidl::android::hardware::security::keymint { + +class WidevineRemotelyProvisionedComponent + : public BnRemotelyProvisionedComponent { + using ScopedAStatus = ::ndk::ScopedAStatus; + + public: + WidevineRemotelyProvisionedComponent() = default; + virtual ~WidevineRemotelyProvisionedComponent() = default; + + ScopedAStatus getHardwareInfo(RpcHardwareInfo* info) override; + + ScopedAStatus generateEcdsaP256KeyPair( + bool testMode, MacedPublicKey* macedPublicKey, + std::vector* privateKeyHandle) override; + + ScopedAStatus generateCertificateRequest( + bool testMode, const std::vector& keysToSign, + const std::vector& endpointEncCertChain, + const std::vector& challenge, DeviceInfo* deviceInfo, + ProtectedData* protectedData, + std::vector* keysToSignMac) override; + + private: + std::unique_ptr provisioner_; +}; + +} // namespace aidl::android::hardware::security::keymint diff --git a/libwvdrmengine/factory_upload_tool/include/properties.h b/libwvdrmengine/factory_upload_tool/include/properties.h new file mode 100644 index 00000000..d61394f8 --- /dev/null +++ b/libwvdrmengine/factory_upload_tool/include/properties.h @@ -0,0 +1,34 @@ +// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine License +// Agreement. + +#ifndef WVCDM_CORE_PROPERTIES_H_ +#define WVCDM_CORE_PROPERTIES_H_ + +#include +#include +#include +#include + +#include "disallow_copy_and_assign.h" + +namespace wvcdm { + +// This class gives device information/meta data. +class Properties { + public: + static bool GetCompanyName(std::string* company_name); + static bool GetModelName(std::string* model_name); + static bool GetArchitectureName(std::string* arch_name); + static bool GetDeviceName(std::string* device_name); + static bool GetProductName(std::string* product_name); + static bool GetBuildInfo(std::string* build_info); + static bool GetOEMCryptoPath(std::string* library_name); + + private: + CORE_DISALLOW_COPY_AND_ASSIGN(Properties); +}; + +} // namespace wvcdm + +#endif // WVCDM_CORE_PROPERTIES_H_ diff --git a/libwvdrmengine/factory_upload_tool/service.cpp b/libwvdrmengine/factory_upload_tool/service.cpp new file mode 100644 index 00000000..60991cab --- /dev/null +++ b/libwvdrmengine/factory_upload_tool/service.cpp @@ -0,0 +1,48 @@ +/* + * Copyright 2020, 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_TAG "android.hardware.security.wv_keymint-service" + +#include +#include +#include + +#include "WidevineRemotelyProvisionedComponent.h" + +using aidl::android::hardware::security::keymint:: + WidevineRemotelyProvisionedComponent; + +template +std::shared_ptr addService(Args&&... args) { + std::shared_ptr ser = + ndk::SharedRefBase::make(std::forward(args)...); + auto instanceName = std::string(T::descriptor) + "/widevine"; + LOG(INFO) << "adding keymint service instance: " << instanceName; + binder_status_t status = + AServiceManager_addService(ser->asBinder().get(), instanceName.c_str()); + CHECK(status == STATUS_OK); + return ser; +} + +int main() { + // Zero threads seems like a useless pool, but below we'll join this thread to + // it, increasing the pool size to 1. + ABinderProcess_setThreadPoolMaxThreadCount(0); + // Add Remotely Provisioned Component Service + addService(); + ABinderProcess_joinThreadPool(); + return EXIT_FAILURE; // should not reach +} diff --git a/libwvdrmengine/factory_upload_tool/src/WidevineOemcryptoInterface.cpp b/libwvdrmengine/factory_upload_tool/src/WidevineOemcryptoInterface.cpp new file mode 100644 index 00000000..dee299c6 --- /dev/null +++ b/libwvdrmengine/factory_upload_tool/src/WidevineOemcryptoInterface.cpp @@ -0,0 +1,143 @@ +// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine License +// Agreement. + +#include "WidevineOemcryptoInterface.h" + +#include + +#include "OEMCryptoCENC.h" +#include "clock.h" +#include "file_store.h" +#include "log.h" + +// These macros lookup the obfuscated name used for OEMCrypto. +#define QUOTE_DEFINE(A) #A +#define QUOTE(A) QUOTE_DEFINE(A) +#define LOOKUP(handle, name) dlsym(handle, QUOTE(name)) +#define LOAD_SYM(name) \ + name = reinterpret_cast(LOOKUP(handle_, OEMCrypto_##name)); \ + if (name == nullptr) { \ + LOGE("%s", dlerror()); \ + return false; \ + } + +// These are implementations required by OEMCrypto Reference Implementation +// and/or the Testbed, but not needed in this package. +namespace wvutil { +int64_t Clock::GetCurrentTime() { return 0; } + +class FileImpl final : public File { + public: + FileImpl() {} + ssize_t Read(char*, size_t) override { return 0; } + ssize_t Write(const char*, size_t) override { return 0; } +}; + +class FileSystem::Impl { + public: + Impl() {} +}; + +FileSystem::FileSystem() {} +FileSystem::FileSystem(const std::string&, void*) {} +FileSystem::~FileSystem() {} +std::unique_ptr FileSystem::Open(const std::string&, int) { + return std::unique_ptr(new FileImpl()); +} +bool FileSystem::Exists(const std::string&) { return false; } +bool FileSystem::Remove(const std::string&) { return false; } +ssize_t FileSystem::FileSize(const std::string&) { return false; } +bool FileSystem::List(const std::string&, std::vector*) { + return false; +} + +} // namespace wvutil + +namespace widevine { + +OEMCryptoInterface::~OEMCryptoInterface() { + if (Terminate != nullptr) { + Terminate(); + } + if (handle_ != nullptr) { + dlclose(handle_); + } +} + +bool OEMCryptoInterface::Init(const std::string& oemcrypto_path) { + dlerror(); + handle_ = dlopen(oemcrypto_path.c_str(), RTLD_LAZY | RTLD_GLOBAL); + if (handle_ == nullptr) { + LOGE("Can't open OEMCrypto library: %s", dlerror()); + return false; + } + LOGI("OEMCrypto library opened."); + + LOAD_SYM(Initialize); + LOAD_SYM(Terminate); + LOAD_SYM(GetBootCertificateChain); + LOAD_SYM(BuildInformation); + + OEMCryptoResult status = Initialize(); + if (status != OEMCrypto_SUCCESS) { + LOGE("OEMCrypto Initialize failed: %d", status); + return false; + } + return true; +} + +OEMCryptoResult OEMCryptoInterface::GetBcc(std::vector& bcc) { + if (handle_ == nullptr) { + return OEMCrypto_ERROR_INIT_FAILED; + } + + bcc.resize(0); + size_t bcc_size = 0; + + std::vector additional_signature; // It should be empty. + size_t additional_signature_size = 0; + OEMCryptoResult result = GetBootCertificateChain(bcc.data(), &bcc_size, + additional_signature.data(), + &additional_signature_size); + LOGI("GetBootCertificateChain first attempt result %d", result); + if (additional_signature_size != 0) { + LOGW( + "The additional_signature_size required by OEMCrypto is %zu, while it " + "is expected to be zero.", + additional_signature_size); + } + + if (result == OEMCrypto_ERROR_SHORT_BUFFER) { + bcc.resize(bcc_size); + additional_signature.resize(additional_signature_size); + result = GetBootCertificateChain(bcc.data(), &bcc_size, + additional_signature.data(), + &additional_signature_size); + LOGI("GetBootCertificateChain second attempt result %d", result); + } + + return result; +} + +OEMCryptoResult OEMCryptoInterface::GetOEMCryptoBuildInfo( + std::string& build_info) { + if (handle_ == nullptr) { + return OEMCrypto_ERROR_INIT_FAILED; + } + + build_info.resize(0); + size_t build_info_size = 0; + + OEMCryptoResult result = BuildInformation(&build_info[0], &build_info_size); + LOGI("BuildInformation first attempt result %d", result); + if (result == OEMCrypto_ERROR_SHORT_BUFFER) { + build_info.resize(build_info_size); + result = BuildInformation(&build_info[0], &build_info_size); + LOGI("BuildInformation second attempt result %d", result); + } + + return result; +} + +} // namespace widevine diff --git a/libwvdrmengine/factory_upload_tool/src/WidevineProvisioner.cpp b/libwvdrmengine/factory_upload_tool/src/WidevineProvisioner.cpp new file mode 100644 index 00000000..5ae48ec3 --- /dev/null +++ b/libwvdrmengine/factory_upload_tool/src/WidevineProvisioner.cpp @@ -0,0 +1,267 @@ +// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine License +// Agreement. + +#include "WidevineProvisioner.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "log.h" +#include "properties.h" + +namespace widevine { + +namespace { +const std::vector> kAuthorizedEekRoots = { + {0x99, 0xB9, 0xEE, 0xDD, 0x5E, 0xE4, 0x52, 0xF6, 0x85, 0xC6, 0x4C, + 0x62, 0xDC, 0x3E, 0x61, 0xAB, 0x57, 0x48, 0x7D, 0x75, 0x37, 0x29, + 0xAD, 0x76, 0x80, 0x32, 0xD2, 0xB3, 0xCB, 0x63, 0x58, 0xD9}, +}; +} // namespace + +WidevineProvisioner::WidevineProvisioner() { + InitializeCryptoInterface(); + assert(crypto_interface_ != nullptr); +} + +bool WidevineProvisioner::GenerateCertificateRequest( + bool testMode, const std::vector& endpointEncCertChain, + std::vector& deviceInfo, std::vector& protectedData) { + if (!GetDeviceInfo(deviceInfo)) { + LOGE("Failed to get device_info."); + return false; + } + + std::vector bcc; + OEMCryptoResult result = crypto_interface_->GetBcc(bcc); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to get BCC."); + return false; + } + + if (!GenerateProtectedData(testMode, endpointEncCertChain, bcc, + protectedData)) { + LOGE("Failed to generate protected data."); + return false; + } + + return true; +} + +bool WidevineProvisioner::GetDeviceInfo(std::vector& device_info) { + auto device_info_map = cppbor::Map(); + device_info_map.add(cppbor::Tstr("type"), cppbor::Tstr("widevine")); + + std::string company_name; + if (!wvcdm::Properties::GetCompanyName(&company_name) || + company_name.empty()) { + LOGE("Failed to get company name."); + return false; + } + device_info_map.add(cppbor::Tstr("manufacturer"), cppbor::Tstr(company_name)); + + std::string model_name; + if (!wvcdm::Properties::GetModelName(&model_name) || model_name.empty()) { + LOGE("Failed to get model name."); + return false; + } + device_info_map.add(cppbor::Tstr("model"), cppbor::Tstr(model_name)); + + std::string arch_name; + if (!wvcdm::Properties::GetArchitectureName(&arch_name) || + arch_name.empty()) { + LOGE("Failed to get architecture name."); + return false; + } + device_info_map.add(cppbor::Tstr("architecture"), cppbor::Tstr(arch_name)); + + std::string device_name; + if (!wvcdm::Properties::GetDeviceName(&device_name) || device_name.empty()) { + LOGE("Failed to get device name."); + return false; + } + device_info_map.add(cppbor::Tstr("device"), cppbor::Tstr(device_name)); + + std::string product_name; + if (!wvcdm::Properties::GetProductName(&product_name) || + product_name.empty()) { + LOGE("Failed to get product name."); + return false; + } + device_info_map.add(cppbor::Tstr("product"), cppbor::Tstr(product_name)); + + std::string build_info; + if (!wvcdm::Properties::GetBuildInfo(&build_info) || build_info.empty()) { + LOGE("Failed to get build info."); + return false; + } + device_info_map.add(cppbor::Tstr("fingerprint"), cppbor::Tstr(build_info)); + + std::string oemcrypto_build_info; + OEMCryptoResult result = + crypto_interface_->GetOEMCryptoBuildInfo(oemcrypto_build_info); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to get oemcrypto build info."); + return false; + } + device_info_map.add(cppbor::Tstr("oemcrypto_build_info"), + cppbor::Tstr(oemcrypto_build_info)); + + device_info = device_info_map.canonicalize().encode(); + return true; +} + +bool WidevineProvisioner::GenerateProtectedData( + bool test_mode, const std::vector& endpoint_encryption_cert_chain, + std::vector bcc, std::vector& protected_data) const { + // Encrypt |signedMac| and |bcc_| with GEEK. + std::vector eek_pub; + std::vector eek_id; + if (!ValidateAndExtractEekPubAndId(test_mode, endpoint_encryption_cert_chain, + &eek_pub, &eek_id)) { + LOGE("Failed to validate and extract the endpoint encryption key."); + return false; + } + + std::vector ephemeralPrivKey(X25519_PRIVATE_KEY_LEN); + std::vector ephemeralPubKey(X25519_PUBLIC_VALUE_LEN); + X25519_keypair(ephemeralPubKey.data(), ephemeralPrivKey.data()); + auto sessionKey = cppcose::x25519_HKDF_DeriveKey( + ephemeralPubKey, ephemeralPrivKey, eek_pub, true /* senderIsA */); + if (!sessionKey) { + LOGE("Failed to derive the session key."); + return false; + } + // Generate 4 bytes of random data as IV + std::vector iv(cppcose::kAesGcmNonceLength); + if (RAND_bytes(iv.data(), iv.size()) != 1) { + LOGE("Failed to generate a random nonce."); + return false; + } + + auto bcc_parse_result = cppbor::parse(bcc.data(), bcc.size()); + cppbor::Item* bcc_item = std::get<0>(bcc_parse_result).get(); + auto coseEncrypted = cppcose::constructCoseEncrypt( + *sessionKey, iv, + cppbor::Array() // payload + .add(cppbor::Array()) // Empty signedMac + .add(std::move(bcc_item->clone())) + .encode(), + {}, // aad + BuildCertReqRecipients(ephemeralPubKey, eek_id)); + if (!coseEncrypted) { + LOGE("Failed to construct a COSE_Encrypt ProtectedData structure"); + return false; + } + + protected_data = coseEncrypted->encode(); + return true; +} + +bool WidevineProvisioner::ValidateAndExtractEekPubAndId( + bool test_mode, const std::vector& endpoint_encryption_cert_chain, + std::vector* eek_pub, std::vector* eek_id) const { + auto parse_result = cppbor::parse(endpoint_encryption_cert_chain); + auto item = std::move(std::get<0>(parse_result)); + if (!item || !item->asArray()) { + LOGE("Error parsing EEK chain: %s", std::get<2>(parse_result).c_str()); + return false; + } + + const cppbor::Array* certArr = item->asArray(); + std::vector lastPubKey; + for (size_t i = 0; i < certArr->size(); ++i) { + auto cosePubKey = cppcose::verifyAndParseCoseSign1( + certArr->get(i)->asArray(), std::move(lastPubKey), {} /* AAD */); + if (!cosePubKey) { + LOGE("Failed to validate EEK chain: %s", + cosePubKey.moveMessage().c_str()); + return false; + } + lastPubKey = *std::move(cosePubKey); + + // In prod mode the first pubkey should match a well-known Google public + // key. + if (!test_mode && i == 0) { + auto parsedPubKey = cppcose::CoseKey::parse(lastPubKey); + if (!parsedPubKey) { + LOGE("%s", parsedPubKey.moveMessage().c_str()); + return false; + } + auto rawPubKey = parsedPubKey->getBstrValue(cppcose::CoseKey::PUBKEY_X); + if (!rawPubKey) { + LOGE("Key is missing required label 'PUBKEY_X'"); + return false; + } + std::vector matcher(rawPubKey->data(), + rawPubKey->data() + rawPubKey->size()); + + if (std::find(std::begin(kAuthorizedEekRoots), + std::end(kAuthorizedEekRoots), + matcher) == std::end(kAuthorizedEekRoots)) { + LOGE("Unrecognized root of EEK chain"); + return false; + } + } + } + + auto eek = cppcose::CoseKey::parseX25519(lastPubKey, true /* requireKid */); + if (!eek) { + LOGE("Failed to get EEK: %s", eek.moveMessage().c_str()); + return false; + } + + *eek_pub = eek->getBstrValue(cppcose::CoseKey::PUBKEY_X).value(); + *eek_id = eek->getBstrValue(cppcose::CoseKey::KEY_ID).value(); + return true; +} + +cppbor::Array WidevineProvisioner::BuildCertReqRecipients( + const std::vector& pubkey, const std::vector& kid) const { + return cppbor::Array() // Array of recipients + .add(cppbor::Array() // Recipient + .add(cppbor::Map() // Protected + .add(cppcose::ALGORITHM, cppcose::ECDH_ES_HKDF_256) + .canonicalize() + .encode()) + .add(cppbor::Map() // Unprotected + .add(cppcose::COSE_KEY, + cppbor::Map() + .add(cppcose::CoseKey::KEY_TYPE, + cppcose::OCTET_KEY_PAIR) + .add(cppcose::CoseKey::CURVE, cppcose::X25519) + .add(cppcose::CoseKey::PUBKEY_X, pubkey) + .canonicalize()) + .add(cppcose::KEY_ID, kid) + .canonicalize()) + .add(cppbor::Null())); // No ciphertext +} + +void WidevineProvisioner::InitializeCryptoInterface() { + std::string oemcrypto_path; + if (!wvcdm::Properties::GetOEMCryptoPath(&oemcrypto_path)) { + LOGE("Failed to get OEMCrypto path."); + } + LOGI("OEMCrypto path is %s", oemcrypto_path.c_str()); + + crypto_interface_ = std::make_unique(); + if (!crypto_interface_->Init(oemcrypto_path)) { + LOGE("Failed to initialize OEMCrypto interface."); + crypto_interface_.reset(nullptr); + } +} + +} // namespace widevine diff --git a/libwvdrmengine/factory_upload_tool/src/WidevineRemotelyProvisionedComponent.cpp b/libwvdrmengine/factory_upload_tool/src/WidevineRemotelyProvisionedComponent.cpp new file mode 100644 index 00000000..6c9fe9c3 --- /dev/null +++ b/libwvdrmengine/factory_upload_tool/src/WidevineRemotelyProvisionedComponent.cpp @@ -0,0 +1,115 @@ +/* + * 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. + */ + +#include "WidevineRemotelyProvisionedComponent.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace aidl::android::hardware::security::keymint { + +using ::std::string; +using ::std::unique_ptr; +using ::std::vector; +using bytevec = ::std::vector; +using ScopedAStatus = ::ndk::ScopedAStatus; + +namespace { + +constexpr auto STATUS_FAILED = IRemotelyProvisionedComponent::STATUS_FAILED; + +struct AStatusDeleter { + void operator()(AStatus* p) { AStatus_delete(p); } +}; + +class Status { + public: + Status() : status_(AStatus_newOk()) {} + Status(int32_t errCode, const std::string& errMsg) + : status_(AStatus_fromServiceSpecificErrorWithMessage(errCode, + errMsg.c_str())) {} + explicit Status(const std::string& errMsg) + : status_(AStatus_fromServiceSpecificErrorWithMessage(STATUS_FAILED, + errMsg.c_str())) {} + explicit Status(AStatus* status) + : status_(status ? status : AStatus_newOk()) {} + + Status(Status&&) = default; + Status(const Status&) = delete; + + operator ::ndk::ScopedAStatus() && { // NOLINT(google-explicit-constructor) + return ndk::ScopedAStatus(status_.release()); + } + + bool isOk() const { return AStatus_isOk(status_.get()); } + + const char* getMessage() const { return AStatus_getMessage(status_.get()); } + + private: + std::unique_ptr status_; +}; + +} // namespace + +ScopedAStatus WidevineRemotelyProvisionedComponent::getHardwareInfo( + RpcHardwareInfo* info) { + info->versionNumber = 1; + info->rpcAuthorName = "Google"; + info->supportedEekCurve = RpcHardwareInfo::CURVE_25519; + return ScopedAStatus::ok(); +} + +ScopedAStatus WidevineRemotelyProvisionedComponent::generateEcdsaP256KeyPair( + bool testMode, MacedPublicKey* macedPublicKey, bytevec* privateKeyHandle) { + return Status("Invalid operation."); +} + +ScopedAStatus WidevineRemotelyProvisionedComponent::generateCertificateRequest( + bool testMode, const vector& keysToSign, + const bytevec& endpointEncCertChain, const bytevec& challenge, + DeviceInfo* deviceInfo, ProtectedData* protectedData, + bytevec* keysToSignMac) { + if (!keysToSign.empty()) { + return Status("Invalid operation: Keys to sign must be empty."); + } + if (deviceInfo == nullptr) { + return Status("Parameter deviceInfo must not be null."); + } + if (protectedData == nullptr) { + return Status("Parameter protectedData must not be null."); + } + + if (provisioner_ == nullptr) { + provisioner_ = std::make_unique(); + } + if (!provisioner_->GenerateCertificateRequest(testMode, endpointEncCertChain, + deviceInfo->deviceInfo, + protectedData->protectedData)) { + return Status("Failed to generate certificate request."); + } + return ScopedAStatus::ok(); +} + +} // namespace aidl::android::hardware::security::keymint diff --git a/libwvdrmengine/factory_upload_tool/src/log.cpp b/libwvdrmengine/factory_upload_tool/src/log.cpp new file mode 100644 index 00000000..5d018870 --- /dev/null +++ b/libwvdrmengine/factory_upload_tool/src/log.cpp @@ -0,0 +1,91 @@ +// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine Master +// License Agreement. +// +// Log - implemented using the standard Android logging mechanism + +/* + * Qutoing from system/core/include/log/log.h: + * Normally we strip ALOGV (VERBOSE messages) from release builds. + * You can modify this (for example with "#define LOG_NDEBUG 0" + * at the top of your source file) to change that behavior. + */ +#ifndef LOG_NDEBUG +# ifdef NDEBUG +# define LOG_NDEBUG 1 +# else +# define LOG_NDEBUG 0 +# endif +#endif + +#define LOG_TAG "Widevine" +#define LOG_BUF_SIZE 1024 + +#include "log.h" + +#include +#include +#include + +#include + +/* + * Uncomment the line below if you want to have the LOGV messages to print + * IMPORTANT : this will affect all of CDM + */ + +// #define LOG_NDEBUG 0 + +namespace wvutil { + +LogPriority g_cutoff = CDM_LOG_INFO; + +void InitLogging() {} + +void Log(const char* file, const char* function, int line, LogPriority level, + const char* format, ...) { + if (level > g_cutoff) return; + + const char* filename = strrchr(file, '/'); + filename = filename == nullptr ? file : filename + 1; + + char buf[LOG_BUF_SIZE]; + int len = + snprintf(buf, LOG_BUF_SIZE, "[%s(%d):%s] ", filename, line, function); + if (len < 0) len = 0; + if (static_cast(len) < sizeof(buf)) { + va_list ap; + va_start(ap, format); + vsnprintf(buf + len, LOG_BUF_SIZE - len, format, ap); + va_end(ap); + } + + android_LogPriority prio = ANDROID_LOG_VERBOSE; + + switch (level) { + case CDM_LOG_ERROR: + prio = ANDROID_LOG_ERROR; + break; + case CDM_LOG_WARN: + prio = ANDROID_LOG_WARN; + break; + case CDM_LOG_INFO: + prio = ANDROID_LOG_INFO; + break; + case CDM_LOG_DEBUG: + prio = ANDROID_LOG_DEBUG; + break; +#if LOG_NDEBUG + case CDM_LOG_VERBOSE: + return; +#else + case CDM_LOG_VERBOSE: + prio = ANDROID_LOG_VERBOSE; + break; +#endif + } + + __android_log_write(prio, LOG_TAG, buf); +} + +} // namespace wvutil diff --git a/libwvdrmengine/factory_upload_tool/src/properties_android.cpp b/libwvdrmengine/factory_upload_tool/src/properties_android.cpp new file mode 100644 index 00000000..5b47b556 --- /dev/null +++ b/libwvdrmengine/factory_upload_tool/src/properties_android.cpp @@ -0,0 +1,93 @@ +// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine License +// Agreement. + +#include +#include + +#include +#include + +#include "log.h" +#include "properties.h" + +namespace { + +bool GetAndroidProperty(const char* key, std::string* value) { + if (!key) { + LOGW("GetAndroidProperty: Invalid property key parameter"); + return false; + } + + if (!value) { + LOGW("GetAndroidProperty: Invalid property value parameter"); + return false; + } + + *value = android::base::GetProperty(key, ""); + + return value->size() > 0; +} + +} // namespace + +namespace wvcdm { + +bool Properties::GetCompanyName(std::string* company_name) { + if (!company_name) { + LOGW("Properties::GetCompanyName: Invalid parameter"); + return false; + } + return GetAndroidProperty("ro.product.manufacturer", company_name); +} + +bool Properties::GetModelName(std::string* model_name) { + if (!model_name) { + LOGW("Properties::GetModelName: Invalid parameter"); + return false; + } + return GetAndroidProperty("ro.product.model", model_name); +} + +bool Properties::GetArchitectureName(std::string* arch_name) { + if (!arch_name) { + LOGW("Properties::GetArchitectureName: Invalid parameter"); + return false; + } + return GetAndroidProperty("ro.product.cpu.abi", arch_name); +} + +bool Properties::GetDeviceName(std::string* device_name) { + if (!device_name) { + LOGW("Properties::GetDeviceName: Invalid parameter"); + return false; + } + return GetAndroidProperty("ro.product.device", device_name); +} + +bool Properties::GetProductName(std::string* product_name) { + if (!product_name) { + LOGW("Properties::GetProductName: Invalid parameter"); + return false; + } + return GetAndroidProperty("ro.product.name", product_name); +} + +bool Properties::GetBuildInfo(std::string* build_info) { + if (!build_info) { + LOGW("Properties::GetBuildInfo: Invalid parameter"); + return false; + } + return GetAndroidProperty("ro.build.fingerprint", build_info); +} + +bool Properties::GetOEMCryptoPath(std::string* library_name) { + if (!library_name) { + LOGW("Properties::GetOEMCryptoPath: Invalid parameter"); + return false; + } + *library_name = "liboemcrypto.so"; + return true; +} + +} // namespace wvcdm