Fall back to L3 if L1 has test keybox am: 31faf51933 am: cbb5bd0f7a

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

Change-Id: I4159f77db5748bde567466157ce5f07be7f64e55
This commit is contained in:
Fred Gylys-Colwell
2022-01-07 06:08:39 +00:00
committed by Automerger Merge Worker
7 changed files with 135 additions and 30 deletions

View File

@@ -358,6 +358,21 @@ void CryptoSession::Init() {
}
}
void CryptoSession::ReinitializeForTest() {
if (initialized_) {
initialized_ = false;
if (OEMCrypto_SUCCESS != OEMCrypto_Terminate()) return;
}
// Give up if we cannot initialize at all.
if (OEMCrypto_SUCCESS != OEMCrypto_Initialize()) return;
initialized_ = true;
// For integration and unit tests we will install a test keybox and do not
// need to do keybox provisioning.
needs_keybox_provisioning_ = false;
// This was skipped in Init because initialization failed.
CacheVersion();
}
void CryptoSession::CacheVersion() {
uint32_t version;
std::string api_version =
@@ -3061,6 +3076,11 @@ CdmResponseType CryptoSession::SetDebugIgnoreKeyboxCount(uint32_t count) {
return MapOEMCryptoResult(status, UNKNOWN_ERROR, "SetDebugIgnoreKeyboxCount");
}
CdmResponseType CryptoSession::SetAllowTestKeybox(bool allow) {
OEMCryptoResult status = OEMCrypto_SetAllowTestKeybox(allow);
return MapOEMCryptoResult(status, UNKNOWN_ERROR, "SetAllowTestKeybox");
}
okp::SystemFallbackPolicy* CryptoSession::GetOkpFallbackPolicy() {
const auto getter = [&]() -> okp::SystemFallbackPolicy* {
// If not set, then OTA keybox provisioning is not supported or

View File

@@ -646,6 +646,17 @@ std::string GetIgnoreCountFile() {
return path;
}
std::string GetAllowTestKeyboxFile() {
std::string path;
if (!wvcdm::Properties::GetDeviceFilesBasePath(wvcdm::kSecurityLevelL1,
&path)) {
LOGW("GetAllowTestKeyboxFile: Unable to get base path");
path = "/data/";
}
path += "debug_allow_test_keybox.txt";
return path;
}
uint32_t GetDebugIgnoreKeyboxCount() {
const std::string filename = GetIgnoreCountFile();
wvcdm::FileSystem file_system;
@@ -698,6 +709,49 @@ OEMCryptoResult SetDebugIgnoreKeyboxCount(uint32_t count) {
return OEMCrypto_SUCCESS;
}
bool GetAllowTestKeybox() {
const std::string filename = GetAllowTestKeyboxFile();
wvcdm::FileSystem file_system;
if (!file_system.Exists(filename)) {
return 0;
}
auto file = file_system.Open(filename, file_system.kReadOnly);
if (!file) {
LOGE("Error opening %s", filename.c_str());
return 0;
}
ssize_t size = file_system.FileSize(filename);
std::string contents(size, ' ');
ssize_t size_read = file->Read(const_cast<char*>(contents.data()), size);
if (size != size_read) {
LOGE("Short allow_test_keybox = %zu", size_read);
return 0;
}
// skip whitespace or any extra garbage.
return (std::string::npos != contents.find("true"));
}
OEMCryptoResult SetAllowTestKeybox(bool allow) {
const std::string filename = GetAllowTestKeyboxFile();
wvcdm::FileSystem file_system;
auto file =
file_system.Open(filename, file_system.kCreate | file_system.kTruncate);
if (!file) {
LOGE("Could not create file %s", filename.c_str());
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
const std::string contents = allow ? "true\n" : "false\n";
const size_t size = contents.size();
ssize_t size_written = file->Write(contents.data(), size);
if (static_cast<ssize_t>(size) != size_written) {
LOGE("Wrote %zd bytes of %s, not %zd, to file %s", size_written,
contents.c_str(), size, filename.c_str());
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
LOGD("Wrote %s to %s", contents.c_str(), filename.c_str());
return OEMCrypto_SUCCESS;
}
struct LevelSession {
FunctionPointers* fcn;
OEMCrypto_SESSION session;
@@ -821,6 +875,7 @@ class Adapter {
level1_.BuildInformation());
}
} else {
level1_failed_ = true;
FallBackToLevel3();
}
return result;
@@ -842,7 +897,6 @@ class Adapter {
Level1Terminate();
level1_ = FunctionPointers(); // revert to all null pointers.
level1_valid_ = false;
level1_failed_ = true;
// Note: if the function pointers are bad, we do not close the library and
// try again later. Instead, we permanently fall back to L3. This is a
// debatable choice: I decided the risk of a dlclose resource leak out
@@ -1168,23 +1222,32 @@ class Adapter {
return result;
}
// Check the system ID of the keybox. This should only be called if the device
// uses provisioning 2.0.
bool UsingTestKeybox() {
uint8_t key_data[256];
size_t key_data_len = sizeof(key_data);
OEMCryptoResult sts = OEMCrypto_GetKeyData(key_data, &key_data_len);
if (sts != OEMCrypto_SUCCESS) return true;
uint32_t* data = reinterpret_cast<uint32_t*>(key_data);
uint32_t system_id = htonl(data[1]);
return system_id == 7912;
}
// 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() {
// then return an error code. The caller should fall back to L3.
OEMCryptoResult ValidateOrInstallL1KeyboxOrCert() {
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;
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
if (!level1_.IsKeyboxOrOEMCertValid) {
// TODO(b/189989043): add metrics.
LOGE("L1 invalid function pointers. Falling back to L3");
FallBackToLevel3();
return level3_.IsKeyboxOrOEMCertValid ? level3_.IsKeyboxOrOEMCertValid()
: OEMCrypto_ERROR_NOT_IMPLEMENTED;
return 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.
@@ -1195,6 +1258,19 @@ class Adapter {
const OEMCrypto_ProvisioningMethod provisioning_method =
level1_.GetProvisioningMethod ? level1_.GetProvisioningMethod()
: OEMCrypto_Keybox;
// For production systems, we do wish to use a test keybox. We do not force
// a fallback to L3 at this point, because this can be overridden by test
// code that requires a test keybox.
if ((rot_valid == OEMCrypto_SUCCESS) &&
(provisioning_method == OEMCrypto_Keybox) && UsingTestKeybox()) {
if (GetAllowTestKeybox()) {
LOGW("Allowing device with test keybox installed.");
} else {
LOGW("Device has test keybox installed.");
return OEMCrypto_ERROR_KEYBOX_INVALID;
}
}
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.
@@ -1232,9 +1308,7 @@ class Adapter {
wvcdm::metrics::
OEMCrypto_INITIALIZED_USING_L3_COULD_NOT_INSTALL_KEYBOX);
}
FallBackToLevel3();
return level3_.IsKeyboxOrOEMCertValid ? level3_.IsKeyboxOrOEMCertValid()
: OEMCrypto_ERROR_NOT_IMPLEMENTED;
return file_attempt;
}
bool IsOTAKeyboxSupported() {
@@ -1348,7 +1422,7 @@ OEMCryptoResult OEMCrypto_InitializeAndCheckKeybox(
// continue on.
if (status != OEMCrypto_SUCCESS) return status;
const OEMCryptoResult keybox_status =
gAdapter->ValidateOrInstallKeyboxOrCert();
gAdapter->ValidateOrInstallL1KeyboxOrCert();
uint32_t ignore_count = GetDebugIgnoreKeyboxCount();
if (keybox_status == OEMCrypto_ERROR_NEEDS_KEYBOX_PROVISIONING ||
ignore_count > 0) {
@@ -1365,7 +1439,13 @@ OEMCryptoResult OEMCrypto_InitializeAndCheckKeybox(
return OEMCrypto_SUCCESS;
}
}
return keybox_status;
if (keybox_status == OEMCrypto_SUCCESS) {
return OEMCrypto_SUCCESS;
}
LOGW("Keybox error: %d. Falling back to L3.", keybox_status);
gAdapter->FallBackToLevel3();
// Return success if the L3 keybox or cert is valid.
return OEMCrypto_IsKeyboxOrOEMCertValid();
}
OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION* session,
@@ -1649,6 +1729,9 @@ OEMCryptoResult OEMCrypto_GetOEMPublicCertificate(uint8_t* public_cert,
OEMCryptoResult OEMCrypto_SetDebugIgnoreKeyboxCount(uint32_t count) {
return SetDebugIgnoreKeyboxCount(count);
}
OEMCryptoResult OEMCrypto_SetAllowTestKeybox(bool allow) {
return SetAllowTestKeybox(allow);
}
} // namespace wvcdm
extern "C" OEMCryptoResult OEMCrypto_SetSandbox(const uint8_t* sandbox_id,