601 lines
24 KiB
C++
601 lines
24 KiB
C++
////////////////////////////////////////////////////////////////////////////////
|
|
// Copyright 2017 Google LLC.
|
|
//
|
|
// This software is licensed under the terms defined in the Widevine Master
|
|
// License Agreement. For a copy of this agreement, please contact
|
|
// widevine-licensing@google.com.
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Implements the DeviceStatusList class.
|
|
|
|
#include "common/device_status_list.h"
|
|
|
|
#include <time.h>
|
|
|
|
#include <map>
|
|
#include <memory>
|
|
|
|
#include <cstdint>
|
|
#include "glog/logging.h"
|
|
#include "absl/strings/ascii.h"
|
|
#include "absl/strings/escaping.h"
|
|
#include "absl/strings/numbers.h"
|
|
#include "absl/strings/str_split.h"
|
|
#include "absl/strings/string_view.h"
|
|
#include "absl/synchronization/mutex.h"
|
|
#include "util/gtl/map_util.h"
|
|
#include "common/client_cert.h"
|
|
#include "common/drm_service_certificate.h"
|
|
#include "common/error_space.h"
|
|
#include "common/hash_algorithm_util.h"
|
|
#include "common/keybox_client_cert.h"
|
|
#include "common/rsa_key.h"
|
|
#include "common/status.h"
|
|
#include "protos/public/client_identification.pb.h"
|
|
#include "protos/public/device_certificate_status.pb.h"
|
|
#include "protos/public/errors.pb.h"
|
|
#include "protos/public/signed_device_info.pb.h"
|
|
|
|
using ::widevine::DeviceCertificateStatusListRequest;
|
|
using ::widevine::SignedDeviceInfo;
|
|
using ::widevine::SignedDeviceInfoRequest;
|
|
|
|
namespace widevine {
|
|
|
|
namespace {
|
|
const char kSignedListTerminator[] = "}";
|
|
const char kSignedList[] = "signedList\":";
|
|
const std::size_t kSignedListLen = strlen(kSignedList);
|
|
} // namespace
|
|
|
|
DeviceStatusList* DeviceStatusList::Instance() {
|
|
// TODO(user): This is "ok" according to Google's Coding for Dummies, but
|
|
// we should inject the status list into the sessions. This will require
|
|
// exposing additional objects in the public interface.
|
|
static DeviceStatusList* device_status_list(nullptr);
|
|
if (!device_status_list) device_status_list = new DeviceStatusList;
|
|
return device_status_list;
|
|
}
|
|
|
|
DeviceStatusList::DeviceStatusList() {}
|
|
|
|
DeviceStatusList::~DeviceStatusList() {}
|
|
|
|
Status DeviceStatusList::UpdateStatusList(
|
|
const std::string& root_certificate_public_key,
|
|
const std::string& serialized_device_certificate_status_list,
|
|
HashAlgorithm hash_algorithm, const std::string& signature,
|
|
uint32_t expiration_period_seconds) {
|
|
if (serialized_device_certificate_status_list.empty()) {
|
|
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
|
"missing-status-list");
|
|
}
|
|
if (signature.empty()) {
|
|
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
|
"missing-status-list-signature");
|
|
}
|
|
std::unique_ptr<RsaPublicKey> root_key(
|
|
RsaPublicKey::Create(root_certificate_public_key));
|
|
if (root_key == nullptr) {
|
|
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
|
"invalid-root-public-key");
|
|
}
|
|
if (!root_key->VerifySignature(serialized_device_certificate_status_list,
|
|
hash_algorithm, signature)) {
|
|
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
|
"invalid-status-list-signature");
|
|
}
|
|
DeviceCertificateStatusList certificate_status_list;
|
|
if (!certificate_status_list.ParseFromString(
|
|
serialized_device_certificate_status_list)) {
|
|
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
|
"signed-certificate-status-list-parse-error");
|
|
}
|
|
if (expiration_period_seconds &&
|
|
(GetCurrentTime() > (certificate_status_list.creation_time_seconds() +
|
|
expiration_period_seconds))) {
|
|
return Status(error_space, EXPIRED_CERTIFICATE_STATUS_LIST,
|
|
"certificate-status-list-expired");
|
|
}
|
|
|
|
absl::WriterMutexLock lock(&status_map_lock_);
|
|
device_status_map_.clear();
|
|
for (int i = 0, n = certificate_status_list.certificate_status_size(); i < n;
|
|
i++) {
|
|
const DeviceCertificateStatus& cert_status =
|
|
certificate_status_list.certificate_status(i);
|
|
if (cert_status.has_device_info()) {
|
|
const ProvisionedDeviceInfo& device_info = cert_status.device_info();
|
|
if (device_info.has_system_id()) {
|
|
device_status_map_[device_info.system_id()] = cert_status;
|
|
} else {
|
|
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
|
"device-info-missing-system-id");
|
|
}
|
|
}
|
|
}
|
|
creation_time_seconds_ = certificate_status_list.creation_time_seconds();
|
|
expiration_period_seconds_ = expiration_period_seconds;
|
|
return OkStatus();
|
|
}
|
|
|
|
Status DeviceStatusList::GetCertStatus(
|
|
const ClientCert& client_cert, const std::string& make,
|
|
const std::string& provider, bool allow_revoked_system_id,
|
|
DeviceCertificateStatus* device_certificate_status) {
|
|
CHECK(device_certificate_status);
|
|
absl::ReaderMutexLock lock(&status_map_lock_);
|
|
if (expiration_period_seconds_ &&
|
|
(GetCurrentTime() >
|
|
(creation_time_seconds_ + expiration_period_seconds_))) {
|
|
return Status(error_space, EXPIRED_CERTIFICATE_STATUS_LIST,
|
|
"certificate-status-list-expired");
|
|
}
|
|
DeviceCertificateStatus* device_cert_status =
|
|
gtl::FindOrNull(device_status_map_, client_cert.system_id());
|
|
|
|
if (device_cert_status == nullptr) {
|
|
if (allow_unknown_devices_ ||
|
|
client_cert.type() == ClientIdentification::KEYBOX) {
|
|
return OkStatus();
|
|
}
|
|
return client_cert.SystemIdUnknownError();
|
|
}
|
|
*device_certificate_status = *device_cert_status;
|
|
|
|
if (device_cert_status->status() == DeviceCertificateStatus::STATUS_REVOKED) {
|
|
if (IsRevokedSystemIdAllowed(client_cert.system_id()) ||
|
|
allow_revoked_system_id) {
|
|
LOG(WARNING) << "Allowing REVOKED device: "
|
|
<< device_cert_status->device_info().ShortDebugString();
|
|
} else {
|
|
return client_cert.SystemIdRevokedError();
|
|
}
|
|
}
|
|
|
|
// The remainder of this function is for DRM certificates.
|
|
if (client_cert.type() == ClientIdentification::KEYBOX) {
|
|
return OkStatus();
|
|
}
|
|
// DRM certificate checks.
|
|
if (client_cert.type() != ClientIdentification::DRM_DEVICE_CERTIFICATE) {
|
|
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
|
"device-certificate-unsupported-token-type");
|
|
}
|
|
if ((device_cert_status->status() ==
|
|
DeviceCertificateStatus::STATUS_TEST_ONLY) &&
|
|
!allow_test_only_devices_) {
|
|
if (IsTestOnlyDeviceAllowedByMake(client_cert.system_id(), make) &&
|
|
IsTestOnlyDeviceAllowedByProvider(client_cert.system_id(), provider)) {
|
|
LOG(WARNING) << "Allowing TEST_ONLY device with systemId = "
|
|
<< client_cert.system_id() << ", make = " << make
|
|
<< ", provider = " << provider << ", device info = "
|
|
<< device_cert_status->device_info().ShortDebugString();
|
|
} else {
|
|
VLOG(2) << "Not allowing TEST ONLY device with systemId = "
|
|
<< client_cert.system_id() << ", provider = " << provider
|
|
<< ", device info = "
|
|
<< device_cert_status->device_info().ShortDebugString();
|
|
return Status(error_space, DEVELOPMENT_CERTIFICATE_NOT_ALLOWED,
|
|
"test-only-drm-certificate-not-allowed");
|
|
}
|
|
}
|
|
if (!client_cert.signed_by_provisioner() &&
|
|
(client_cert.signer_serial_number() !=
|
|
device_cert_status->drm_serial_number())) {
|
|
// Widevine-provisioned device, and the intermediate certificate serial
|
|
// number does not match that in the status list. If the status list is
|
|
// newer than the certificate, indicate an invalid certificate, so that
|
|
// the device re-provisions. If, on the other hand, the certificate status
|
|
// list is older than the certificate, the certificate is for all purposes
|
|
// unknown.
|
|
if (client_cert.signer_creation_time_seconds() < creation_time_seconds_) {
|
|
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
|
"intermediate-certificate-serial-number-mismatch");
|
|
}
|
|
return client_cert.SystemIdUnknownError();
|
|
}
|
|
return OkStatus();
|
|
}
|
|
|
|
bool DeviceStatusList::GetDeviceInfo(const ClientCert& client_cert,
|
|
ProvisionedDeviceInfo* device_info) {
|
|
CHECK(device_info);
|
|
absl::ReaderMutexLock lock(&status_map_lock_);
|
|
DeviceCertificateStatus* device_cert_status =
|
|
gtl::FindOrNull(device_status_map_, client_cert.system_id());
|
|
if (device_cert_status != nullptr) {
|
|
*device_info = device_cert_status->device_info();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool DeviceStatusList::GetRevokedIdentifiers(
|
|
uint32_t system_id,
|
|
DeviceCertificateStatus::RevokedIdentifiers* revoked_identifiers) {
|
|
CHECK(revoked_identifiers);
|
|
absl::ReaderMutexLock lock(&status_map_lock_);
|
|
DeviceCertificateStatus* device_cert_status =
|
|
gtl::FindOrNull(device_status_map_, system_id);
|
|
if (device_cert_status) {
|
|
*revoked_identifiers = device_cert_status->revoked_identifiers();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool DeviceStatusList::IsSystemIdActive(uint32_t system_id) {
|
|
absl::ReaderMutexLock lock(&status_map_lock_);
|
|
DeviceCertificateStatus* device_cert_status =
|
|
gtl::FindOrNull(device_status_map_, system_id);
|
|
if (!device_cert_status) {
|
|
return allow_unknown_devices_ ||
|
|
KeyboxClientCert::IsSystemIdKnown(system_id);
|
|
}
|
|
if (device_cert_status->status() ==
|
|
DeviceCertificateStatus::STATUS_TEST_ONLY) {
|
|
return allow_test_only_devices_;
|
|
}
|
|
if (device_cert_status) {
|
|
ProvisionedDeviceInfo device_info = device_cert_status->device_info();
|
|
if (device_cert_status->status() ==
|
|
DeviceCertificateStatus::STATUS_REVOKED) {
|
|
if (IsRevokedSystemIdAllowed(system_id)) {
|
|
LOG(WARNING) << "REVOKED system_id: " << system_id
|
|
<< " is allowed to be active";
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return device_cert_status->status() !=
|
|
DeviceCertificateStatus::STATUS_REVOKED;
|
|
}
|
|
|
|
uint32_t DeviceStatusList::GetCurrentTime() const { return time(nullptr); }
|
|
|
|
void DeviceStatusList::AllowRevokedDevices(const std::string& system_id_list) {
|
|
for (absl::string_view sp : absl::StrSplit(system_id_list, ',')) {
|
|
allowed_revoked_devices_.push_back(std::stoi(std::string(sp)));
|
|
}
|
|
std::sort(allowed_revoked_devices_.begin(), allowed_revoked_devices_.end());
|
|
}
|
|
|
|
void DeviceStatusList::AllowTestOnlyDevicesByMake(
|
|
const std::string& device_list_by_make) {
|
|
absl::WriterMutexLock lock(&allowed_test_only_devices_mutex_);
|
|
if (device_list_by_make.empty()) {
|
|
allowed_test_only_devices_by_make_.clear();
|
|
return;
|
|
}
|
|
for (absl::string_view device : absl::StrSplit(device_list_by_make, ',')) {
|
|
const std::pair<absl::string_view, absl::string_view> device_split =
|
|
absl::StrSplit(device, ':');
|
|
if (device_split.second.empty() || device_split.second == "*") {
|
|
allowed_test_only_devices_by_make_.emplace(
|
|
std::stoi(std::string(device_split.first)), "*");
|
|
VLOG(2) << "Allowing TEST_ONLY device: systemId = "
|
|
<< std::stoi(std::string(device_split.first)) << ", make *";
|
|
} else {
|
|
allowed_test_only_devices_by_make_.emplace(
|
|
std::stoi(std::string(device_split.first)),
|
|
absl::AsciiStrToUpper(device_split.second));
|
|
VLOG(2) << "Allowing TEST_ONLY device: systemId = "
|
|
<< std::stoi(std::string(device_split.first))
|
|
<< ", make = " << absl::AsciiStrToUpper(device_split.second);
|
|
}
|
|
}
|
|
}
|
|
|
|
void DeviceStatusList::AllowTestOnlyDevicesByProvider(
|
|
const std::string& device_list_by_provider) {
|
|
absl::WriterMutexLock lock(&allowed_test_only_devices_mutex_);
|
|
if (device_list_by_provider.empty()) {
|
|
allowed_test_only_devices_by_provider_.clear();
|
|
return;
|
|
}
|
|
for (absl::string_view device :
|
|
absl::StrSplit(device_list_by_provider, ',')) {
|
|
const std::pair<absl::string_view, absl::string_view> device_split =
|
|
absl::StrSplit(device, ':');
|
|
if (device_split.second.empty() || device_split.second == "*") {
|
|
allowed_test_only_devices_by_provider_.emplace(
|
|
std::stoi(std::string(device_split.first)), "*");
|
|
VLOG(2) << "Allowing TEST_ONLY device: systemId = "
|
|
<< std::stoi(std::string(device_split.first)) << ", provider *";
|
|
} else {
|
|
allowed_test_only_devices_by_provider_.emplace(
|
|
std::stoi(std::string(device_split.first)),
|
|
absl::AsciiStrToUpper(device_split.second));
|
|
VLOG(2) << "Allowing TEST_ONLY device: systemId = "
|
|
<< std::stoi(std::string(device_split.first))
|
|
<< ", provider = " << absl::AsciiStrToUpper(device_split.second);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool DeviceStatusList::IsRevokedSystemIdAllowed(uint32_t system_id) {
|
|
auto it = std::binary_search(allowed_revoked_devices_.begin(),
|
|
allowed_revoked_devices_.end(), system_id);
|
|
return it;
|
|
}
|
|
|
|
bool DeviceStatusList::IsTestOnlyDeviceAllowedByMake(
|
|
uint32_t system_id, const std::string& manufacturer) {
|
|
absl::ReaderMutexLock lock(&allowed_test_only_devices_mutex_);
|
|
std::pair<std::multimap<uint32_t, std::string>::iterator,
|
|
std::multimap<uint32_t, std::string>::iterator>
|
|
allowed_makes = allowed_test_only_devices_by_make_.equal_range(system_id);
|
|
for (auto it = allowed_makes.first; it != allowed_makes.second; ++it) {
|
|
std::string allowed_makes = (*it).second;
|
|
if (allowed_makes == "*" ||
|
|
allowed_makes == absl::AsciiStrToUpper(manufacturer)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool DeviceStatusList::IsTestOnlyDeviceAllowedByProvider(
|
|
uint32_t system_id, const std::string& provider) {
|
|
absl::ReaderMutexLock lock(&allowed_test_only_devices_mutex_);
|
|
std::pair<std::multimap<uint32_t, std::string>::iterator,
|
|
std::multimap<uint32_t, std::string>::iterator>
|
|
allowed_providers =
|
|
allowed_test_only_devices_by_provider_.equal_range(system_id);
|
|
for (auto it = allowed_providers.first; it != allowed_providers.second;
|
|
++it) {
|
|
std::string allowed_provider = (*it).second;
|
|
if (allowed_provider == "*" ||
|
|
allowed_provider == absl::AsciiStrToUpper(provider)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
Status DeviceStatusList::DetermineAndDeserializeServiceResponse(
|
|
const std::string& service_response,
|
|
DeviceCertificateStatusList* certificate_status_list,
|
|
std::string* serialized_certificate_status_list,
|
|
HashAlgorithm* hash_algorithm, std::string* signature) {
|
|
if (certificate_status_list == nullptr) {
|
|
return Status(error_space, error::INVALID_ARGUMENT,
|
|
"certificate_status_list is empty");
|
|
} else if (serialized_certificate_status_list == nullptr) {
|
|
return Status(error_space, error::INVALID_ARGUMENT,
|
|
"serialized_certificate_status_list is empty");
|
|
} else if (signature == nullptr) {
|
|
return Status(error_space, error::INVALID_ARGUMENT, "signature is empty");
|
|
}
|
|
|
|
// We support three types of payload parsing. The legacy path checks for a
|
|
// JSON encoded payload as well as just a plain base64 (web safe or normal)
|
|
// payload. If that doesn't match, then the method will try to parse the
|
|
// serialized PublishedDeviceInfo proto.
|
|
Status status = ExtractPublishedDevicesInfo(
|
|
service_response, serialized_certificate_status_list, hash_algorithm,
|
|
signature);
|
|
|
|
// If the payload was not correctly parsed as a PublishedDevices proto.
|
|
// then attempt to parse it as a legacy payload.
|
|
if (!status.ok()) {
|
|
status = ExtractLegacyDeviceList(service_response,
|
|
serialized_certificate_status_list,
|
|
hash_algorithm, signature);
|
|
// The payload could not be parsed in either format, return the failure
|
|
// information.
|
|
if (!status.ok()) {
|
|
return status;
|
|
}
|
|
}
|
|
|
|
if (!certificate_status_list->ParseFromString(
|
|
*serialized_certificate_status_list)) {
|
|
return Status(error_space, widevine::INVALID_CERTIFICATE_STATUS_LIST,
|
|
"certificate-status-list-parse-error");
|
|
}
|
|
return OkStatus();
|
|
}
|
|
|
|
Status DeviceStatusList::ExtractLegacyDeviceList(
|
|
const std::string& raw_certificate_provisioning_service_response,
|
|
std::string* serialized_certificate_status_list,
|
|
HashAlgorithm* hash_algorithm, std::string* signature) {
|
|
// First, attempt to extract the legacy JSON response. Example legacy format.
|
|
// "signedList":"<b64 encoded data>"
|
|
// where the b64 encoded data is a DeviceCertificateStatusListResponse.
|
|
size_t b64_list_response_start =
|
|
raw_certificate_provisioning_service_response.find(kSignedList);
|
|
std::string serialized_signed_certificate_status_list;
|
|
if (b64_list_response_start != std::string::npos) {
|
|
size_t b64_list_response_end =
|
|
raw_certificate_provisioning_service_response.find(
|
|
kSignedListTerminator, b64_list_response_start);
|
|
if (b64_list_response_end == std::string::npos) {
|
|
return Status(
|
|
error_space, error::INVALID_ARGUMENT,
|
|
"Unable to parse the certificate_provisioning_service_response. "
|
|
"SignedList not terminated.");
|
|
}
|
|
std::string signed_list(
|
|
raw_certificate_provisioning_service_response.begin() +
|
|
b64_list_response_start + kSignedListLen,
|
|
raw_certificate_provisioning_service_response.begin() +
|
|
b64_list_response_end);
|
|
|
|
// Strip off quotes.
|
|
signed_list.erase(std::remove(signed_list.begin(), signed_list.end(), '\"'),
|
|
signed_list.end());
|
|
// Strip off spaces.
|
|
signed_list.erase(std::remove(signed_list.begin(), signed_list.end(), ' '),
|
|
signed_list.end());
|
|
|
|
// Strip off newlines.
|
|
signed_list.erase(std::remove(signed_list.begin(), signed_list.end(), '\n'),
|
|
signed_list.end());
|
|
|
|
// Strip off carriage return (the control-M character)
|
|
signed_list.erase(std::remove(signed_list.begin(), signed_list.end(), '\r'),
|
|
signed_list.end());
|
|
if (!absl::WebSafeBase64Unescape(
|
|
signed_list, &serialized_signed_certificate_status_list)) {
|
|
if (!absl::Base64Unescape(signed_list,
|
|
&serialized_signed_certificate_status_list)) {
|
|
return Status(error_space, error::INVALID_ARGUMENT,
|
|
"Base64 decode of signedlist failed.");
|
|
}
|
|
}
|
|
} else {
|
|
// If this was not a legacy JSON response, attempt to deserialize the base64
|
|
// response.
|
|
if (!absl::WebSafeBase64Unescape(
|
|
raw_certificate_provisioning_service_response,
|
|
&serialized_signed_certificate_status_list)) {
|
|
if (!absl::Base64Unescape(raw_certificate_provisioning_service_response,
|
|
&serialized_signed_certificate_status_list)) {
|
|
return Status(error_space, error::INVALID_ARGUMENT,
|
|
"Base64 decode of certList failed.");
|
|
}
|
|
}
|
|
}
|
|
|
|
// Attempt to parse the legacy serialized signed status list into the proto
|
|
// and extract the serialized status list and signature.
|
|
return ParseLegacySignedDeviceCertificateStatusList(
|
|
serialized_signed_certificate_status_list,
|
|
serialized_certificate_status_list, hash_algorithm, signature);
|
|
}
|
|
|
|
Status DeviceStatusList::ExtractPublishedDevicesInfo(
|
|
const std::string& serialized_published_devices,
|
|
std::string* serialized_certificate_status_list,
|
|
HashAlgorithm* hash_algorithm, std::string* signature) {
|
|
// TODO(b/139067045): Change from using the SignedDeviceInfo proto
|
|
// to using the correct proto from the API. This duplicate, wire-compatible
|
|
// proto was a temporary way to workaround Proto2/Proto3 compatibility issues.
|
|
SignedDeviceInfo devices_info;
|
|
if (!devices_info.ParseFromString(serialized_published_devices)) {
|
|
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
|
"published-devices-info-parse-error");
|
|
}
|
|
*serialized_certificate_status_list =
|
|
devices_info.device_certificate_status_list();
|
|
*hash_algorithm = HashAlgorithmProtoToEnum(devices_info.hash_algorithm());
|
|
*signature = devices_info.signature();
|
|
return OkStatus();
|
|
}
|
|
|
|
Status DeviceStatusList::GenerateSignedDeviceCertificateStatusListRequest(
|
|
const std::string& version,
|
|
const std::string& serialized_service_certificate,
|
|
std::string* signed_device_certificate_status_list_request) {
|
|
if (version.empty()) {
|
|
return Status(error_space, error::INVALID_ARGUMENT, "SDK version is empty");
|
|
}
|
|
DCHECK(signed_device_certificate_status_list_request);
|
|
if (signed_device_certificate_status_list_request == nullptr) {
|
|
return Status(error_space, error::INVALID_ARGUMENT,
|
|
"Signed_device_certificate_status_list_request is empty");
|
|
}
|
|
// Construct SignedDeviceCertificateStatusListRequest.
|
|
DeviceCertificateStatusListRequest request;
|
|
request.set_sdk_version(version);
|
|
request.set_sdk_time_seconds(DeviceStatusList::Instance()->GetCurrentTime());
|
|
request.set_service_certificate(serialized_service_certificate);
|
|
std::string device_certificate_status_list_request;
|
|
request.SerializeToString(&device_certificate_status_list_request);
|
|
SignedDeviceInfoRequest signed_request;
|
|
signed_request.set_device_certificate_status_list_request(
|
|
device_certificate_status_list_request);
|
|
const DrmServiceCertificate* sc =
|
|
DrmServiceCertificate::GetDefaultDrmServiceCertificate();
|
|
if (sc == nullptr) {
|
|
signed_device_certificate_status_list_request->clear();
|
|
return Status(error_space, widevine::SERVICE_CERTIFICATE_NOT_FOUND,
|
|
"Drm service certificate is not loaded.");
|
|
}
|
|
const RsaPrivateKey* private_key = sc->private_key();
|
|
if (private_key == nullptr) {
|
|
return Status(error_space, widevine::INVALID_SERVICE_PRIVATE_KEY,
|
|
"Private key in the service certificate is null.");
|
|
}
|
|
std::string signature;
|
|
private_key->GenerateSignature(
|
|
device_certificate_status_list_request,
|
|
HashAlgorithmProtoToEnum(signed_request.hash_algorithm()), &signature);
|
|
signed_request.set_signature(signature);
|
|
signed_request.SerializeToString(
|
|
signed_device_certificate_status_list_request);
|
|
return OkStatus();
|
|
}
|
|
|
|
Status DeviceStatusList::ParseLegacySignedDeviceCertificateStatusList(
|
|
const std::string& serialized_signed_device_certificate_status_list,
|
|
std::string* serialized_device_certificate_status_list,
|
|
HashAlgorithm* hash_algorithm, std::string* signature) {
|
|
// Parse the serialized_signed_device_certificate_status_list to extract the
|
|
// serialized_device_certificate_status_list
|
|
SignedDeviceCertificateStatusList signed_device_list;
|
|
if (!signed_device_list.ParseFromString(
|
|
serialized_signed_device_certificate_status_list)) {
|
|
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
|
"signed-certificate-status-list-parse-error");
|
|
}
|
|
if (signed_device_list.certificate_status_list().empty()) {
|
|
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
|
"missing-status-list");
|
|
}
|
|
if (signed_device_list.signature().empty()) {
|
|
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
|
"missing-status-list-signature");
|
|
}
|
|
*serialized_device_certificate_status_list =
|
|
signed_device_list.certificate_status_list();
|
|
*hash_algorithm =
|
|
HashAlgorithmProtoToEnum(signed_device_list.hash_algorithm());
|
|
*signature = signed_device_list.signature();
|
|
return OkStatus();
|
|
}
|
|
|
|
void DeviceStatusList::RevokedDrmCertificateSerialNumbers(
|
|
const std::string& drm_certificate_serial_numbers) {
|
|
for (absl::string_view drm_certificate_serial_number :
|
|
absl::StrSplit(drm_certificate_serial_numbers, ',')) {
|
|
revoked_drm_certificate_serial_numbers_.insert(
|
|
std::string(drm_certificate_serial_number));
|
|
}
|
|
}
|
|
|
|
bool DeviceStatusList::IsDrmCertificateRevoked(
|
|
const std::string& device_certificate_serial_number) {
|
|
if (revoked_drm_certificate_serial_numbers_.find(
|
|
device_certificate_serial_number) !=
|
|
revoked_drm_certificate_serial_numbers_.end()) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
Status DeviceStatusList::GetDeviceCertificateStatusBySystemId(
|
|
uint32_t system_id, DeviceCertificateStatus* device_certificate_status) {
|
|
absl::ReaderMutexLock lock(&status_map_lock_);
|
|
if (expiration_period_seconds_ &&
|
|
(GetCurrentTime() >
|
|
(creation_time_seconds_ + expiration_period_seconds_))) {
|
|
return Status(error_space, EXPIRED_CERTIFICATE_STATUS_LIST,
|
|
"certificate-status-list-expired");
|
|
}
|
|
DeviceCertificateStatus* device_cert_status =
|
|
gtl::FindOrNull(device_status_map_, system_id);
|
|
if (device_cert_status == nullptr) {
|
|
return Status(error_space, DRM_DEVICE_CERTIFICATE_UNKNOWN,
|
|
"device-certificate-status-unknown");
|
|
} else {
|
|
*device_certificate_status = *device_cert_status;
|
|
}
|
|
return OkStatus();
|
|
}
|
|
|
|
} // namespace widevine
|