Wrapped OKP info into several classes.
[ Cherry pick of http://ag/15836995 ] [ Merge of http://go/wvgerrit/133744 ] This changes adds several small classes which contain and manage system and engine information related to OTA keybox provisioning. These classes closely map to the OKP device file messages. Bug: 189232882 Test: Linux unit tests Change-Id: Ia9334c38f9d7ea89b30d9ad05f0595570bb38658 Storing and loading OKP info. [ Merge of http://go/wvgerrit/133763 and http://go/ag/15645333 ] This change extends the DeviceFiles module to be able to store and load OKP info. Mild data validation is performed when storing and loading the information. Bug: 189232882 Test: Android unit tests Change-Id: I077de3234157252f2255a4389bf82a8d5344a355 System OKP fallback policy. [ Merge of http://go/wvgerrit/133783 and http://go/ag/15645334 ] SystemFallbackPolicy provides a thread-safe interface for accessing and modifying OKP info. Bug: 189232882 Test: Android unit tests Change-Id: I4e43e3bc047ed5fb6cb517b53e4094e812b70e1e Engine OKP provisioner. [ Merge of http://go/wvgerrit/133803 and http://go/ag/15645335 ] The OtaKeyboxProvisioner provides a CdmEngine-specific context for performing OTA keybox provisioning. Utilizes the system-wide SystemFallbackPolicy to relay provisioning status between engines. The provisioner will handle message wrapping and unwrapping of the raw OTA keybox request / response into the SignedProvisioningMessage which is sent to/received from the provisioning server. [ Partial merge of http://go/wvgerrit/125844 ] Note: Includes partial CryptoSession changes from various CLs. CryptoSession functionality has been stripped to reduce impact of this CL. Bug: 189232882 Test: Android unit tests Change-Id: I282bf7d1887daefb2250af1bd595c4dc3dfcfb29 Integrated OKP into CDM Engine [ Merge of http://go/wvgerrit/133804 and http://go/ag/15646376 ] Extended the functionality of the CdmEngine to check if the device requires OKP and to initialize OKP resources if required. The functionality of OpenSession() and GetProvisioningRequest() have been the most affected. If OKP is required, these methods will signal to the app that provisioning is required and will return an OKP request. Once a device is provisioned, the OKP data is cleared away and the CdmEngine will resume normal operation. Engines created after a device is provisioned will immediately enter normal operations. The exception is for CdmEngines which failed to perform OKP for some reason and are still running. Those apps will need to restart before gaining access to L1 operations. Bug: 187646550 Test: Android integration tests Change-Id: Ia572a66a7b73479355758aa3d0c682691eaca0fc
This commit is contained in:
@@ -95,6 +95,7 @@ const char kTrue[] = "true";
|
||||
const char kUsageInfoFileNameExt[] = ".bin";
|
||||
const char kUsageInfoFileNamePrefix[] = "usage";
|
||||
const char kUsageTableFileName[] = "usgtable.bin";
|
||||
const char kOkpInfoFileName[] = "okp.bin";
|
||||
const char kWildcard[] = "*";
|
||||
// TODO(b/192430982): Renable expiration of legacy DRM certificates
|
||||
// constexpr int64_t kFourMonthsInSeconds = (2 * 30 + 2 * 31) * 24 * 60 * 60;
|
||||
@@ -1627,6 +1628,164 @@ bool DeviceFiles::HasCertificate(CertificateType certificate_type) {
|
||||
return FileExists(certificate_file_name);
|
||||
}
|
||||
|
||||
bool DeviceFiles::StoreOkpInfo(const okp::SystemFallbackInfo& info) {
|
||||
using StoredOkpInfo = video_widevine_client::sdk::OtaKeyboxProvisioningInfo;
|
||||
using okp::SystemState;
|
||||
RETURN_FALSE_IF_UNINITIALIZED();
|
||||
if (security_level_ != kSecurityLevelL1) {
|
||||
LOGE("OKP info is only supported by L1: level = %d",
|
||||
static_cast<int>(security_level_));
|
||||
return false;
|
||||
}
|
||||
video_widevine_client::sdk::File file;
|
||||
file.set_type(video_widevine_client::sdk::File::OKP_INFO);
|
||||
file.set_version(video_widevine_client::sdk::File::VERSION_1);
|
||||
StoredOkpInfo* stored_info = file.mutable_okp_info();
|
||||
switch (info.state()) {
|
||||
case SystemState::kNeedsProvisioning:
|
||||
stored_info->set_state(StoredOkpInfo::OKP_NEEDS_PROVISIONING);
|
||||
break;
|
||||
case SystemState::kFallbackMode:
|
||||
stored_info->set_state(StoredOkpInfo::OKP_FALLBACK_MODE);
|
||||
break;
|
||||
case SystemState::kProvisioned:
|
||||
stored_info->set_state(StoredOkpInfo::OKP_PROVISIONED);
|
||||
break;
|
||||
case SystemState::kUnknown:
|
||||
default:
|
||||
LOGE("Unexpected OKP state: state = %d", static_cast<int>(info.state()));
|
||||
return false;
|
||||
}
|
||||
if (info.first_checked_time() <= 0) {
|
||||
LOGE("OKP first checked time is missing");
|
||||
return false;
|
||||
}
|
||||
stored_info->set_first_checked_time(info.first_checked_time());
|
||||
|
||||
if (info.state() == SystemState::kProvisioned) {
|
||||
if (!info.HasProvisioningTime()) {
|
||||
LOGE("OKP set as provisioned, but missing provisioning time");
|
||||
return false;
|
||||
}
|
||||
stored_info->set_provisioning_time(info.provisioning_time());
|
||||
} else if (info.state() == SystemState::kFallbackMode) {
|
||||
if (!info.HasBackoffStartTime() || !info.HasBackoffDuration()) {
|
||||
LOGE("OKP fallback information is missing ");
|
||||
return false;
|
||||
}
|
||||
stored_info->set_backoff_start_time(info.backoff_start_time());
|
||||
stored_info->set_backoff_duration(info.backoff_duration());
|
||||
} else {
|
||||
if (info.HasBackoffDuration()) {
|
||||
// Store backoff duration from before.
|
||||
stored_info->set_backoff_duration(info.backoff_duration());
|
||||
}
|
||||
}
|
||||
|
||||
std::string serialized_file;
|
||||
file.SerializeToString(&serialized_file);
|
||||
return StoreFileWithHash(GetOkpInfoFileName(), serialized_file) == kNoError;
|
||||
}
|
||||
|
||||
bool DeviceFiles::RetrieveOkpInfo(okp::SystemFallbackInfo* info) {
|
||||
using StoredOkpInfo = video_widevine_client::sdk::OtaKeyboxProvisioningInfo;
|
||||
using okp::SystemState;
|
||||
RETURN_FALSE_IF_UNINITIALIZED();
|
||||
RETURN_FALSE_IF_NULL(info);
|
||||
info->Clear();
|
||||
if (security_level_ != kSecurityLevelL1) {
|
||||
LOGE("OKP info is only supported by L1: level = %d",
|
||||
static_cast<int>(security_level_));
|
||||
return false;
|
||||
}
|
||||
// File meta-data validation.
|
||||
video_widevine_client::sdk::File file;
|
||||
if (RetrieveHashedFile(GetOkpInfoFileName(), &file) != kNoError) {
|
||||
LOGE("Unable to retrieve OKP info file");
|
||||
return false;
|
||||
}
|
||||
if (file.type() != video_widevine_client::sdk::File::OKP_INFO) {
|
||||
LOGE("Incorrect file type: type = %d, expected_type = %d",
|
||||
static_cast<int>(file.type()),
|
||||
static_cast<int>(video_widevine_client::sdk::File::OKP_INFO));
|
||||
return false;
|
||||
}
|
||||
if (file.version() != video_widevine_client::sdk::File::VERSION_1) {
|
||||
LOGE("Incorrect file version: version = %d, expected_version = %d",
|
||||
static_cast<int>(file.version()),
|
||||
static_cast<int>(video_widevine_client::sdk::File::VERSION_1));
|
||||
return false;
|
||||
}
|
||||
if (!file.has_okp_info()) {
|
||||
// OKP info is only stored if at least 1 field is non-empty. This
|
||||
// must be an error.
|
||||
LOGD("OKP info is not present in file");
|
||||
return false;
|
||||
}
|
||||
|
||||
const StoredOkpInfo& stored_info = file.okp_info();
|
||||
switch (stored_info.state()) {
|
||||
case StoredOkpInfo::OKP_NEEDS_PROVISIONING:
|
||||
info->SetState(SystemState::kNeedsProvisioning);
|
||||
break;
|
||||
case StoredOkpInfo::OKP_FALLBACK_MODE:
|
||||
info->SetState(SystemState::kFallbackMode);
|
||||
break;
|
||||
case StoredOkpInfo::OKP_PROVISIONED:
|
||||
info->SetState(SystemState::kProvisioned);
|
||||
break;
|
||||
case StoredOkpInfo::OKP_UNKNOWN:
|
||||
default:
|
||||
LOGE("Unexpected OKP state: stored_state = %d",
|
||||
static_cast<int>(stored_info.state()));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (stored_info.first_checked_time() <= 0) {
|
||||
LOGE("OKP first check time not present");
|
||||
info->Clear();
|
||||
return false;
|
||||
}
|
||||
info->SetFirstCheckedTime(stored_info.first_checked_time());
|
||||
|
||||
if (info->state() == SystemState::kProvisioned) {
|
||||
if (stored_info.provisioning_time() <= 0) {
|
||||
LOGE("OKP set as provisioned, but missing provisioning time");
|
||||
info->Clear();
|
||||
return false;
|
||||
}
|
||||
info->SetProvisioningTime(stored_info.provisioning_time());
|
||||
return true;
|
||||
}
|
||||
|
||||
if (info->state() == SystemState::kFallbackMode) {
|
||||
if (stored_info.backoff_start_time() <= 0 ||
|
||||
stored_info.backoff_duration() <= 0) {
|
||||
LOGE("OKP backoff information is missing");
|
||||
info->Clear();
|
||||
return false;
|
||||
}
|
||||
info->SetBackoffStartTime(stored_info.backoff_start_time());
|
||||
info->SetBackoffDuration(stored_info.backoff_duration());
|
||||
return true;
|
||||
}
|
||||
// Provisioned.
|
||||
if (stored_info.backoff_duration() > 0) {
|
||||
info->SetBackoffDuration(stored_info.backoff_duration());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeviceFiles::DeleteOkpInfo() {
|
||||
RETURN_FALSE_IF_UNINITIALIZED();
|
||||
if (security_level_ != kSecurityLevelL1) {
|
||||
LOGE("OKP info is only supported by L1: level = %d",
|
||||
static_cast<int>(security_level_));
|
||||
return false;
|
||||
}
|
||||
return RemoveFile(GetOkpInfoFileName());
|
||||
}
|
||||
|
||||
DeviceFiles::ResponseType DeviceFiles::StoreFileWithHash(
|
||||
const std::string& name, const std::string& serialized_file) {
|
||||
std::string hash = Sha256Hash(serialized_file);
|
||||
@@ -1845,6 +2004,8 @@ std::string DeviceFiles::GetUsageInfoFileName(const std::string& app_id) {
|
||||
return kUsageInfoFileNamePrefix + hash + kUsageInfoFileNameExt;
|
||||
}
|
||||
|
||||
std::string DeviceFiles::GetOkpInfoFileName() { return kOkpInfoFileName; }
|
||||
|
||||
std::string DeviceFiles::GetFileNameSafeHash(const std::string& input) {
|
||||
return Base64SafeEncode(Md5Hash(input));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user