Merge "wv_factory_extraction_tool: extract provision 4.0 csr" into tm-d1-dev am: 63ad411c0e
Original change: https://googleplex-android-review.googlesource.com/c/platform/vendor/widevine/+/18197386 Change-Id: I79b5410b3ca4afa015c4f4fef4c63f36c470d2f4 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
42
libwvdrmengine/tools/factory_upload_tool/Android.bp
Normal file
42
libwvdrmengine/tools/factory_upload_tool/Android.bp
Normal file
@@ -0,0 +1,42 @@
|
||||
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: "wv_factory_extraction_tool",
|
||||
relative_install_path: "hw",
|
||||
vendor: true,
|
||||
cflags: [
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
],
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"libcppbor_external",
|
||||
"libcppcose_rkp",
|
||||
"libcrypto",
|
||||
"liblog",
|
||||
"libutils",
|
||||
],
|
||||
srcs: [
|
||||
"cli.cpp",
|
||||
"src/log.cpp",
|
||||
"src/properties_android.cpp",
|
||||
"src/WidevineProvisioner.cpp",
|
||||
"src/WidevineOemcryptoInterface.cpp",
|
||||
],
|
||||
include_dirs: [
|
||||
"vendor/widevine/libwvdrmengine/oemcrypto/include",
|
||||
"vendor/widevine/libwvdrmengine/cdm/util/include",
|
||||
],
|
||||
local_include_dirs: [
|
||||
"include",
|
||||
],
|
||||
}
|
||||
|
||||
139
libwvdrmengine/tools/factory_upload_tool/cli.cpp
Normal file
139
libwvdrmengine/tools/factory_upload_tool/cli.cpp
Normal file
@@ -0,0 +1,139 @@
|
||||
//
|
||||
// Copyright 2022 Google LLC. All Rights Reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine License
|
||||
// Agreement.
|
||||
//
|
||||
|
||||
#define LOG_TAG "wv_factory_extraction_tool"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <sys/random.h>
|
||||
|
||||
#include "WidevineProvisioner.h"
|
||||
|
||||
constexpr size_t kChallengeSize = 16;
|
||||
|
||||
// The Google root key for the Endpoint Encryption Key chain, encoded as COSE_Sign1
|
||||
inline constexpr uint8_t kCoseEncodedRootCert[] = {
|
||||
0x84, 0x43, 0xa1, 0x01, 0x27, 0xa0, 0x58, 0x2a, 0xa4, 0x01, 0x01, 0x03, 0x27, 0x20, 0x06,
|
||||
0x21, 0x58, 0x20, 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, 0x58, 0x40, 0x1e, 0x22, 0x08, 0x4b, 0xa4, 0xb7, 0xa4, 0xc8,
|
||||
0xd7, 0x4e, 0x03, 0x0e, 0xfe, 0xb8, 0xaf, 0x14, 0x4c, 0xa7, 0x3b, 0x6f, 0xa5, 0xcd, 0xdc,
|
||||
0xda, 0x79, 0xc6, 0x2b, 0x64, 0xfe, 0x99, 0x39, 0xaf, 0x76, 0xe7, 0x80, 0xfa, 0x66, 0x00,
|
||||
0x85, 0x0d, 0x07, 0x98, 0x2a, 0xac, 0x91, 0x5c, 0xa7, 0x25, 0x14, 0x49, 0x06, 0x34, 0x75,
|
||||
0xca, 0x8a, 0x27, 0x7a, 0xd9, 0xe3, 0x5a, 0x49, 0xeb, 0x02, 0x03};
|
||||
|
||||
// The Google Endpoint Encryption Key certificate, encoded as COSE_Sign1
|
||||
inline constexpr uint8_t kCoseEncodedGeekCert[] = {
|
||||
0x84, 0x43, 0xa1, 0x01, 0x27, 0xa0, 0x58, 0x4e, 0xa5, 0x01, 0x01, 0x02, 0x58, 0x20,
|
||||
0xd0, 0xae, 0xc1, 0x15, 0xca, 0x2a, 0xcf, 0x73, 0xae, 0x6b, 0xcc, 0xcb, 0xd1, 0x96,
|
||||
0x1d, 0x65, 0xe8, 0xb1, 0xdd, 0xd7, 0x4a, 0x1a, 0x37, 0xb9, 0x43, 0x3a, 0x97, 0xd5,
|
||||
0x99, 0xdf, 0x98, 0x08, 0x03, 0x38, 0x18, 0x20, 0x04, 0x21, 0x58, 0x20, 0xbe, 0x85,
|
||||
0xe7, 0x46, 0xc4, 0xa3, 0x42, 0x5a, 0x40, 0xd9, 0x36, 0x3a, 0xa6, 0x15, 0xd0, 0x2c,
|
||||
0x58, 0x7e, 0x3d, 0xdc, 0x33, 0x02, 0x32, 0xd2, 0xfc, 0x5e, 0x1e, 0x87, 0x25, 0x5f,
|
||||
0x72, 0x60, 0x58, 0x40, 0x9b, 0xcf, 0x90, 0xe2, 0x2e, 0x4b, 0xab, 0xd1, 0x18, 0xb1,
|
||||
0x0e, 0x8e, 0x5d, 0x20, 0x27, 0x4b, 0x84, 0x58, 0xfe, 0xfc, 0x32, 0x90, 0x7e, 0x72,
|
||||
0x05, 0x83, 0xbc, 0xd7, 0x82, 0xbe, 0xfa, 0x64, 0x78, 0x2d, 0x54, 0x10, 0x4b, 0xc0,
|
||||
0x31, 0xbf, 0x6b, 0xe8, 0x1e, 0x35, 0xe2, 0xf0, 0x2d, 0xce, 0x6c, 0x2f, 0x4f, 0xf2,
|
||||
0xf5, 0x4f, 0xa5, 0xd4, 0x83, 0xad, 0x96, 0xa2, 0xf1, 0x87, 0x58, 0x04};
|
||||
|
||||
std::vector<uint8_t> generateChallenge() {
|
||||
std::vector<uint8_t> challenge(kChallengeSize);
|
||||
|
||||
ssize_t bytesRemaining = static_cast<ssize_t>(challenge.size());
|
||||
uint8_t* writePtr = challenge.data();
|
||||
while (bytesRemaining > 0) {
|
||||
int bytesRead = getrandom(writePtr, bytesRemaining, /*flags=*/0);
|
||||
if (bytesRead < 0) {
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
} else {
|
||||
std::cerr << errno << ": " << strerror(errno) << std::endl;
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
bytesRemaining -= bytesRead;
|
||||
writePtr += bytesRead;
|
||||
}
|
||||
|
||||
return challenge;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> getEekChain() {
|
||||
cppbor::Array chain;
|
||||
chain.add(cppbor::EncodedItem(
|
||||
std::vector<uint8_t>(std::begin(kCoseEncodedRootCert), std::end(kCoseEncodedRootCert))));
|
||||
chain.add(cppbor::EncodedItem(
|
||||
std::vector<uint8_t>(std::begin(kCoseEncodedGeekCert), std::end(kCoseEncodedGeekCert))));
|
||||
return chain.encode();
|
||||
}
|
||||
|
||||
cppbor::Array composeCertificateRequest(
|
||||
const std::vector<uint8_t>& protectedData,
|
||||
const std::vector<uint8_t>& verifiedDeviceInfo,
|
||||
const std::vector<uint8_t>& challenge) {
|
||||
cppbor::Array macedKeysToSign = cppbor::Array()
|
||||
.add(std::vector<uint8_t>(0)) // empty protected headers as bstr
|
||||
.add(cppbor::Map()) // empty unprotected headers
|
||||
.add(cppbor::Null()) // nil for the payload
|
||||
.add(std::vector<uint8_t>(0)); // MAC as returned from the HAL
|
||||
|
||||
cppbor::Array deviceInfo =
|
||||
cppbor::Array().add(cppbor::EncodedItem(verifiedDeviceInfo)).add(cppbor::Map()); // Empty device info
|
||||
|
||||
cppbor::Array certificateRequest = cppbor::Array()
|
||||
.add(std::move(deviceInfo))
|
||||
.add(challenge)
|
||||
.add(cppbor::EncodedItem(protectedData))
|
||||
.add(std::move(macedKeysToSign));
|
||||
return certificateRequest;
|
||||
}
|
||||
|
||||
cppbor::Array getCsr(widevine::WidevineProvisioner& provisioner) {
|
||||
const std::vector<uint8_t> challenge = generateChallenge();
|
||||
std::vector<uint8_t> verifiedDeviceInfo;
|
||||
std::vector<uint8_t> protectedData;
|
||||
|
||||
if (!provisioner.GenerateCertificateRequest(false, getEekChain(),
|
||||
verifiedDeviceInfo,
|
||||
protectedData)) {
|
||||
std::cerr << "Failed to generate certificate request." << std::endl;
|
||||
exit(-1);
|
||||
}
|
||||
auto csr =
|
||||
composeCertificateRequest(protectedData, verifiedDeviceInfo, challenge);
|
||||
return csr;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (argc<2) {
|
||||
fprintf(stderr,"%s <bcc|device_info>\n",argv[0]);
|
||||
return 0;
|
||||
}
|
||||
widevine::WidevineProvisioner provisioner;
|
||||
if (!std::strcmp(argv[1],"bcc")) {
|
||||
auto bcc = provisioner.GetBcc();
|
||||
fwrite(bcc.data(), 1 , bcc.size(), stdout);
|
||||
fflush(stdout);
|
||||
} else if (!std::strcmp(argv[1],"device_info")) {
|
||||
std::vector<uint8_t> deviceInfo;
|
||||
if (provisioner.GetDeviceInfo(deviceInfo)) {
|
||||
fwrite(deviceInfo.data(), 1 , deviceInfo.size(), stdout);
|
||||
fflush(stdout);
|
||||
}
|
||||
} else if (!std::strcmp(argv[1],"csr")) {
|
||||
auto csr = getCsr(provisioner);
|
||||
auto bytes = csr.encode();
|
||||
std::copy(bytes.begin(), bytes.end(), std::ostream_iterator<char>(std::cout));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -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 <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#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<uint8_t>& 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_
|
||||
@@ -0,0 +1,53 @@
|
||||
// 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 <cppbor.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "WidevineOemcryptoInterface.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
class WidevineProvisioner {
|
||||
public:
|
||||
WidevineProvisioner();
|
||||
WidevineProvisioner(const WidevineProvisioner&) = delete;
|
||||
WidevineProvisioner& operator=(const WidevineProvisioner&) = delete;
|
||||
virtual ~WidevineProvisioner() = default;
|
||||
|
||||
std::vector<uint8_t> GetBcc();
|
||||
|
||||
bool GenerateCertificateRequest(
|
||||
bool testMode, const std::vector<uint8_t>& endpointEncCertChain,
|
||||
std::vector<uint8_t>& deviceInfo, std::vector<uint8_t>& protectedData);
|
||||
|
||||
bool GetDeviceInfo(std::vector<uint8_t>& device_info);
|
||||
|
||||
private:
|
||||
bool 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;
|
||||
bool 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;
|
||||
cppbor::Array BuildCertReqRecipients(const std::vector<uint8_t>& pubkey,
|
||||
const std::vector<uint8_t>& kid) const;
|
||||
void InitializeCryptoInterface();
|
||||
|
||||
std::unique_ptr<OEMCryptoInterface> crypto_interface_;
|
||||
};
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
#endif // WIDEVINE_PROVISIONER_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 <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
#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_
|
||||
@@ -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
|
||||
@@ -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
|
||||
91
libwvdrmengine/tools/factory_upload_tool/src/log.cpp
Normal file
91
libwvdrmengine/tools/factory_upload_tool/src/log.cpp
Normal 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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user