Source release 19.2.0

This commit is contained in:
Alex Dale
2024-06-25 14:03:53 -07:00
parent b8bdfccebe
commit cd8256726f
89 changed files with 2747 additions and 35949 deletions

View File

@@ -81,9 +81,7 @@ CdmSession::CdmSession(wvutil::FileSystem* file_system,
security_level_(kSecurityLevelUninitialized),
requested_security_level_(kLevelDefault),
is_initial_usage_update_(true),
is_usage_update_needed_(false),
mock_license_parser_in_use_(false),
mock_policy_engine_in_use_(false) {
is_usage_update_needed_(false) {
assert(metrics_); // metrics_ must not be null.
crypto_metrics_ = metrics_->GetCryptoMetrics();
crypto_session_.reset(CryptoSession::MakeCryptoSession(crypto_metrics_));
@@ -263,8 +261,11 @@ CdmResponseType CdmSession::RestoreOfflineSession(const CdmKeySetId& key_set_id,
usage_entry_ = std::move(license_data.usage_entry);
usage_entry_index_ = license_data.usage_entry_index;
CdmResponseType result = LoadPrivateOrLegacyKey(
license_data.drm_certificate, license_data.wrapped_private_key);
CdmResponseType result = crypto_session_->MarkOfflineSession();
if (result != NO_ERROR) return result;
result = LoadPrivateOrLegacyKey(license_data.drm_certificate,
license_data.wrapped_private_key);
if (result != NO_ERROR) return result;
// Attempts to restore a released offline license are treated as a release
@@ -866,18 +867,10 @@ CdmResponseType CdmSession::DeleteUsageEntry(uint32_t usage_entry_index) {
// The usage entry cannot be deleted if it has a crypto session handling
// it, so close and reopen session.
UpdateUsageEntryInformation();
CdmResponseType sts;
crypto_session_->Close();
crypto_session_.reset(CryptoSession::MakeCryptoSession(crypto_metrics_));
M_TIME(sts = crypto_session_->Open(requested_security_level_),
crypto_metrics_, crypto_session_open_, sts, requested_security_level_);
if (sts != NO_ERROR) return sts;
usage_table_ = nullptr;
bool has_support = false;
if (crypto_session_->HasUsageTableSupport(&has_support) && has_support) {
usage_table_ = crypto_session_->GetUsageTable();
}
crypto_session_->Close();
CdmResponseType sts = ResetCryptoSession();
if (sts != NO_ERROR) return sts;
if (usage_table_ == nullptr) {
LOGE("Usage table header unavailable");
@@ -1012,14 +1005,8 @@ bool CdmSession::StoreLicense(CdmOfflineLicenseState state, int* error_detail) {
}
CdmResponseType CdmSession::RemoveKeys() {
CdmResponseType sts;
crypto_session_.reset(CryptoSession::MakeCryptoSession(crypto_metrics_));
// Ignore errors
M_TIME(sts = crypto_session_->Open(requested_security_level_),
crypto_metrics_, crypto_session_open_, sts, requested_security_level_);
policy_engine_.reset(
new PolicyEngine(session_id_, nullptr, crypto_session_.get()));
return CdmResponseType(NO_ERROR);
crypto_session_->Close();
return ResetCryptoSession();
}
CdmResponseType CdmSession::RemoveLicense() {
@@ -1313,6 +1300,77 @@ CdmResponseType CdmSession::GenerateRsaSignature(const std::string& message,
return crypto_session_->GenerateRsaSignature(message, signature, scheme);
}
CdmResponseType CdmSession::ResetCryptoSession() {
LOGD("Resetting crypto session: session_id = %s, ksid = %s",
IdToString(session_id_), IdToString(key_set_id_));
if (mock_crypto_session_in_use_) {
// If the crypto session is not reset, then there is nothing to do.
return CdmResponseType(NO_ERROR);
}
CdmResponseType sts;
crypto_session_.reset(CryptoSession::MakeCryptoSession(crypto_metrics_));
usage_table_ = nullptr;
M_TIME(sts = crypto_session_->Open(requested_security_level_),
crypto_metrics_, crypto_session_open_, sts, requested_security_level_);
CdmResponseType final_sts(NO_ERROR);
if (sts != NO_ERROR) {
// Challenging case, still need to reset other components.
LOGE("Failed to open crypto session: sts = %s", sts.ToString().c_str());
final_sts = sts;
} else {
// Reset all component dependent on the crypto session.
security_level_ = crypto_session_->GetSecurityLevel();
crypto_metrics_->crypto_session_security_level_.Record(security_level_);
if (!file_handle_->Init(security_level_)) {
LOGE("Unable to initialize file handle");
final_sts = CdmResponseType(SESSION_FILE_HANDLE_INIT_ERROR);
}
if (!file_handle_->HasCertificate(atsc_mode_enabled_)) {
LOGE("Missing certificate: atsc_mode_enabled = %s",
BoolToString(atsc_mode_enabled_));
final_sts = CdmResponseType(NEED_PROVISIONING);
}
bool has_support = false;
if (crypto_session_->HasUsageTableSupport(&has_support) && has_support) {
usage_table_ = crypto_session_->GetUsageTable();
}
}
// Even if the session is not open, other members need new session pointer.
if (mock_policy_engine_in_use_) {
// Simply pass the new pointer.
policy_engine_->set_crypto_session(crypto_session_.get());
} else {
// Attempt to maintain event listener.
WvCdmEventListener* event_listener =
policy_engine_ ? policy_engine_->event_listener() : nullptr;
policy_engine_.reset(
new PolicyEngine(session_id_, event_listener, crypto_session_.get()));
}
if (mock_license_parser_in_use_) {
license_parser_->set_crypto_session(crypto_session_.get());
license_parser_->set_policy_engine(policy_engine_.get());
} else {
license_parser_.reset(new CdmLicense(session_id_));
std::string service_certificate;
if (!Properties::GetServiceCertificate(session_id_, &service_certificate))
service_certificate.clear();
if (!license_parser_->Init(Properties::UsePrivacyMode(session_id_),
service_certificate, crypto_session_.get(),
policy_engine_.get())) {
LOGE("Failed to initialize license parser");
final_sts = CdmResponseType(LICENSE_PARSER_INIT_ERROR);
}
}
return final_sts;
}
// For testing only - takes ownership of pointers
void CdmSession::set_license_parser(CdmLicense* license_parser) {
@@ -1322,6 +1380,7 @@ void CdmSession::set_license_parser(CdmLicense* license_parser) {
void CdmSession::set_crypto_session(CryptoSession* crypto_session) {
crypto_session_.reset(crypto_session);
mock_crypto_session_in_use_ = true;
}
void CdmSession::set_policy_engine(PolicyEngine* policy_engine) {

View File

@@ -658,7 +658,7 @@ CdmResponseType CryptoSession::GetProvisioningToken(
additional_token);
} else if (pre_provision_token_type_ ==
kClientTokenDrmCertificateReprovisioning) {
status = GetTokenFromEmbeddedCertificate(token);
status = GetTokenFromEmbeddedCertificate(requested_security_level, token);
}
metrics_->crypto_session_get_token_.Increment(status);
return status;
@@ -970,6 +970,15 @@ CdmResponseType CryptoSession::Open(
return CdmResponseType(NO_ERROR);
}
CdmResponseType CryptoSession::MarkOfflineSession() {
OEMCryptoResult sts;
WithOecSessionLock("MarkOfflineSession", [&] {
sts = OEMCrypto_MarkOfflineSession(oec_session_id_);
});
if (sts == OEMCrypto_ERROR_NOT_IMPLEMENTED) sts = OEMCrypto_SUCCESS;
return MapOEMCryptoResult(sts, LOAD_KEY_ERROR, "MarkOfflineSession");
}
void CryptoSession::Close() {
LOGV("Closing crypto session: id = %u, open = %s", oec_session_id_,
open_ ? "true" : "false");
@@ -1244,7 +1253,7 @@ CdmResponseType CryptoSession::PrepareAndSignProvisioningRequest(
}
OEMCryptoResult sts;
// TODO: b/305093063 - Refactor to a switch statement to improve readability
// TODO: b/330770643 - Refactor to a switch statement to improve readability
if (pre_provision_token_type_ == kClientTokenKeybox) {
should_specify_algorithm = false;
} else if (pre_provision_token_type_ == kClientTokenOemCert) {
@@ -1437,19 +1446,19 @@ CdmResponseType CryptoSession::GetBootCertificateChain(
}
CdmResponseType CryptoSession::GetTokenFromEmbeddedCertificate(
std::string* token) {
RequestedSecurityLevel requested_security_level, std::string* token) {
RETURN_IF_UNINITIALIZED(CRYPTO_SESSION_NOT_INITIALIZED);
RETURN_IF_NULL(token, PARAMETER_NULL);
token->clear();
CdmClientTokenType token_type = kClientTokenUninitialized;
const CdmResponseType sts =
GetProvisioningMethod(requested_security_level_, &token_type);
GetProvisioningMethod(requested_security_level, &token_type);
if (sts != NO_ERROR) {
LOGE("Failed to get token type");
return sts;
}
if (token_type != kClientTokenDrmCertificateReprovisioning) {
token->clear();
return CdmResponseType(NO_ERROR);
}
@@ -1458,7 +1467,7 @@ CdmResponseType CryptoSession::GetTokenFromEmbeddedCertificate(
OEMCryptoResult status =
WithOecReadLock("GetTokenFromEmbeddedCertificate - attempt 1", [&] {
return OEMCrypto_GetEmbeddedDrmCertificate(
reinterpret_cast<uint8_t*>(&embedded_certificate.front()),
MutableStringDataPointer(&embedded_certificate),
&certificate_length);
});
@@ -1467,26 +1476,27 @@ CdmResponseType CryptoSession::GetTokenFromEmbeddedCertificate(
status =
WithOecReadLock("GetTokenFromEmbeddedCertificate - attempt 2", [&] {
return OEMCrypto_GetEmbeddedDrmCertificate(
reinterpret_cast<uint8_t*>(&embedded_certificate.front()),
MutableStringDataPointer(&embedded_certificate),
&certificate_length);
});
}
if (status == OEMCrypto_SUCCESS) {
// TODO: b/312782308 - Store embedded certificate as SignedDrmCertificate
video_widevine_client::sdk::HashedFile hashed_file;
video_widevine_client::sdk::File file;
embedded_certificate.resize(certificate_length);
if (hashed_file.ParseFromString(embedded_certificate) &&
file.ParseFromString(hashed_file.file())) {
*token = file.device_certificate().certificate();
return CdmResponseType(NO_ERROR);
}
if (status != OEMCrypto_SUCCESS) {
return MapOEMCryptoResult(status, GET_TOKEN_FROM_EMBEDDED_CERT_ERROR,
"GetTokenFromEmbeddedCertificate");
}
embedded_certificate.resize(certificate_length);
token->clear();
return MapOEMCryptoResult(status, GET_TOKEN_FROM_EMBEDDED_CERT_ERROR,
"GetTokenFromEmbeddedCertificate");
// TODO: b/312782308 - Store embedded certificate as SignedDrmCertificate
video_widevine_client::sdk::HashedFile hashed_file;
video_widevine_client::sdk::File file;
if (!hashed_file.ParseFromString(embedded_certificate) ||
!file.ParseFromString(hashed_file.file())) {
LOGE("Failed to parse embedded certificate");
return CdmResponseType(GET_TOKEN_FROM_EMBEDDED_CERT_ERROR);
}
*token = file.device_certificate().certificate();
return CdmResponseType(NO_ERROR);
}
CdmResponseType CryptoSession::GetDeviceInformation(
@@ -2995,7 +3005,12 @@ CdmResponseType CryptoSession::ShrinkUsageTableHeader(
});
if (result == OEMCrypto_ERROR_SHORT_BUFFER) {
usage_table_header->resize(usage_table_header_len);
// Don't shrink the buffer yet so OEMCrypto can see the existing data.
if (usage_table_header_len > usage_table_header->size()) {
usage_table_header->resize(usage_table_header_len);
} else {
usage_table_header_len = usage_table_header->size();
}
WithOecWriteLock("ShrinkUsageTableHeader Attempt 2", [&] {
result = OEMCrypto_ShrinkUsageTableHeader(
requested_security_level, new_entry_count,

View File

@@ -680,7 +680,7 @@ void InitializationData::DumpToLogs() const {
if (!is_supported()) {
LOGD("InitData: Not supported");
}
if (!IsEmpty()) {
if (IsEmpty()) {
LOGD("InitData: Empty");
}
std::string type_info = type();
@@ -736,6 +736,9 @@ void InitializationData::DumpToLogs() const {
LOGD("InitData: entitlement_key_id %d: %s -> %s", i,
wvutil::b2a_hex(key.entitlement_key_id()).c_str(),
wvutil::b2a_hex(key.key_id()).c_str());
LOGD("InitData: entitled_key %d: %s", i,
wvutil::b2a_hex(key.key()).c_str());
LOGD("InitData: iv %d: %s", i, wvutil::b2a_hex(key.iv()).c_str());
}
}

View File

@@ -195,9 +195,7 @@ std::vector<CryptoKey> ExtractContentKeys(
} // namespace
CdmLicense::CdmLicense(const CdmSessionId& session_id)
: crypto_session_(nullptr),
policy_engine_(nullptr),
session_id_(session_id),
: session_id_(session_id),
initialized_(false),
protocol_version_(video_widevine::VERSION_2_2),
renew_with_client_id_(false),
@@ -207,16 +205,18 @@ CdmLicense::CdmLicense(const CdmSessionId& session_id)
license_key_type_(kLicenseKeyTypeContent) {}
CdmLicense::CdmLicense(const CdmSessionId& session_id, wvutil::Clock* clock)
: crypto_session_(nullptr),
policy_engine_(nullptr),
session_id_(session_id),
: session_id_(session_id),
initialized_(false),
protocol_version_(video_widevine::VERSION_2_2),
renew_with_client_id_(false),
is_offline_(false),
use_privacy_mode_(false),
clock_(clock),
license_key_type_(kLicenseKeyTypeContent) {
clock_.reset(clock);
if (!clock_) {
LOGW("Input |clock| is null, using default");
clock_.reset(new wvutil::Clock());
}
}
CdmLicense::~CdmLicense() {}

View File

@@ -232,3 +232,7 @@ OEMCryptoResult OEMCrypto_GetBCCType(RequestedSecurityLevel level,
WEAK OEMCryptoResult OEMCrypto_UseSecondaryKey(OEMCrypto_SESSION, bool) {
return OEMCrypto_SUCCESS;
}
WEAK OEMCryptoResult OEMCrypto_MarkOfflineSession(OEMCrypto_SESSION) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}

View File

@@ -39,9 +39,12 @@ PolicyEngine::PolicyEngine(CdmSessionId session_id,
if (version >= kMinOemCryptoApiVersionSupportsRenewalDelayBase) {
policy_timers_.reset(new PolicyTimersV18());
}
} else {
LOGW("Failed to get API version: session_id = %s", IdToString(session_id_));
}
if (policy_timers_ == nullptr) {
if (!policy_timers_) {
// Use V16 policy timers if getting version failed.
policy_timers_.reset(new PolicyTimersV16());
}
InitDevice(crypto_session);

View File

@@ -254,6 +254,31 @@ bool PSSVerify(const uint8_t* message, size_t mLen,
return !memcmp(H, H2, hLen);
}
bool AesCryptCommon(CCOperation op, const std::string& in,
const std::string& iv, const std::string& key,
std::string* out, bool has_padding) {
assert(!in.empty());
assert(iv.size() == kCCBlockSizeAES128);
assert(out != nullptr);
assert(!key.empty());
std::string temp;
temp.resize(in.length() + kCCBlockSizeAES128);
size_t length;
CCCryptorStatus result = CCCrypt(
op, kCCAlgorithmAES128, has_padding ? kCCOptionPKCS7Padding : 0,
key.c_str(), key.length(), iv.c_str(), in.c_str(), in.length(),
&temp[0], temp.size(), &length);
if (result != kCCSuccess) {
LOGE("AesCbcKey::Encrypt: Encryption failure: %d", result);
return false;
}
out->assign(temp, 0, length);
return true;
}
} // namespace
namespace wvcdm {
@@ -269,27 +294,13 @@ bool AesCbcKey::Init(const std::string& key) {
}
bool AesCbcKey::Encrypt(const std::string& in, const std::string& iv,
std::string* out) {
assert(!in.empty());
assert(iv.size() == kCCBlockSizeAES128);
assert(out != nullptr);
assert(!key_.empty());
std::string* out, bool has_padding) {
return AesCryptCommon(kCCEncrypt, in, iv, key_, out, has_padding);
}
std::string temp;
temp.resize(in.length() + kCCBlockSizeAES128);
size_t length;
CCCryptorStatus result =
CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
key_.c_str(), key_.length(), iv.c_str(), in.c_str(), in.length(),
&temp[0], temp.size(), &length);
if (result != kCCSuccess) {
LOGE("AesCbcKey::Encrypt: Encryption failure: %d", result);
return false;
}
out->assign(temp, 0, length);
return true;
bool AesCbcKey::Decrypt(const std::string& in, const std::string& iv,
std::string* out, bool has_padding) {
return AesCryptCommon(kCCDecrypt, in, iv, key_, out, has_padding);
}
RsaPublicKey::RsaPublicKey() {}
@@ -385,6 +396,12 @@ std::string Md5Hash(const std::string& data) {
return hash;
}
std::string Sha1Hash(const std::string& data) {
std::string hash(CC_SHA_DIGEST_LENGTH, '\0');
CC_SHA1(data.data(), data.size(), reinterpret_cast<uint8_t*>(&hash[0]));
return hash;
}
std::string Sha256Hash(const std::string& data) {
std::string hash(CC_SHA256_DIGEST_LENGTH, '\0');
CC_SHA256(data.data(), data.size(), reinterpret_cast<uint8_t*>(&hash[0]));

View File

@@ -90,7 +90,7 @@ bool AesCbcKey::Init(const std::string& key) {
}
bool AesCbcKey::Encrypt(const std::string& in, const std::string& iv,
std::string* out) {
std::string* out, bool has_padding) {
if (in.empty()) {
LOGE("No cleartext provided");
return false;
@@ -117,6 +117,7 @@ bool AesCbcKey::Encrypt(const std::string& in, const std::string& iv,
EVP_CIPHER_CTX_free(evp_cipher_ctx);
return false;
}
if (!has_padding) EVP_CIPHER_CTX_set_padding(evp_cipher_ctx, 0);
out->resize(in.size() + AES_BLOCK_SIZE);
int out_length = static_cast<int>(out->size());
@@ -145,6 +146,63 @@ bool AesCbcKey::Encrypt(const std::string& in, const std::string& iv,
return true;
}
bool AesCbcKey::Decrypt(const std::string& in, const std::string& iv,
std::string* out, bool has_padding) {
if (in.empty()) {
LOGE("No ciphertext provided");
return false;
}
if (iv.size() != AES_BLOCK_SIZE) {
LOGE("Invalid IV size: %zu", iv.size());
return false;
}
if (out == nullptr) {
LOGE("Ciphertext output parameter |out| not provided");
return false;
}
if (key_.empty()) {
LOGE("AES key not initialized");
return false;
}
EVP_CIPHER_CTX* evp_cipher_ctx = EVP_CIPHER_CTX_new();
if (EVP_DecryptInit(evp_cipher_ctx, EVP_aes_128_cbc(),
reinterpret_cast<const uint8_t*>(&key_[0]),
reinterpret_cast<const uint8_t*>(&iv[0])) == 0) {
LOGE("AES CBC setup failure: %s",
ERR_error_string(ERR_get_error(), nullptr));
EVP_CIPHER_CTX_free(evp_cipher_ctx);
return false;
}
if (!has_padding) EVP_CIPHER_CTX_set_padding(evp_cipher_ctx, 0);
out->resize(in.size() + AES_BLOCK_SIZE);
int out_length = static_cast<int>(out->size());
if (EVP_DecryptUpdate(evp_cipher_ctx, reinterpret_cast<uint8_t*>(&(*out)[0]),
&out_length,
reinterpret_cast<const uint8_t*>(in.data()),
static_cast<int>(in.size())) == 0) {
LOGE("AES CBC encryption failure: %s",
ERR_error_string(ERR_get_error(), nullptr));
EVP_CIPHER_CTX_free(evp_cipher_ctx);
return false;
}
int padding = 0;
if (EVP_DecryptFinal_ex(evp_cipher_ctx,
reinterpret_cast<uint8_t*>(&(*out)[out_length]),
&padding) == 0) {
LOGE("PKCS7 padding failure: %s",
ERR_error_string(ERR_get_error(), nullptr));
EVP_CIPHER_CTX_free(evp_cipher_ctx);
return false;
}
EVP_CIPHER_CTX_free(evp_cipher_ctx);
out->resize(out_length + padding);
return true;
}
RsaPublicKey::RsaPublicKey() {}
RsaPublicKey::~RsaPublicKey() {}
@@ -410,6 +468,13 @@ std::string Md5Hash(const std::string& data) {
return hash;
}
std::string Sha1Hash(const std::string& data) {
std::string hash(SHA_DIGEST_LENGTH, '\0');
SHA1(reinterpret_cast<const uint8_t*>(data.data()), data.size(),
reinterpret_cast<uint8_t*>(&hash[0]));
return hash;
}
std::string Sha256Hash(const std::string& data) {
std::string hash(SHA256_DIGEST_LENGTH, '\0');
SHA256(reinterpret_cast<const uint8_t*>(data.data()), data.size(),

View File

@@ -11,6 +11,8 @@
#ifdef __APPLE__
# include <CommonCrypto/CommonDigest.h>
# define SHA1 CC_SHA1
# define SHA_DIGEST_LENGTH CC_SHA_DIGEST_LENGTH
# define SHA256 CC_SHA256
# define SHA256_DIGEST_LENGTH CC_SHA256_DIGEST_LENGTH
# define SHA512 CC_SHA512
@@ -32,7 +34,12 @@ AesCbcKey::~AesCbcKey() {}
bool AesCbcKey::Init(const std::string& key) { return false; }
bool AesCbcKey::Encrypt(const std::string& in, const std::string& iv,
std::string* out) {
std::string* out, bool has_padding) {
return false;
}
bool AesCbcKey::Decrypt(const std::string& in, const std::string& iv,
std::string* out, bool bool_padding) {
return false;
}
@@ -65,6 +72,12 @@ std::string Md5Hash(const std::string& data) {
return hash;
}
std::string Sha1Hash(const std::string& data) {
std::string hash(SHA_DIGEST_LENGTH, '\0');
SHA1(data.data(), data.size(), reinterpret_cast<uint8_t*>(&hash[0]));
return hash;
}
std::string Sha256Hash(const std::string& data) {
std::string hash(SHA256_DIGEST_LENGTH, '\0');
SHA256(data.data(), data.size(), reinterpret_cast<uint8_t*>(&hash[0]));

View File

@@ -7,6 +7,7 @@
#include "crypto_session.h"
#include "device_files.h"
#include "license_protocol.pb.h"
#include "privacy_crypto.h"
#include "wv_cdm_constants.h"
#include "wv_cdm_types.h"
@@ -18,16 +19,22 @@ constexpr size_t kKeyboxSystemIdOffset = 4;
// Index of certificate within cerificate chain which contains the
// system ID (0 = leaf/device cert, 1 = intermediate/device family cert).
constexpr size_t kOemCertSystemIdIndex = 1;
// OID of X.509 certificate extension containing the Widevine system ID.
// OID of X.509 TBSCertificate extension containing the Widevine
// system ID.
const char kWidevineSystemIdExtensionOid[] = "1.3.6.1.4.1.11129.4.1.1";
constexpr size_t kSystemIdLength = sizeof(uint32_t);
constexpr bool IsSupportedSecurityLevel(CdmSecurityLevel security_level) {
constexpr bool IsSupportedCdmSecurityLevel(CdmSecurityLevel security_level) {
return security_level == kSecurityLevelL1 ||
security_level == kSecurityLevelL2 ||
security_level == kSecurityLevelL3;
}
constexpr bool IsSupportedRequestedSecurityLevel(
RequestedSecurityLevel security_level) {
return security_level == kLevelDefault || security_level == kLevel3;
}
} // namespace
SystemIdExtractor::SystemIdExtractor(RequestedSecurityLevel security_level,
@@ -45,28 +52,35 @@ bool SystemIdExtractor::ExtractSystemId(uint32_t* system_id) {
LOGE("Output |system_id| is null");
return false;
}
if (crypto_session_->GetCachedSystemId(system_id)) {
if (!VerifySecurityLevelExpectations()) {
// VerifySecurityLevelExpectations() will log details.
return false;
}
if (crypto_session_->IsOpen() &&
crypto_session_->GetCachedSystemId(system_id)) {
return true;
}
CdmClientTokenType type = kClientTokenUninitialized;
const CdmResponseType status =
crypto_session_->GetProvisioningMethod(security_level_, &type);
if (status != NO_ERROR) {
LOGE("Failed to get provisioning method: security_level = %s, status = %d",
RequestedSecurityLevelToString(security_level_), status.ToInt());
LOGE("Failed to get provisioning method: security_level = %s, status = %s",
RequestedSecurityLevelToString(security_level_),
status.ToString().c_str());
return false;
}
bool success = false;
switch (type) {
case kClientTokenDrmCert:
// TODO: b/309675153 - Extract system id when using DRM reprovisioning.
case kClientTokenDrmCertificateReprovisioning:
LOGW(
"Cannot get a system ID from a DRM certificate, "
"using null system ID: security_level = %s",
RequestedSecurityLevelToString(security_level_));
*system_id = NULL_SYSTEM_ID;
return true;
case kClientTokenDrmCertificateReprovisioning:
success = ExtractSystemIdDrmReprovisioning(system_id);
break;
case kClientTokenKeybox:
success = ExtractSystemIdProv20(system_id);
break;
@@ -90,7 +104,10 @@ bool SystemIdExtractor::ExtractSystemId(uint32_t* system_id) {
// static
bool SystemIdExtractor::ExtractSystemIdFromKeyboxData(
const std::string& key_data, uint32_t* system_id) {
if (system_id == nullptr) return false;
if (system_id == nullptr) {
LOGE("Output |system_id| is null");
return false;
}
if (key_data.size() < (kKeyboxSystemIdOffset + kSystemIdLength)) {
LOGE("Keybox data does not contain system ID: key_data_size = %zu",
key_data.size());
@@ -106,7 +123,10 @@ bool SystemIdExtractor::ExtractSystemIdFromKeyboxData(
// static
bool SystemIdExtractor::ExtractSystemIdFromOemCert(const std::string& oem_cert,
uint32_t* system_id) {
if (system_id == nullptr) return false;
if (system_id == nullptr) {
LOGE("Output |system_id| is null");
return false;
}
return ExtractExtensionValueFromCertificate(oem_cert,
kWidevineSystemIdExtensionOid,
kOemCertSystemIdIndex, system_id);
@@ -122,8 +142,9 @@ bool SystemIdExtractor::ExtractSystemIdProv20(uint32_t* system_id) {
return true;
}
if (status != NO_ERROR) {
LOGE("Failed to get keybox data: security_level = %s, status = %d",
RequestedSecurityLevelToString(security_level_), status.ToInt());
LOGE("Failed to get keybox data: security_level = %s, status = %s",
RequestedSecurityLevelToString(security_level_),
status.ToString().c_str());
return false;
}
if (!ExtractSystemIdFromKeyboxData(key_data, system_id)) {
@@ -138,8 +159,9 @@ bool SystemIdExtractor::ExtractSystemIdProv30(uint32_t* system_id) {
const CdmResponseType status =
crypto_session_->GetTokenFromOemCert(security_level_, &oem_cert);
if (status != NO_ERROR) {
LOGE("Failed to get OEM certificate: security_level = %s, status = %d",
RequestedSecurityLevelToString(security_level_), status.ToInt());
LOGE("Failed to get OEM certificate: security_level = %s, status = %s",
RequestedSecurityLevelToString(security_level_),
status.ToString().c_str());
return false;
}
if (!ExtractSystemIdFromOemCert(oem_cert, system_id)) {
@@ -152,7 +174,7 @@ bool SystemIdExtractor::ExtractSystemIdProv30(uint32_t* system_id) {
bool SystemIdExtractor::ExtractSystemIdProv40(uint32_t* system_id) {
const CdmSecurityLevel security_level =
crypto_session_->GetSecurityLevel(security_level_);
if (!IsSupportedSecurityLevel(security_level)) {
if (!IsSupportedCdmSecurityLevel(security_level)) {
LOGE("Unsupported security level: %s",
CdmSecurityLevelToString(security_level));
return false;
@@ -160,7 +182,7 @@ bool SystemIdExtractor::ExtractSystemIdProv40(uint32_t* system_id) {
DeviceFiles real_device_files(fs_);
// Mock DeviceFiles for testing.
DeviceFiles& device_files =
(test_device_files_ ? *test_device_files_ : real_device_files);
(test_device_files_ != nullptr ? *test_device_files_ : real_device_files);
if (!device_files.Init(security_level)) {
LOGE("Failed to initialize device files: security_level = %s",
CdmSecurityLevelToString(security_level));
@@ -190,4 +212,74 @@ bool SystemIdExtractor::ExtractSystemIdProv40(uint32_t* system_id) {
}
return true;
}
bool SystemIdExtractor::ExtractSystemIdDrmReprovisioning(uint32_t* system_id) {
std::string token;
const CdmResponseType status =
crypto_session_->GetTokenFromEmbeddedCertificate(security_level_, &token);
if (status != NO_ERROR) {
LOGE("Failed to load embedded certificate token");
return false;
}
video_widevine::SignedDrmCertificate signed_certificate;
video_widevine::DrmCertificate drm_certificate;
if (!signed_certificate.ParseFromString(token) ||
!drm_certificate.ParseFromString(signed_certificate.drm_certificate())) {
LOGE("Failed to parse embedded certificate");
return false;
}
if (!drm_certificate.has_system_id()) {
LOGE("Failed to extract system_id from embedded certificate");
return false;
}
*system_id = drm_certificate.system_id();
return true;
}
bool SystemIdExtractor::VerifySecurityLevelExpectations() {
if (!IsSupportedRequestedSecurityLevel(security_level_)) {
LOGE("Unsupported requested extractor security level: %d",
static_cast<int>(security_level_));
return false;
}
if (!crypto_session_->IsOpen()) {
// Any other issues with the security level should be caught and
// handled by the respective extractor methods.
return true;
}
// The SystemIdExtractor is intended to work with unopened
// CryptoSessions, but does not restrict this.
// If the crypto session is open, it is already tied to a
// security level; for the extractor work as expected the
// session's security level must be the same as extractor's
// requested security level.
const CdmSecurityLevel session_security_level =
crypto_session_->GetSecurityLevel();
if (!IsSupportedCdmSecurityLevel(session_security_level)) {
LOGE("Failed to get session security level: %s",
CdmSecurityLevelToString(session_security_level));
return false;
}
const CdmSecurityLevel extractor_security_level =
crypto_session_->GetSecurityLevel(security_level_);
if (!IsSupportedCdmSecurityLevel(extractor_security_level)) {
LOGE("Failed to get extractor security level: %s",
CdmSecurityLevelToString(extractor_security_level));
return false;
}
if (session_security_level != extractor_security_level) {
LOGE(
"Extractor and session security levels do not match: "
"session_security_level = %s, extractor_security_level = %s, "
"requested_security_level = %s",
CdmSecurityLevelToString(session_security_level),
CdmSecurityLevelToString(extractor_security_level),
RequestedSecurityLevelToString(security_level_));
return false;
}
return true;
}
} // namespace wvcdm

View File

@@ -12,6 +12,8 @@ namespace wvcdm {
namespace {
const char kEmptyIdRep[] = "<empty>";
const char kNullIdRep[] = "<null>";
const char kFalseRep[] = "false";
const char kTrueRep[] = "true";
// Thread local buffer used by UnknownEnumValueToString() to represent
// unknown enum values.
@@ -896,6 +898,8 @@ const char* IdPtrToString(const std::string* id) {
return id->empty() ? kEmptyIdRep : id->c_str();
}
const char* BoolToString(bool value) { return value ? kTrueRep : kFalseRep; }
const char* OemCryptoResultToString(OEMCryptoResult result) {
switch (result) {
/* OEMCrypto return values */
@@ -1031,6 +1035,8 @@ const char* OemCryptoResultToString(OEMCryptoResult result) {
return "ERROR_INSUFFICIENT_PRIVILEGE";
case OEMCrypto_ERROR_INVALID_KEY:
return "ERROR_INVALID_KEY";
case OEMCrypto_ERROR_INVALID_OEM_CERTIFICATE:
return "OEMCrypto_ERROR_INVALID_OEM_CERTIFICATE";
/* ODK return values */
case ODK_ERROR_CORE_MESSAGE:
return "ERROR_CORE_MESSAGE";