Use local provisioning server am: 44ba42f5cc

Original change: https://googleplex-android-review.googlesource.com/c/platform/vendor/widevine/+/16058116

Change-Id: Ib10701963fc4ba89d37f49b95186640244576305
This commit is contained in:
Fred Gylys-Colwell
2021-10-28 17:02:10 +00:00
committed by Automerger Merge Worker
17 changed files with 527 additions and 98 deletions

View File

@@ -287,6 +287,19 @@ class CryptoSession {
virtual CdmResponseType GetProvisioningMethod(
SecurityLevel requested_security_level, CdmClientTokenType* token_type);
// OTA Provisioning
// TODO(sigquit): include rest of http://go/wvgerrit/126004
// Generates an OTA provisioning request.
// This should only be called by an instance of OtaKeyboxProvisioner.
virtual CdmResponseType PrepareOtaProvisioningRequest(bool use_test_key,
std::string* request);
// Loads an OTA provisioning response.
// This should only be called by an instance of OtaKeyboxProvisioner.
virtual CdmResponseType LoadOtaProvisioning(bool use_test_key,
const std::string& response);
protected:
// Creates an instance of CryptoSession with the given |crypto_metrics|.
// |crypto_metrics| is owned by the caller, must NOT be null, and must
@@ -294,6 +307,10 @@ class CryptoSession {
explicit CryptoSession(metrics::CryptoMetrics* crypto_metrics);
int session_count() const { return session_count_; }
bool initialized() const { return initialized_; }
void OverrideInitializedForTesting(bool initialized) {
initialized_ = initialized;
}
private:
friend class CryptoSessionForTest;
@@ -434,6 +451,7 @@ class CryptoSession {
static bool initialized_;
static int session_count_;
static int termination_counter_;
static bool needs_keybox_provisioning_;
enum CachedBooleanProperty {
// Property has not yet been checked/cached.

View File

@@ -9,6 +9,12 @@
#include "wv_cdm_types.h"
namespace wvcdm {
// Initialize OEMCrypto, then check the keybox and see if it is valid. If not,
// and OTA provisioning is supported, set needs_keybox_provisioning to true.
// If the keybox is not valid and OTA provisioning is not supported, set
// needs_keybox_provisioning to false and use L3 only.
OEMCryptoResult OEMCrypto_InitializeAndCheckKeybox(
bool* needs_keybox_provisioning);
// This attempts to open a session at the desired security level.
// If one level is not available, the other will be used instead.
@@ -17,7 +23,6 @@ OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION* session,
OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t* keybox,
size_t keyBoxLength,
SecurityLevel level);
OEMCryptoResult OEMCrypto_IsKeyboxOrOEMCertValid(SecurityLevel level);
OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID, size_t* idLength,
SecurityLevel level);
OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData, size_t* keyDataLength,

View File

@@ -85,7 +85,7 @@ constexpr size_t kMaxSubsampleRegionSizes[] = {
static_assert(ArraySize(kMaxSubsampleRegionSizes) ==
RESOURCE_RATING_TIER_MAX - RESOURCE_RATING_TIER_MIN + 1,
"The kMaxSubsampleRegionSizes table needs to be updated to "
"reflect the supported range of resource rating tiers.");
"reflect the supported range of resource rating tiers");
constexpr size_t kDefaultMaxSubsampleRegionSize = kMaxSubsampleRegionSizes[0];
@@ -171,6 +171,7 @@ size_t GenericEncryptionBlockSize(CdmEncryptionAlgorithm algorithm) {
shared_mutex CryptoSession::static_field_mutex_;
shared_mutex CryptoSession::oem_crypto_mutex_;
bool CryptoSession::initialized_ = false;
bool CryptoSession::needs_keybox_provisioning_ = false;
int CryptoSession::session_count_ = 0;
int CryptoSession::termination_counter_ = 0;
std::unique_ptr<UsageTableHeader> CryptoSession::usage_table_header_l1_;
@@ -321,8 +322,9 @@ void CryptoSession::Init() {
sandbox_id.length());
metrics_->oemcrypto_set_sandbox_.Record(sandbox_id);
}
M_TIME(sts = OEMCrypto_Initialize(), metrics_, oemcrypto_initialize_,
sts);
M_TIME(sts = OEMCrypto_InitializeAndCheckKeybox(
&needs_keybox_provisioning_),
metrics_, oemcrypto_initialize_, sts);
});
if (OEMCrypto_SUCCESS != sts) {
LOGE("OEMCrypto_Initialize failed: status = %d", static_cast<int>(sts));
@@ -357,6 +359,10 @@ void CryptoSession::Init() {
: kStringNotAvailable;
LOGD("OEMCrypto version (L3 security level): %s.%s", api_version.c_str(),
api_minor_version.c_str());
if (needs_keybox_provisioning_) {
LOGE("OEMCrypto needs provisioning");
// TODO(fredgc,sigquit,rfrias): handle provisioning.
}
}
}
@@ -1440,7 +1446,7 @@ size_t CryptoSession::GetMaxSubsampleRegionSize() {
// If something went wrong, use the default.
if (max_subsample_region_size_ == 0) {
LOGW("Unable to get maximum subsample region size. Defaulting to %zu.",
LOGW("Unable to get maximum subsample region size. Defaulting to %zu",
kDefaultMaxSubsampleRegionSize);
max_subsample_region_size_ = kDefaultMaxSubsampleRegionSize;
}
@@ -2994,7 +3000,7 @@ OEMCryptoResult CryptoSession::LegacyDecryptInChunks(
static_assert(sizeof(fake_sample.iv) == kAes128BlockSize,
"The size of an AES-128 block and the size of an AES-128 "
"IV have become misaligned.");
"IV have become misaligned");
memcpy(fake_sample.iv, block_end - kAes128BlockSize, kAes128BlockSize);
}
}
@@ -3009,6 +3015,33 @@ OEMCryptoResult CryptoSession::LegacyDecryptInChunks(
return sts;
}
// TODO(sigquit): include rest of http://go/wvgerrit/126004
CdmResponseType CryptoSession::PrepareOtaProvisioningRequest(
bool use_test_key, std::string* request) {
RETURN_IF_NULL(request, PARAMETER_NULL);
size_t buffer_length = 0;
OEMCryptoResult status =
OEMCrypto_GenerateOTARequest(nullptr, &buffer_length, use_test_key);
if (status != OEMCrypto_ERROR_SHORT_BUFFER)
return MapOEMCryptoResult(status, UNKNOWN_ERROR,
"PrepareOtaProvisioningRequest");
std::string temp_buffer(buffer_length, '\0');
uint8_t* buf = reinterpret_cast<uint8_t*>(&temp_buffer[0]);
status = OEMCrypto_GenerateOTARequest(buf, &buffer_length, use_test_key);
if (OEMCrypto_SUCCESS == status) request->assign(temp_buffer);
return MapOEMCryptoResult(status, UNKNOWN_ERROR,
"PrepareOtaProvisioningRequest");
}
CdmResponseType CryptoSession::LoadOtaProvisioning(
bool use_test_key, const std::string& response) {
OEMCryptoResult status = OEMCrypto_ProcessOTAKeybox(
reinterpret_cast<const uint8_t*>(response.data()), response.size(),
use_test_key);
return MapOEMCryptoResult(status, UNKNOWN_ERROR, "LoadOtaProvisioning");
}
template <class Func>
auto CryptoSession::WithStaticFieldWriteLock(const char* tag, Func body)
-> decltype(body()) {

View File

@@ -318,7 +318,12 @@ typedef OEMCryptoResult (*L1_LoadProvisioning_t)(
size_t signature_length, uint8_t* wrapped_private_key,
size_t* wrapped_private_key_length);
typedef uint32_t (*L1_MinorAPIVersion_t)();
typedef OEMCryptoResult (*L1_GenerateOTARequest_t)(uint8_t* buffer,
size_t* buffer_length,
bool use_test_key);
typedef OEMCryptoResult (*L1_ProcessOTAKeybox_t)(const uint8_t* buffer,
size_t buffer_length,
bool use_test_key);
struct FunctionPointers {
wvcdm::CdmSecurityLevel security_level;
uint32_t version;
@@ -415,6 +420,8 @@ struct FunctionPointers {
L1_SelectKey_V13_t SelectKey_V13;
L1_LoadTestKeybox_V13_t LoadTestKeybox_V13;
L1_RefreshKeys_V14_t RefreshKeys_V14;
L1_GenerateOTARequest_t GenerateOTARequest;
L1_ProcessOTAKeybox_t ProcessOTAKeybox;
};
size_t GetOffset(const std::string& message, const std::string& field) {
@@ -643,7 +650,7 @@ struct LevelSession {
if ((level1_.version >= min) && (level1_.version <= max)) { \
level1_.Name = (L1_##Name##_t)dlsym(level1_library_, QUOTE(Function)); \
if (!level1_.Name) { \
LOGW("Could not load L1 %s. Falling Back to L3.", QUOTE(Function)); \
LOGW("Could not load L1 %s. Falling back to L3.", QUOTE(Function)); \
if (level1_.Terminate) level1_.Terminate(); \
return false; \
} \
@@ -778,7 +785,7 @@ class Adapter {
}
OEMCryptoResult st = level1_.Initialize();
if (st != OEMCrypto_SUCCESS) {
LOGW("Could not initialize L1. Falling Back to L3.");
LOGW("Could not initialize L1. Falling back to L3.");
metrics->OemCryptoDynamicAdapterMetrics::SetInitializationMode(
wvcdm::metrics::
OEMCrypto_INITIALIZED_USING_L3_COULD_NOT_INITIALIZE_L1);
@@ -790,7 +797,7 @@ class Adapter {
metrics->SetL1MinApiVersion(kMinimumVersion);
if (level1_.version < kMinimumVersion) {
LOGW("liboemcrypto.so is version %d, not %d. Falling Back to L3.",
LOGW("liboemcrypto.so is version %d, not %d. Falling back to L3.",
level1_.version, kMinimumVersion);
metrics->OemCryptoDynamicAdapterMetrics::SetInitializationMode(
wvcdm::metrics::OEMCrypto_INITIALIZED_USING_L3_WRONG_L1_VERSION);
@@ -887,6 +894,8 @@ class Adapter {
LOOKUP_ALL(16, MaximumUsageTableHeaderSize, OEMCrypto_MaximumUsageTableHeaderSize);
LOOKUP_ALL(16, LoadProvisioning, OEMCrypto_LoadProvisioning);
LOOKUP_ALL(16, MinorAPIVersion, OEMCrypto_MinorAPIVersion);
LOOKUP_ALL(16, GenerateOTARequest, OEMCrypto_GenerateOTARequest);
LOOKUP_ALL(16, ProcessOTAKeybox, OEMCrypto_ProcessOTAKeybox);
// clang-format on
// There was a mistake in version 16.3 of the header that did not rename
@@ -915,81 +924,6 @@ class Adapter {
}
}
// TODO(119830252): make the code below available to a static adapter.
// Check if the keybox or oem certificate is valid, if so, we are finished
// with initialization.
OEMCryptoResult root_valid = level1_.IsKeyboxOrOEMCertValid();
OEMCrypto_ProvisioningMethod provisioning_method =
level1_.GetProvisioningMethod();
if (root_valid == OEMCrypto_SUCCESS) {
// The keybox or certificate is valid -- that means initialization is done
// and we only have save some metrics and return.
metrics->OemCryptoDynamicAdapterMetrics::SetInitializationMode(
(provisioning_method == OEMCrypto_Keybox)
? wvcdm::metrics::OEMCrypto_INITIALIZED_USING_L1_WITH_KEYBOX
: wvcdm::metrics::
OEMCrypto_INITIALIZED_USING_L1_WITH_PROVISIONING_3_0);
return true;
}
// At this point, the keybox or cert is not valid. We look on the file
// system for one. If it is there we try to install it.
wvcdm::FileSystem file_system;
std::string filename;
if (!wvcdm::Properties::GetFactoryKeyboxPath(&filename)) {
// No keybox or cert file found. Give up.
LOGW("Bad Level 1 Root of Trust. Falling Back to L3.");
level1_.Terminate();
metrics->OemCryptoDynamicAdapterMetrics::SetInitializationMode(
wvcdm::metrics::OEMCrypto_INITIALIZED_USING_L3_BAD_KEYBOX);
return false;
}
ssize_t size = file_system.FileSize(filename);
if (size <= 0) {
// A keybox or cert file was found, but it has size 0. Give up.
LOGW("Could not find %s. Falling Back to L3.", filename.c_str());
level1_.Terminate();
metrics->OemCryptoDynamicAdapterMetrics::SetInitializationMode(
wvcdm::metrics::
OEMCrypto_INITIALIZED_USING_L3_COULD_NOT_OPEN_FACTORY_KEYBOX);
return false;
}
auto file = file_system.Open(filename, file_system.kReadOnly);
if (!file) {
// A keybox or cert file was found, but can't open it. Give up.
LOGW("Could not open %s. Falling Back to L3.", filename.c_str());
level1_.Terminate();
metrics->OemCryptoDynamicAdapterMetrics::SetInitializationMode(
wvcdm::metrics::
OEMCrypto_INITIALIZED_USING_L3_COULD_NOT_OPEN_FACTORY_KEYBOX);
return false;
}
std::vector<uint8_t> root_key(size);
ssize_t size_read = file->Read(reinterpret_cast<char*>(&root_key[0]), size);
if (level1_.InstallKeyboxOrOEMCert(&root_key[0], size_read) !=
OEMCrypto_SUCCESS) {
// A keybox or cert file was read, but I could not install it. Give up.
LOGE("Could NOT install root key from %s. Falling Back to L3.",
filename.c_str());
level1_.Terminate();
metrics->OemCryptoDynamicAdapterMetrics::SetInitializationMode(
wvcdm::metrics::
OEMCrypto_INITIALIZED_USING_L3_COULD_NOT_INSTALL_KEYBOX);
return false;
}
if (level1_.IsKeyboxOrOEMCertValid() != OEMCrypto_SUCCESS) {
// A keybox or cert file was read and installed, but it is still not
// valid. Give up.
LOGE("Installed bad key from %s. Falling Back to L3.", filename.c_str());
level1_.Terminate();
metrics->OemCryptoDynamicAdapterMetrics::SetInitializationMode(
wvcdm::metrics::
OEMCrypto_INITIALIZED_USING_L3_COULD_NOT_INSTALL_KEYBOX);
return false;
}
// A valid keybox or cert file was read and installed. Yay! return success.
LOGI("Installed root key from %s", filename.c_str());
metrics->OemCryptoDynamicAdapterMetrics::SetInitializationMode(
wvcdm::metrics::OEMCrypto_INITIALIZED_USING_L1_INSTALLED_KEYBOX);
return true;
}
@@ -1133,6 +1067,77 @@ class Adapter {
return result;
}
// Check the L1 keybox or cert. If it is valid, return success. If not, try to
// install one. If one is not available, but OTA provisioning is supported,
// return OEMCrypto_ERROR_NEEDS_KEYBOX_PROVISIONING. If none of these work,
// then return the status of the L3 keybox or cert.
OEMCryptoResult ValidateOrInstallKeyboxOrCert() {
if (!level1_valid_) {
// TODO(b/189989043): add metrics.
// If level 1 not initialized, then return level 3's answer.
return level3_.IsKeyboxOrOEMCertValid ? level3_.IsKeyboxOrOEMCertValid()
: OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
if (!level1_.IsKeyboxOrOEMCertValid) {
// TODO(b/189989043): add metrics.
LOGE("L1 invalid function pointers. Falling back to L3");
if (level1_.Terminate) level1_.Terminate();
level1_valid_ = false;
return level3_.IsKeyboxOrOEMCertValid ? level3_.IsKeyboxOrOEMCertValid()
: OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
// Check if the keybox or oem certificate is valid, if so, we are finished
// with initialization. Record some metrics and return success.
const OEMCryptoResult rot_valid = level1_.IsKeyboxOrOEMCertValid();
wvcdm::metrics::OemCryptoDynamicAdapterMetrics& metrics =
wvcdm::metrics::GetDynamicAdapterMetricsInstance();
// Figure out provisioning method. Defaults to keybox.
const OEMCrypto_ProvisioningMethod provisioning_method =
level1_.GetProvisioningMethod ? level1_.GetProvisioningMethod()
: OEMCrypto_Keybox;
if (rot_valid == OEMCrypto_SUCCESS) {
// The keybox or certificate is valid -- that means initialization is done
// and we only have save some metrics and return.
metrics.OemCryptoDynamicAdapterMetrics::SetInitializationMode(
(provisioning_method == OEMCrypto_Keybox)
? wvcdm::metrics::OEMCrypto_INITIALIZED_USING_L1_WITH_KEYBOX
: wvcdm::metrics::
OEMCrypto_INITIALIZED_USING_L1_WITH_PROVISIONING_3_0);
return OEMCrypto_SUCCESS;
}
// At this point, the L1 keybox or cert is not valid. If are able to
// install one, then we look on the file system for one. If it is there we
// try to install it.
OEMCryptoResult file_attempt = TryToInstallKeybox();
if (file_attempt == OEMCrypto_SUCCESS) {
// If loading the keybox succeeded, we are done. good job.
metrics.OemCryptoDynamicAdapterMetrics::SetInitializationMode(
wvcdm::metrics::OEMCrypto_INITIALIZED_USING_L1_INSTALLED_KEYBOX);
return OEMCrypto_SUCCESS;
}
if (rot_valid == OEMCrypto_ERROR_NEEDS_KEYBOX_PROVISIONING) {
// TODO(b/189989043): add metrics.
return OEMCrypto_ERROR_NEEDS_KEYBOX_PROVISIONING;
}
if (file_attempt == OEMCrypto_ERROR_NOT_IMPLEMENTED) {
// No keybox to load, and none installed, so give up.
LOGW("Bad Level 1 Root of Trust. Falling back to L3");
metrics.OemCryptoDynamicAdapterMetrics::SetInitializationMode(
wvcdm::metrics::OEMCrypto_INITIALIZED_USING_L3_BAD_KEYBOX);
} else {
// There was a keybox to load, but there was an error loading it. So give
// up.
LOGW("Error installing Level 1 Root of Trust. Falling back to L3");
metrics.OemCryptoDynamicAdapterMetrics::SetInitializationMode(
wvcdm::metrics::
OEMCrypto_INITIALIZED_USING_L3_COULD_NOT_INSTALL_KEYBOX);
}
if (level1_.Terminate) level1_.Terminate();
level1_valid_ = false;
return level3_.IsKeyboxOrOEMCertValid ? level3_.IsKeyboxOrOEMCertValid()
: OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
private:
bool level1_valid_;
void* level1_library_;
@@ -1149,6 +1154,51 @@ class Adapter {
if (!var) return false;
return !strcmp(var, "yes");
}
// Try to install a keybox from the file system.
OEMCryptoResult TryToInstallKeybox() {
if (!level1_.InstallKeyboxOrOEMCert) return OEMCrypto_ERROR_NOT_IMPLEMENTED;
wvcdm::FileSystem file_system;
std::string filename;
if (!wvcdm::Properties::GetFactoryKeyboxPath(&filename)) {
// No keybox or cert file found. Give up.
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
const ssize_t size = file_system.FileSize(filename);
if (size <= 0) {
// The keybox file does not exit or has size 0.
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
// After this, any error will be logged differently to metrics
// because we found a keybox on the filesystem, so we expect it to work.
auto file = file_system.Open(filename, file_system.kReadOnly);
if (!file) {
// A keybox or cert file was found, but can't open it. Give up, but
// log it as a different error because the file did exist.
LOGW("Could not open %s", filename.c_str());
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
std::vector<uint8_t> root_key(size);
ssize_t size_read =
file->Read(reinterpret_cast<char*>(root_key.data()), size);
if (level1_.InstallKeyboxOrOEMCert(root_key.data(), size_read) !=
OEMCrypto_SUCCESS) {
// A keybox or cert file was read, but I could not install it. Give up.
LOGE("Could NOT install root key from %s", filename.c_str());
return OEMCrypto_ERROR_KEYBOX_INVALID;
}
if (!level1_.IsKeyboxOrOEMCertValid) return OEMCrypto_ERROR_NOT_IMPLEMENTED;
OEMCryptoResult rot_valid = level1_.IsKeyboxOrOEMCertValid();
if (rot_valid == OEMCrypto_SUCCESS) {
// A valid keybox or cert file was read and installed. Yay! return
// success.
LOGI("Installed root key from %s", filename.c_str());
} else {
LOGW("Installed root key from %s, but invalid(%d)", filename.c_str(),
rot_valid);
}
return rot_valid;
}
};
static std::unique_ptr<Adapter> gAdapter;
@@ -1156,6 +1206,23 @@ static std::unique_ptr<Adapter> gAdapter;
namespace wvcdm {
OEMCryptoResult OEMCrypto_InitializeAndCheckKeybox(
bool* needs_keybox_provisioning) {
if (!needs_keybox_provisioning) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
if (!gAdapter) {
gAdapter.reset(new Adapter());
}
const OEMCryptoResult status = gAdapter->Initialize();
if (status != OEMCrypto_SUCCESS) return status;
const OEMCryptoResult keybox_status =
gAdapter->ValidateOrInstallKeyboxOrCert();
if (keybox_status == OEMCrypto_ERROR_NEEDS_KEYBOX_PROVISIONING) {
*needs_keybox_provisioning = true;
return OEMCrypto_SUCCESS;
}
return keybox_status;
}
OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION* session,
SecurityLevel level) {
if (!gAdapter) return OEMCrypto_ERROR_OPEN_SESSION_FAILED;
@@ -1183,15 +1250,6 @@ OEMCrypto_ProvisioningMethod OEMCrypto_GetProvisioningMethod(
return fcn->GetProvisioningMethod();
}
OEMCryptoResult OEMCrypto_IsKeyboxOrOEMCertValid(SecurityLevel level) {
if (!gAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
const FunctionPointers* fcn = gAdapter->GetFunctionPointers(level);
if (!fcn) return OEMCrypto_ERROR_INVALID_SESSION;
if (fcn->IsKeyboxOrOEMCertValid == nullptr)
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
return fcn->IsKeyboxOrOEMCertValid();
}
OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID, size_t* idLength,
SecurityLevel level) {
if (!gAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
@@ -2155,7 +2213,12 @@ extern "C" OEMCryptoResult OEMCrypto_LoadTestKeybox(const uint8_t* buffer,
}
extern "C" OEMCryptoResult OEMCrypto_IsKeyboxOrOEMCertValid() {
return OEMCrypto_IsKeyboxOrOEMCertValid(kLevelDefault);
if (!gAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
const FunctionPointers* fcn = gAdapter->GetFunctionPointers(kLevelDefault);
if (!fcn) return OEMCrypto_ERROR_INVALID_SESSION;
if (fcn->IsKeyboxOrOEMCertValid == nullptr)
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
return fcn->IsKeyboxOrOEMCertValid();
}
extern "C" OEMCrypto_ProvisioningMethod OEMCrypto_GetProvisioningMethod() {
@@ -2733,3 +2796,24 @@ extern "C" OEMCryptoResult OEMCrypto_FreeSecureBuffer(
}
return pair.fcn->FreeSecureBuffer(pair.session, output_descriptor, secure_fd);
}
extern "C" OEMCryptoResult OEMCrypto_GenerateOTARequest(uint8_t* buffer,
size_t* buffer_length,
bool use_test_key) {
if (!gAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
const FunctionPointers* fcn = gAdapter->GetFunctionPointers(kLevelDefault);
if (!fcn) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
if (fcn->GenerateOTARequest == nullptr)
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
return fcn->GenerateOTARequest(buffer, buffer_length, use_test_key);
}
extern "C" OEMCryptoResult OEMCrypto_ProcessOTAKeybox(const uint8_t* buffer,
size_t buffer_length,
bool use_test_key) {
if (!gAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
const FunctionPointers* fcn = gAdapter->GetFunctionPointers(kLevelDefault);
if (!fcn) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
if (fcn->ProcessOTAKeybox == nullptr) return OEMCrypto_ERROR_NOT_IMPLEMENTED;
return fcn->ProcessOTAKeybox(buffer, buffer_length, use_test_key);
}

View File

@@ -0,0 +1,17 @@
// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
#include "OEMCryptoCENC.h"
extern "C" OEMCryptoResult OEMCrypto_GenerateOTARequest(uint8_t* buffer,
size_t* buffer_length,
bool use_test_key) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
extern "C" OEMCryptoResult OEMCrypto_ProcessOTAKeybox(const uint8_t* buffer,
size_t buffer_length,
bool use_test_key) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}

View File

@@ -82,6 +82,32 @@ const std::string kCpStagingProvisioningServiceCertificate =
"8598ed5751b38694419242a875d9e00d5a5832933024b934859ec8be78adccbb"
"1ec7127ae9afeef9c5cd2e15bd3048e8ce652f7d8c5d595a0323238c598a28";
// Service certificate for qa.widevine.com
const std::string kCpQAProvisioningServiceCertificate =
"0abc02080312100ec8164669cc2fdfc253b3b5e763276e18abd8cdcf05228e02"
"3082010a0282010100b24d497c0cc6ab5072f97623daa49b8d5564360654d8e5"
"8df8db7a23158f1afdd04724cbadbe87001532d9d6dec3b06973666da7759ec3"
"bf3083e2d9b85e7a47c340db796b085493a460eef31d56e3f15d857713c55cdb"
"164fe09e2a06be7fb979ad55e33a59ade3712aed2445b89fc145556a9e0093fa"
"36fc3ff4d05291a9633d20c80a13cd0d924ed9078395714c30b49019f4d6f5ba"
"093ad958aee9a164ba73ec298f905662de5859d3e6fae41c063f262d29dae75e"
"8654ac9d68f3e3fccc809573d0f90704a77f9bce391d0a5f265f438119e4fb0b"
"ec27706f5c7fc888f665730b691a0431e30cb3b57dfd078838c44550c3b79b35"
"0552a92a760f90c6cf02030100013a0f71612e7769646576696e652e636f6d12"
"800331ca1662fdc97e02debdca5b6de35dce5da87b5f61b15745ccf83e66197c"
"e31bc6379ae4f6a5e4fd8a0e76f979701c5a715c06d70908563626d0dd3986b5"
"e623a7b6336789c67f0fc68f9ec68e045f85d9a06942f4af0fe47d801cf035af"
"27924f1c4cd395d15ce2f92f48044254fdefe37320471d7009160f5293183ca4"
"a09bca71f76f1457a80eebcf12706bd79256f1b02e67dc002fc81e18c00d880f"
"04b0187e6ef59ae75eaaf6b16672a887b9657f1796607d1585d1998283af1650"
"9bd9a170c262056aad69e222a4c3180d104a76d76da29082e4f2e5297d1ec44d"
"ed98c999981688089d8ff2d62f0f13b96ce5e89a4c215d60f025fa29811fcdc1"
"848fe0581f612f45733da4b4c8803ae8088dcb3b811ea9c691daccfbe9cbf603"
"13e8f85eb68f2f1d8cdf9e4fc91a46157a90fffafbd9d408b319307ea4d3d4a9"
"d2f177355ad361f5284057dec1b186beb85dcbda64bf00a164cecc66c1878961"
"7748618d069c39f365e8347acdae777cc4e3c3c3c3fe9698c4f5ee1285b0e6a9"
"675e";
// -----------------------------------------------------------------------------
// Below are several choices for licenseing servers: production, UAT, staging
// and staging and maybe Google Play. We haven't tested with Google Play in a
@@ -354,4 +380,8 @@ const std::string& ConfigTestEnv::GetLicenseServerUrl(
}
}
std::string ConfigTestEnv::QAProvisioningServiceCertificate() {
return a2bs_hex(kCpQAProvisioningServiceCertificate);
}
} // namespace wvcdm

View File

@@ -116,6 +116,8 @@ class ConfigTestEnv {
const std::string& provisioning_service_certificate) {
provisioning_service_certificate_.assign(provisioning_service_certificate);
}
// The QA service certificate, used for a local provisioning server.
static std::string QAProvisioningServiceCertificate();
private:
void Init(ServerConfigurationId server_id);

View File

@@ -0,0 +1,11 @@
// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
#include <gtest/gtest.h>
#include "crypto_session.h"
namespace wvcdm {
TEST(OTAKeyboxTest, TestThatTheBuildFilesWork) { ASSERT_TRUE(true); }
} // namespace wvcdm

View File

@@ -112,6 +112,11 @@ void show_menu(const char* prog_name, const std::string& extra_help_text) {
<< " in the url" << std::endl
<< std::endl;
std::cout << " --qa_provisioning" << std::endl;
std::cout << " use the QA provisioning cert and QA test keybox"
<< std::endl
<< std::endl;
std::cout << " --fake_sleep" << std::endl;
std::cout << " Use a fake clock to sleep for duration tests. This cannot"
<< " be used with a real OEMCrypto." << std::endl
@@ -162,6 +167,7 @@ bool ExtractSignedMessage(const std::string& response,
} // namespace
ConfigTestEnv WvCdmTestBase::default_config_(kContentProtectionUatServer);
bool WvCdmTestBase::use_qa_test_keybox_ = false;
void WvCdmTestBase::StripeBuffer(std::vector<uint8_t>* buffer, size_t size,
uint8_t init) {
@@ -211,6 +217,13 @@ TestCryptoSession::TestCryptoSession(metrics::CryptoMetrics* crypto_metrics)
// The first CryptoSession should have initialized OEMCrypto. This is right
// after that, so should tell oemcrypto to use a test keybox.
if (session_count() == 1) {
if (!initialized()) {
// If not initialized, try again and see if we are just missing a keybox.
// Since we plan to install a test keybox, we can ignore keybox errors.
const OEMCryptoResult status = ::OEMCrypto_Initialize();
if (status != OEMCrypto_SUCCESS) return;
OverrideInitializedForTesting(true);
}
WvCdmTestBase::InstallTestRootOfTrust();
}
}
@@ -262,12 +275,14 @@ void WvCdmTestBase::SetUp() {
}
void WvCdmTestBase::InstallTestRootOfTrust() {
const wvoec::WidevineKeybox& test_keybox =
use_qa_test_keybox_ ? wvoec::kQATestKeybox : wvoec::kTestKeybox;
switch (wvoec::global_features.derive_key_method) {
case wvoec::DeviceFeatures::LOAD_TEST_KEYBOX:
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_LoadTestKeybox(
reinterpret_cast<const uint8_t*>(&wvoec::kTestKeybox),
sizeof(wvoec::kTestKeybox)));
reinterpret_cast<const uint8_t*>(&test_keybox),
sizeof(test_keybox)));
break;
case wvoec::DeviceFeatures::LOAD_TEST_RSA_KEY:
// Rare case: used by devices with baked in DRM cert.
@@ -451,6 +466,10 @@ bool WvCdmTestBase::Initialize(int argc, const char* const argv[],
is_cast_receiver = true;
} else if (arg == "--fake_sleep") {
wvcdm::TestSleep::set_real_sleep(false);
} else if (arg == "--qa_provisioning") {
use_qa_test_keybox_ = true;
default_config_.set_provisioning_service_certificate(
default_config_.QAProvisioningServiceCertificate());
} else if (arg.find("--gtest") == 0) {
// gtest arguments will be passed to gtest by the main program.
continue;

View File

@@ -67,6 +67,9 @@ class WvCdmTestBase : public ::testing::Test {
// arguments before any tests are created.
static ConfigTestEnv default_config_;
// If the tests should use the QA test keybox.
static bool use_qa_test_keybox_;
// Configuration for an individual test. This is initialized to be the
// default configuration, but can be modified by the test itself.
ConfigTestEnv config_;

View File

@@ -1186,6 +1186,9 @@ void PrintTo(const enum OEMCryptoResult& value, ::std::ostream* os) {
case OEMCrypto_ERROR_INVALID_ENTITLED_KEY_SESSION:
*os << "OEMCrypto_ERROR_INVALID_ENTITLED_KEY_SESSION";
break;
case OEMCrypto_ERROR_NEEDS_KEYBOX_PROVISIONING:
*os << "ERROR_NEEDS_KEYBOX_PROVISIONING";
break;
// ODK Values.
case ODK_ERROR_CORE_MESSAGE:
*os << "CORE_MESSAGE";

View File

@@ -97,6 +97,10 @@ test_name := initialization_data_unittest
test_src_dir := ../core/test
include $(LOCAL_PATH)/unit-test.mk
test_name := keybox_ota_test
test_src_dir := ../core/test
include $(LOCAL_PATH)/unit-test.mk
test_name := license_keys_unittest
test_src_dir := ../core/test
include $(LOCAL_PATH)/unit-test.mk

View File

@@ -624,6 +624,9 @@ typedef enum OEMCrypto_ProvisioningMethod {
#define OEMCrypto_MinorAPIVersion _oecc108
#define OEMCrypto_AllocateSecureBuffer _oecc109
#define OEMCrypto_FreeSecureBuffer _oecc110
// Reserved 111-112.
#define OEMCrypto_GenerateOTARequest _oecc113
#define OEMCrypto_ProcessOTAKeybox _oecc114
// clang-format on
/// @addtogroup initcontrol
@@ -2963,7 +2966,24 @@ OEMCrypto_ProvisioningMethod OEMCrypto_GetProvisioningMethod(void);
* @retval OEMCrypto_ERROR_INVALID_RSA_KEY
* @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED
*
* @threading
* On devices that support OEMCrypto_GenerateOTARequest and
* OEMCrypto_ProcessOTAKeybox, this function may return
* OEMCrypto_ERROR_NEEDS_KEYBOX_PROVISIONING when a valid keybox is not
* present.
*
* Parameters:
* none
*
* Returns:
* OEMCrypto_SUCCESS
* OEMCrypto_ERROR_BAD_MAGIC
* OEMCrypto_ERROR_BAD_CRC
* OEMCrypto_ERROR_KEYBOX_INVALID
* OEMCrypto_ERROR_INVALID_RSA_KEY
* OEMCrypto_ERROR_SYSTEM_INVALIDATED
* OEMCrypto_ERROR_NEEDS_KEYBOX_PROVISIONING
*
* Threading:
* This is a "Property Function" and may be called simultaneously with any
* other property function or session function, but not any initialization or
* usage table function, as if the CDM holds a read lock on the OEMCrypto
@@ -4719,6 +4739,93 @@ OEMCryptoResult OEMCrypto_FreeSecureBuffer(
/// @}
/****************************************************************************/
/****************************************************************************/
/* The following functions are optional. They are only used if the device
* supports OTA keybox provisioning. Widevine does not allow all devices to
* support OTA provisioning. Using an OTA provisioned keybox usually lowers a
* device's security profile in the DCSL. Please work with your Widevine Partner
* Engineer before implementing these functions to make sure you understand the
* security implications of using Keybox OTA Provisioning.
*/
/*
* OEMCrypto_GenerateOTARequest
*
* Description:
* Generate an OTA Keybox provisioning request. The format of the
* message is specified in the document Keybox OTA Reprovisioning. If
* use_test_key is true, then the debug model key and id should be
* used. Widevine does not allow all devices to support OTA
* provisioning. Using an OTA provisioned keybox usually lowers a device's
* security profile in the DCSL.
*
* Parameters:
* [out] buffer: where the provisioning request is stored.
* [in/out] buffer_length: length of the request, in bytes.
* [in] use_test_key: If true, use the debug model key. This is used for
* testing the workflow.
*
* Returns:
* OEMCrypto_SUCCESS on success
* OEMCrypto_ERROR_SHORT_BUFFER - if buffer_length is too small.
* OEMCrypto_ERROR_NOT_IMPLEMENTED
* Any other error will be logged.
*
* Threading:
* This is an "Initialization and Termination Function" and will not be called
* simultaneously with any other function, as if the CDM holds a write lock on
* the OEMCrypto system. It will be called only after
* OEMCrypto_IsKeyboxOrOEMCertValid() returns
* OEMCrypto_ERROR_NEEDS_KEYBOX_PROVISIONING immediately after initialization,
* and before any session is opened.
*
* Version:
* This method is new in API version 16.
*/
OEMCryptoResult OEMCrypto_GenerateOTARequest(uint8_t* buffer,
size_t* buffer_length,
bool use_test_key);
/*
* OEMCrypto_ProcessOTAKeybox
*
* Description:
* The buffer will be parsed as an OTA Keybox provisioning message, as
* described in the document OTA Keybox Reprovisioning. The
* signature will be verified. The keybox will be decrypted and verified. If
* use_test_key is false, the keybox will be installed permanently.
*
* If use_test_keybox is true, do not use the real model key, use the debug
* model key specified in OTA Keybox Reprovisioning.
*
* Parameters:
* [in] buffer: pointer to provisioning response.
* [in] buffer_length: length of the buffer, in bytes.
* [in] use_test_key: If true, use the debug model key. This is used for
* testing the workflow.
*
* Returns:
* OEMCrypto_SUCCESS on success
* OEMCrypto_ERROR_NOT_IMPLEMENTED
* OEMCrypto_ERROR_SIGNATURE_FAILURE - signature of message was wrong.
* OEMCrypto_ERROR_KEYBOX_INVALID - if the keybox was unpacked, but is
* invalid.
* OEMCrypto_ERROR_WRITE_KEYBOX - could not save keybox.
* Any other error will be logged.
*
* Threading:
* This is an "Initialization and Termination Function" and will not be called
* simultaneously with any other function, as if the CDM holds a write lock on
* the OEMCrypto system. It will only be called after
* OEMCrypto_GenerateOTARequest.
*
* Version:
* This method is new in API version 16.
*/
OEMCryptoResult OEMCrypto_ProcessOTAKeybox(const uint8_t* buffer,
size_t buffer_length,
bool use_test_key);
/****************************************************************************/
/****************************************************************************/
/* The following functions are deprecated. They are not required for the

View File

@@ -88,6 +88,7 @@ typedef enum OEMCryptoResult {
OEMCrypto_ERROR_MULTIPLE_USAGE_ENTRIES = 58,
OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION = 59,
OEMCrypto_ERROR_INVALID_ENTITLED_KEY_SESSION = 60,
OEMCrypto_ERROR_NEEDS_KEYBOX_PROVISIONING = 61,
/* ODK return values */
ODK_ERROR_BASE = 1000,
ODK_ERROR_CORE_MESSAGE = ODK_ERROR_BASE,

View File

@@ -18,6 +18,7 @@ LOCAL_SRC_FILES:= \
oemcrypto_test.cpp \
oemcrypto_test_android.cpp \
oemcrypto_test_main.cpp \
ota_keybox_test.cpp \
wvcrc.cpp \
../../cdm/util/test/test_sleep.cpp \

View File

@@ -18,6 +18,7 @@ namespace wvoec {
// This is a test keybox. It will not be accepted by production systems. By
// using a known keybox for these tests, the results for a given set of inputs
// to a test are predictable and can be compared to the actual results.
// clang-format off
static const WidevineKeybox kTestKeybox = {
// Sample keybox used for test vectors
{
@@ -49,6 +50,43 @@ static const WidevineKeybox kTestKeybox = {
0x39, 0xf2, 0x94, 0xa7,
}
};
// clang-format on
// This test keybox is only accepted by the QA provisioning server.
// It is not valid with the production provisioning server.
// clang-format off
static const WidevineKeybox kQATestKeybox = {
// Sample keybox used for test vectors
{
// deviceID = WidevineQATestOnlyKeybox000
0x57, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65,
0x51, 0x41, 0x54, 0x65, 0x73, 0x74, 0x4f, 0x6e,
0x6c, 0x79, 0x4b, 0x65, 0x79, 0x62, 0x6f, 0x78,
0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00,
}, {
// key
0x03, 0x77, 0x0f, 0x4e, 0x29, 0x77, 0x4b, 0x43,
0x9e, 0xd2, 0x8a, 0x94, 0x73, 0xb3, 0x26, 0x65,
}, {
// data (system ID 2000000 = 0x1E8480).
0x00, 0x00, 0x00, 0x02, 0x00, 0x1e, 0x84, 0x80,
0x90, 0x46, 0x8a, 0x1d, 0x27, 0x52, 0xca, 0xdb,
0x5b, 0xf4, 0x67, 0xcb, 0xd3, 0x5e, 0x9e, 0xe9,
0xb1, 0xcf, 0x89, 0x74, 0x08, 0x26, 0x96, 0x5b,
0x43, 0x02, 0x7c, 0xb6, 0x4a, 0x9d, 0xf6, 0x7e,
0x24, 0x82, 0x1d, 0xe2, 0x89, 0x52, 0x8e, 0xac,
0xf2, 0x98, 0xac, 0x92, 0xa9, 0x40, 0x11, 0x9f,
0x9f, 0xf8, 0x55, 0x84, 0x42, 0x04, 0x34, 0xbc,
0x53, 0x14, 0x3d, 0x44, 0x97, 0x5c, 0xd9, 0xb4,
}, {
// magic
0x6b, 0x62, 0x6f, 0x78,
}, {
// Crc
0x43, 0xa2, 0x67, 0x63,
}
};
// clang-format on
// A 2048 bit RSA key in PKCS#8 PrivateKeyInfo format
// Used to verify the functions that manipulate RSA keys.

View File

@@ -0,0 +1,53 @@
// 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 <gtest/gtest.h>
#include "OEMCryptoCENC.h"
#include "clock.h"
#include "log.h"
#include "oec_decrypt_fallback_chain.h"
#include "oec_device_features.h"
#include "oec_session_util.h"
#include "oec_test_data.h"
#include "oemcrypto_session_tests_helper.h"
#include "oemcrypto_types.h"
#include "platform.h"
#include "string_conversions.h"
#include "test_sleep.h"
using namespace std;
namespace wvoec {
class OTAKeyboxProvisioningTest : public ::testing::Test, public SessionUtil {
protected:
void SetUp() override {
::testing::Test::SetUp();
wvcdm::TestSleep::SyncFakeClock();
const ::testing::TestInfo* const test_info =
::testing::UnitTest::GetInstance()->current_test_info();
LOGD("Running test %s.%s", test_info->test_case_name(), test_info->name());
OEMCrypto_SetSandbox(kTestSandbox, sizeof(kTestSandbox));
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Initialize());
}
void TearDown() override {
OEMCrypto_Terminate();
::testing::Test::TearDown();
}
};
TEST_F(OTAKeyboxProvisioningTest, BasicTest) {
OEMCryptoResult result = OEMCrypto_IsKeyboxValid();
if (result == OEMCrypto_SUCCESS) {
cout << " "
<< "Keybox valid after initialization. Skipping rest of test." << endl;
return;
}
ASSERT_EQ(result, OEMCrypto_ERROR_NEEDS_KEYBOX_PROVISIONING);
cout << " "
<< "OTA Keybox functions supported. Device needs provisioning." << endl;
}
} // namespace wvoec