wv_factory_extraction_tool: extract provision 4.0 csr

Bug: 231677822
Test: adb shell wv_factory_extraction_tool csr
Change-Id: I9f21514b027261f1d69c24a4d2f54051ccaac9a5
This commit is contained in:
Robert Shih
2022-05-06 01:38:52 -07:00
parent 56d976fec6
commit 05878ffbe1
9 changed files with 923 additions and 0 deletions

View File

@@ -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 <dlfcn.h>
#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<name##_t>(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<File> FileSystem::Open(const std::string&, int) {
return std::unique_ptr<File>(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<std::string>*) {
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<uint8_t>& bcc) {
if (handle_ == nullptr) {
return OEMCrypto_ERROR_INIT_FAILED;
}
bcc.resize(0);
size_t bcc_size = 0;
std::vector<uint8_t> 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

View File

@@ -0,0 +1,276 @@
// 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 <cppbor.h>
#include <cppbor_parse.h>
#include <keymaster/cppcose/cppcose.h>
#include <openssl/bn.h>
#include <openssl/curve25519.h>
#include <openssl/ec.h>
#include <openssl/evp.h>
#include <openssl/hkdf.h>
#include <openssl/rand.h>
#include <string.h>
#include <memory>
#include <string>
#include <utility>
#include "log.h"
#include "properties.h"
namespace widevine {
namespace {
const std::vector<std::vector<uint8_t>> 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);
}
std::vector<uint8_t> WidevineProvisioner::GetBcc() {
std::vector<uint8_t> bcc;
OEMCryptoResult result = crypto_interface_->GetBcc(bcc);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to get BCC.");
}
return bcc;
}
bool WidevineProvisioner::GenerateCertificateRequest(
bool testMode, const std::vector<uint8_t>& endpointEncCertChain,
std::vector<uint8_t>& deviceInfo, std::vector<uint8_t>& protectedData) {
if (!GetDeviceInfo(deviceInfo)) {
LOGE("Failed to get device_info.");
return false;
}
std::vector<uint8_t> 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<uint8_t>& 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<uint8_t>& endpoint_encryption_cert_chain,
std::vector<uint8_t> bcc, std::vector<uint8_t>& protected_data) const {
// Encrypt |signedMac| and |bcc_| with GEEK.
std::vector<uint8_t> eek_pub;
std::vector<uint8_t> 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<uint8_t> ephemeralPrivKey(X25519_PRIVATE_KEY_LEN);
std::vector<uint8_t> 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<uint8_t> 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<uint8_t>& endpoint_encryption_cert_chain,
std::vector<uint8_t>* eek_pub, std::vector<uint8_t>* 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<uint8_t> 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<uint8_t> 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<uint8_t>& pubkey, const std::vector<uint8_t>& 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<OEMCryptoInterface>();
if (!crypto_interface_->Init(oemcrypto_path)) {
LOGE("Failed to initialize OEMCrypto interface.");
crypto_interface_.reset(nullptr);
}
}
} // namespace widevine

View File

@@ -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 <stdarg.h>
#include <stdio.h>
#include <utils/Log.h>
#include <string>
/*
* 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<unsigned int>(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

View File

@@ -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 <android-base/properties.h>
#include <unistd.h>
#include <sstream>
#include <string>
#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