Merge latest oemcrypto-v17 change

No-Typo-Check: Not related to this change.

Bug: 161477208
Change-Id: I99e4780f6855b7045aa0cd5a49c13d2d0d51ed64
This commit is contained in:
Kyle Zhang
2022-01-21 05:58:12 +00:00
committed by Fred Gylys-Colwell
parent c924960962
commit 642965c678
176 changed files with 301013 additions and 296749 deletions

View File

@@ -61,7 +61,7 @@ class UsagePropertySet : public CdmClientPropertySet {
const std::string empty_;
};
CdmEngine::CdmEngine(FileSystem* file_system,
CdmEngine::CdmEngine(wvutil::FileSystem* file_system,
std::shared_ptr<metrics::EngineMetrics> metrics)
: metrics_(metrics),
cert_provisioning_(),
@@ -182,11 +182,6 @@ CdmResponseType CdmEngine::OpenSession(const CdmKeySystem& key_system,
}
return sts;
}
if (sts != NO_ERROR) {
LOGE("Bad session init: status = %d", static_cast<int>(sts));
return sts;
}
const CdmSessionId id = new_session->session_id();
LOGI("New session: session_id = %s", IdToString(id));
@@ -344,7 +339,7 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
}
LOGD("key_request = (%zu) %s", key_request->message.size(),
wvcdm::Base64SafeEncode(key_request->message).c_str());
wvutil::Base64SafeEncode(key_request->message).c_str());
return KEY_MESSAGE;
}
@@ -382,7 +377,7 @@ CdmResponseType CdmEngine::AddKey(const CdmSessionId& session_id,
id = iter->second.first;
} else {
LOGD("key_data = (%zu) %s", key_data.size(),
wvcdm::Base64SafeEncode(key_data).c_str());
wvutil::Base64SafeEncode(key_data).c_str());
}
std::shared_ptr<CdmSession> session;
@@ -678,9 +673,7 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
return status;
}
if (query_token == QUERY_KEY_SRM_UPDATE_SUPPORT) {
const bool is_srm_update_supported = crypto_session->IsSrmUpdateSupported();
*query_response =
is_srm_update_supported ? QUERY_VALUE_TRUE : QUERY_VALUE_FALSE;
*query_response = QUERY_VALUE_FALSE;
return NO_ERROR;
}
if (query_token == QUERY_KEY_WVCDM_VERSION) {
@@ -737,6 +730,9 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
case kClientTokenOemCert:
*query_response = QUERY_VALUE_OEM_CERTIFICATE;
break;
case kClientTokenBootCertChain:
*query_response = QUERY_VALUE_BOOT_CERTIFICATE_CHAIN;
break;
case kClientTokenUninitialized:
default:
LOGW("GetProvisioningMethod returned invalid method: token_type = %d",
@@ -1030,7 +1026,7 @@ CdmResponseType CdmEngine::GetProvisioningRequest(
if (status != NO_ERROR) return status;
}
const CdmResponseType status = cert_provisioning_->GetProvisioningRequest(
requested_security_level, cert_type, cert_authority,
file_system_, requested_security_level, cert_type, cert_authority,
file_system_->origin(), spoid_, request, default_url);
if (status != NO_ERROR) {
cert_provisioning_.reset(); // Release resources.
@@ -1135,13 +1131,37 @@ bool CdmEngine::IsProvisioned(CdmSecurityLevel security_level) {
UsagePropertySet property_set;
property_set.set_security_level(
security_level == kSecurityLevelL3 ? kLevel3 : kLevelDefault);
return GetProvisioningStatus(security_level) == kProvisioned;
}
CdmSession session(file_system_, metrics_->AddSession());
const CdmResponseType status = session.Init(&property_set);
if (NO_ERROR != status) {
LOGE("Init failed: status = %d", static_cast<int>(status));
CdmProvisioningStatus CdmEngine::GetProvisioningStatus(
CdmSecurityLevel security_level) {
std::unique_ptr<CryptoSession> crypto_session(
CryptoSession::MakeCryptoSession(metrics_->GetCryptoMetrics()));
CdmResponseType status = crypto_session->Open(
security_level == kSecurityLevelL3 ? kLevel3 : kLevelDefault);
if (status != NO_ERROR) {
LOGE("Failed to open crypto session: status = %d",
static_cast<int>(status));
return kUnknownProvisionStatus;
}
return status == NO_ERROR;
const CdmSecurityLevel cdm_security_level =
crypto_session->GetSecurityLevel();
DeviceFiles handle(file_system_);
if (!handle.Init(cdm_security_level)) {
LOGE("Failed to initialize device files.");
return kUnknownProvisionStatus;
}
UsagePropertySet property_set;
if (handle.HasCertificate(property_set.use_atsc_mode())) {
return kProvisioned;
}
if (crypto_session->GetPreProvisionTokenType() == kClientTokenBootCertChain &&
!handle.HasOemCertificate()) {
return kNeedsOemCertProvisioning;
}
return kNeedsDrmCertProvisioning;
}
CdmResponseType CdmEngine::Unprovision(CdmSecurityLevel security_level) {
@@ -1173,7 +1193,7 @@ CdmResponseType CdmEngine::Unprovision(CdmSecurityLevel security_level) {
// TODO(b/141705730): Remove usage entries during unprovisioning.
if (!file_system_->IsGlobal()) {
if (!handle.RemoveCertificate()) {
if (!handle.RemoveCertificate() || !handle.RemoveOemCertificate()) {
LOGE("Unable to delete certificate");
return UNPROVISION_ERROR_2;
}
@@ -1421,7 +1441,7 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
}
// Return a random usage report from a random security level
SecurityLevel security_level =
CdmRandom::RandomBool() ? kLevelDefault : kLevel3;
wvutil::CdmRandom::RandomBool() ? kLevelDefault : kLevel3;
CdmResponseType status = UNKNOWN_ERROR;
do {
status = GetUsageInfo(app_id, security_level, error_detail, usage_info);
@@ -1486,7 +1506,7 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
return NO_ERROR;
}
const size_t index = CdmRandom::RandomInRange(usage_data.size() - 1);
const size_t index = wvutil::CdmRandom::RandomInRange(usage_data.size() - 1);
status = usage_session_->RestoreUsageSession(usage_data[index], error_detail);
if (KEY_ADDED != status) {
// TODO(b/141704872): Make multiple attempts.
@@ -1897,7 +1917,7 @@ CdmResponseType CdmEngine::ParseDecryptHashString(
return INVALID_DECRYPT_HASH_FORMAT;
}
*hash = wvcdm::a2bs_hex(tokens[2]);
*hash = wvutil::a2bs_hex(tokens[2]);
if (hash->empty()) {
LOGE("Malformed hash: %s", hash_string.c_str());
return INVALID_DECRYPT_HASH_FORMAT;
@@ -1990,7 +2010,7 @@ bool CdmEngine::ValidateKeySystem(const CdmKeySystem& key_system) {
}
void CdmEngine::OnTimerEvent() {
Clock clock;
wvutil::Clock clock;
const uint64_t current_time = clock.GetCurrentTime();
bool usage_update_period_expired = false;
if (current_time - last_usage_information_update_time_ >

View File

@@ -14,7 +14,7 @@
namespace wvcdm {
CdmEngine* CdmEngineFactory::CreateCdmEngine(FileSystem* file_system) {
CdmEngine* CdmEngineFactory::CreateCdmEngine(wvutil::FileSystem* file_system) {
std::unique_ptr<metrics::EngineMetrics> engine_metrics(
new metrics::EngineMetrics());

View File

@@ -24,7 +24,7 @@
// Stringify turns macro arguments into static C strings.
// Example: STRINGIFY(this_argument) -> "this_argument"
#define STRINGIFY(PARAM...) #PARAM
#define STRINGIFY(PARAM) #PARAM
#define RETURN_STATUS_IF_NULL(PARAM) \
if ((PARAM) == nullptr) { \
@@ -66,7 +66,7 @@ int DrmKeyTypeToMetricValue(CryptoWrappedKey::Type type) {
}
} // namespace
CdmSession::CdmSession(FileSystem* file_system,
CdmSession::CdmSession(wvutil::FileSystem* file_system,
std::shared_ptr<metrics::SessionMetrics> metrics)
: metrics_(metrics),
initialized_(false),
@@ -705,7 +705,7 @@ CdmResponseType CdmSession::Decrypt(const CdmDecryptionParametersV16& params) {
is_usage_update_needed_ = has_provider_session_token();
}
} else {
Clock clock;
wvutil::Clock clock;
int64_t current_time = clock.GetCurrentTime();
if (policy_engine_->HasLicenseOrRentalOrPlaybackDurationExpired(
current_time)) {
@@ -887,9 +887,9 @@ bool CdmSession::GenerateKeySetId(bool atsc_mode_enabled,
}
if (atsc_mode_enabled)
*key_set_id = ATSC_KEY_SET_ID_PREFIX + b2a_hex(random_data);
*key_set_id = ATSC_KEY_SET_ID_PREFIX + wvutil::b2a_hex(random_data);
else
*key_set_id = KEY_SET_ID_PREFIX + b2a_hex(random_data);
*key_set_id = KEY_SET_ID_PREFIX + wvutil::b2a_hex(random_data);
// key set collision
if (file_handle_->LicenseExists(*key_set_id)) {

View File

@@ -28,7 +28,7 @@ const std::string kProvisioningServerUrl =
"?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE";
// NOTE: Provider ID = widevine.com
const std::string kCpProductionServiceCertificate = wvcdm::a2bs_hex(
const std::string kCpProductionServiceCertificate = wvutil::a2bs_hex(
"0ab9020803121051434fe2a44c763bcc2c826a2d6ef9a718f7d793d005228e02"
"3082010a02820101009e27088659dbd9126bc6ed594caf652b0eaab82abb9862"
"ada1ee6d2cb5247e94b28973fef5a3e11b57d0b0872c930f351b5694354a8c77"
@@ -51,6 +51,30 @@ const std::string kCpProductionServiceCertificate = wvcdm::a2bs_hex(
"26e0c050f3fd3ebe68cef9903ef6405b25fc6e31f93559fcff05657662b3653a"
"8598ed5751b38694419242a875d9e00d5a5832933024b934859ec8be78adccbb"
"1ec7127ae9afeef9c5cd2e15bd3048e8ce652f7d8c5d595a0323238c598a28");
// Retrieves |stored_oem_cert| from |file_handle|, and load the OEM private key
// to |crypto_session|. Returns true if all operations are successful.
bool RetrieveOemCertificateAndLoadPrivateKey(CryptoSession& crypto_session,
DeviceFiles& file_handle,
std::string& stored_oem_cert) {
stored_oem_cert.clear();
CryptoWrappedKey wrapped_private_key;
if (file_handle.RetrieveOemCertificate(&stored_oem_cert,
&wrapped_private_key) !=
DeviceFiles::kCertificateValid) {
LOGE("An invalid stored OEM certificated is retrieved");
stored_oem_cert.clear();
return false;
}
if (crypto_session.LoadOemCertificatePrivateKey(wrapped_private_key) !=
NO_ERROR) {
LOGE("Can not load the OEM private key");
stored_oem_cert.clear();
return false;
}
return true;
}
} // namespace
// Protobuf generated classes.
using video_widevine::ClientIdentification_ClientCapabilities;
@@ -60,6 +84,7 @@ using video_widevine::EncryptedClientIdentification;
using video_widevine::ProvisioningOptions;
using video_widevine::ProvisioningRequest;
using video_widevine::ProvisioningResponse;
using video_widevine::PublicKeyToCertify;
using video_widevine::SignedDrmCertificate;
using video_widevine::SignedProvisioningMessage;
using video_widevine::
@@ -130,10 +155,14 @@ CdmResponseType CertificateProvisioning::SetSpoidParameter(
*/
SignedProvisioningMessage::ProvisioningType
CertificateProvisioning::GetProvisioningType() {
if (crypto_session_->GetPreProvisionTokenType() == kClientTokenOemCert)
return SignedProvisioningMessage::PROVISIONING_30;
else
return SignedProvisioningMessage::PROVISIONING_20;
switch (crypto_session_->GetPreProvisionTokenType()) {
case kClientTokenBootCertChain:
return SignedProvisioningMessage::PROVISIONING_40;
case kClientTokenOemCert:
return SignedProvisioningMessage::PROVISIONING_30;
default:
return SignedProvisioningMessage::PROVISIONING_20;
}
}
/*
@@ -144,20 +173,20 @@ CertificateProvisioning::GetProvisioningType() {
* Returns NO_ERROR for success and CERT_PROVISIONING_REQUEST_ERROR_? if fails.
*/
CdmResponseType CertificateProvisioning::GetProvisioningRequest(
SecurityLevel requested_security_level, CdmCertificateType cert_type,
const std::string& cert_authority, const std::string& origin,
const std::string& spoid, CdmProvisioningRequest* request,
std::string* default_url) {
wvutil::FileSystem* file_system, SecurityLevel requested_security_level,
CdmCertificateType cert_type, const std::string& cert_authority,
const std::string& origin, const std::string& spoid,
CdmProvisioningRequest* request, std::string* default_url) {
return CloseSessionOnError(GetProvisioningRequestInternal(
requested_security_level, cert_type, cert_authority, origin, spoid,
request, default_url));
file_system, requested_security_level, cert_type, cert_authority, origin,
spoid, request, default_url));
}
CdmResponseType CertificateProvisioning::GetProvisioningRequestInternal(
SecurityLevel requested_security_level, CdmCertificateType cert_type,
const std::string& cert_authority, const std::string& origin,
const std::string& spoid, CdmProvisioningRequest* request,
std::string* default_url) {
wvutil::FileSystem* file_system, SecurityLevel requested_security_level,
CdmCertificateType cert_type, const std::string& cert_authority,
const std::string& origin, const std::string& spoid,
CdmProvisioningRequest* request, std::string* default_url) {
if (!request || !default_url) {
LOGE("Output parameter |%s| is not provided",
request ? "default_url" : "request");
@@ -174,30 +203,17 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequestInternal(
return status;
}
if (crypto_session_->GetPreProvisionTokenType() ==
kClientTokenBootCertChain) {
return GetProvisioning40RequestInternal(file_system, request);
}
// Prepare device provisioning request.
ProvisioningRequest provisioning_request;
wvcdm::ClientIdentification id;
status = id.InitForProvisioning(crypto_session_.get());
status = FillEncryptedClientId(/*client_token=*/"", provisioning_request);
if (status != NO_ERROR) return status;
video_widevine::ClientIdentification client_id;
CdmAppParameterMap app_parameter;
status = id.Prepare(app_parameter, kEmptyString, &client_id);
if (status != NO_ERROR) return status;
if (!service_certificate_->has_certificate()) {
LOGE("Service certificate not staged");
return CERT_PROVISIONING_EMPTY_SERVICE_CERTIFICATE;
}
// Encrypt client identification
EncryptedClientIdentification* encrypted_client_id =
provisioning_request.mutable_encrypted_client_id();
status = service_certificate_->EncryptClientId(
crypto_session_.get(), &client_id, encrypted_client_id);
uint32_t nonce;
status = crypto_session_->GenerateNonce(&nonce);
@@ -272,13 +288,184 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequestInternal(
if (!wvcdm::Properties::provisioning_messages_are_binary()) {
// Return request as web-safe base64 string
*request = Base64SafeEncodeNoPad(serialized_request);
*request = wvutil::Base64SafeEncodeNoPad(serialized_request);
} else {
*request = std::move(serialized_request);
}
return NO_ERROR;
}
CdmResponseType CertificateProvisioning::GetProvisioning40RequestInternal(
wvutil::FileSystem* file_system, CdmProvisioningRequest* request) {
if (!crypto_session_->IsOpen()) {
LOGE("Crypto session is not open");
return PROVISIONING_4_CRYPTO_SESSION_NOT_OPEN;
}
if (file_system == nullptr) {
LOGE("file_system is nullptr but is required in provisioning 4");
return PROVISIONING_4_FILE_SYSTEM_IS_NULL;
}
const CdmSecurityLevel security_level = crypto_session_->GetSecurityLevel();
DeviceFiles file_handle(file_system);
if (!file_handle.Init(security_level)) {
LOGE("Failed to initialize DeviceFiles");
return PROVISIONING_4_FAILED_TO_INITIALIZE_DEVICE_FILES;
}
ProvisioningRequest provisioning_request;
// Determine the current stage by checking if OEM cert exists.
std::string stored_oem_cert;
if (file_handle.HasOemCertificate()) {
// This is second stage requesting for DRM cert. We try to use the stored
// OEM cert. In case of error, we just fall back to the first stage
// provisioning (request for an OEM cert).
if (!RetrieveOemCertificateAndLoadPrivateKey(*crypto_session_, file_handle,
stored_oem_cert)) {
stored_oem_cert.clear();
LOGD("Deleting the stored OEM certificate due to unsuccessful read");
if (!file_handle.RemoveOemCertificate()) {
// This should not happen.
LOGE("Failed to delete the OEM certificate certificate");
}
}
}
// If this is the first stage, |stored_oem_cert| remains empty. In this case,
// the client identification token will be retrieved from OEMCrypto, which is
// the BCC in this case.
// If this is the second stage, |stored_oem_cert| is non-empty and will be
// used as the client identification token.
CdmResponseType status =
FillEncryptedClientId(stored_oem_cert, provisioning_request);
if (status != NO_ERROR) return status;
std::string public_key;
std::string public_key_signature;
provisioning_40_wrapped_private_key_.clear();
provisioning_40_key_type_ = CryptoWrappedKey::kUninitialized;
status = crypto_session_->GenerateCertificateKeyPair(
&public_key, &public_key_signature, &provisioning_40_wrapped_private_key_,
&provisioning_40_key_type_);
if (status != NO_ERROR) return status;
PublicKeyToCertify* key_to_certify =
provisioning_request.mutable_certificate_public_key();
key_to_certify->set_public_key(public_key);
key_to_certify->set_signature(public_key_signature);
key_to_certify->set_key_type(provisioning_40_key_type_ ==
CryptoWrappedKey::kRsa
? PublicKeyToCertify::RSA
: PublicKeyToCertify::ECC);
// In provisioning 4, the message is not signed.
SignedProvisioningMessage signed_provisioning_msg;
provisioning_request.SerializeToString(
signed_provisioning_msg.mutable_message());
signed_provisioning_msg.set_provisioning_type(GetProvisioningType());
signed_provisioning_msg.set_protocol_version(
SignedProvisioningMessage_ProvisioningProtocolVersion_VERSION_1_1);
std::string serialized_request;
signed_provisioning_msg.SerializeToString(&serialized_request);
if (!wvcdm::Properties::provisioning_messages_are_binary()) {
// Return request as web-safe base64 string
*request = wvutil::Base64SafeEncodeNoPad(serialized_request);
} else {
*request = std::move(serialized_request);
}
return NO_ERROR;
}
CdmResponseType CertificateProvisioning::FillEncryptedClientId(
const std::string& client_token,
ProvisioningRequest& provisioning_request) {
if (!crypto_session_->IsOpen()) {
return UNKNOWN_ERROR;
}
wvcdm::ClientIdentification id;
CdmResponseType status =
id.InitForProvisioningRequest(client_token, crypto_session_.get());
if (status != NO_ERROR) return status;
video_widevine::ClientIdentification client_id;
CdmAppParameterMap app_parameter;
status = id.Prepare(app_parameter, kEmptyString, &client_id);
if (status != NO_ERROR) return status;
if (!service_certificate_->has_certificate()) {
LOGE("Service certificate not staged");
return CERT_PROVISIONING_EMPTY_SERVICE_CERTIFICATE;
}
// Encrypt client identification
return service_certificate_->EncryptClientId(
crypto_session_.get(), &client_id,
provisioning_request.mutable_encrypted_client_id());
}
CdmResponseType CertificateProvisioning::HandleProvisioning40Response(
wvutil::FileSystem* file_system, const std::string& response_message) {
ProvisioningResponse provisioning_response;
if (response_message.empty() ||
!provisioning_response.ParseFromString(response_message)) {
return PROVISIONING_4_RESPONSE_FAILED_TO_PARSE_MESSAGE;
}
if (provisioning_response.has_status() &&
provisioning_response.status() != ProvisioningResponse::NO_ERROR) {
LOGE("Provisioning Response status: %d", provisioning_response.status());
switch (provisioning_response.status()) {
case ProvisioningResponse::REVOKED_DEVICE_CREDENTIALS:
case ProvisioningResponse::REVOKED_DEVICE_SERIES:
return DEVICE_REVOKED;
default:
return PROVISIONING_4_RESPONSE_HAS_ERROR_STATUS;
}
}
const std::string& device_certificate =
provisioning_response.device_certificate();
if (device_certificate.empty()) {
LOGE("Provisioning response has no certificate");
return PROVISIONING_4_RESPONSE_HAS_NO_CERTIFICATE;
}
if (provisioning_40_wrapped_private_key_.empty()) {
LOGE("No private key was generated");
return PROVISIONING_4_NO_PRIVATE_KEY;
}
const CryptoWrappedKey private_key(provisioning_40_key_type_,
provisioning_40_wrapped_private_key_);
const CdmSecurityLevel security_level = crypto_session_->GetSecurityLevel();
CloseSession();
DeviceFiles file_handle(file_system);
if (!file_handle.Init(security_level)) {
LOGE("Failed to initialize DeviceFiles");
return PROVISIONING_4_FAILED_TO_INITIALIZE_DEVICE_FILES_2;
}
// Check the stage of the provisioning by checking if an OEM cert is already
// stored in the file system.
if (!file_handle.HasOemCertificate()) {
// No OEM cert already stored => the response is expected to be an OEM cert.
if (!file_handle.StoreOemCertificate(device_certificate, private_key)) {
LOGE("Failed to store provisioning 4 OEM certificate");
return PROVISIONING_4_FAILED_TO_STORE_OEM_CERTIFICATE;
}
} else {
// The response is assumed to be an DRM cert.
if (!file_handle.StoreCertificate(device_certificate, private_key)) {
LOGE("Failed to store provisioning 4 DRM certificate");
return PROVISIONING_4_FAILED_TO_STORE_DRM_CERTIFICATE;
}
}
return NO_ERROR;
}
/*
* The response message consists of a device certificate and the device RSA key.
* The device RSA key is stored in the T.E.E. The device certificate is stored
@@ -287,8 +474,9 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequestInternal(
* Returns NO_ERROR for success and CERT_PROVISIONING_RESPONSE_ERROR_? if fails.
*/
CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
FileSystem* file_system, const CdmProvisioningResponse& response_message,
std::string* cert, std::string* wrapped_key) {
wvutil::FileSystem* file_system,
const CdmProvisioningResponse& response_message, std::string* cert,
std::string* wrapped_key) {
if (response_message.empty()) {
LOGE("Provisioning response message is empty");
return CERT_PROVISIONING_RESPONSE_ERROR_1;
@@ -317,6 +505,11 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
return CERT_PROVISIONING_RESPONSE_ERROR_2;
}
if (signed_response.provisioning_type() ==
SignedProvisioningMessage::PROVISIONING_40) {
return HandleProvisioning40Response(file_system, signed_response.message());
}
bool error = false;
if (!signed_response.has_signature()) {
LOGE("Signed response does not have signature");
@@ -491,7 +684,7 @@ bool CertificateProvisioning::ExtractAndDecodeSignedMessage(
// Decode the base64-encoded message.
const std::vector<uint8_t> decoded_message =
wvcdm::Base64SafeDecode(message_string);
wvutil::Base64SafeDecode(message_string);
result->assign(decoded_message.begin(), decoded_message.end());
return true;
}

View File

@@ -68,13 +68,14 @@ using video_widevine::ProvisioningRequest;
using video_widevine::ProvisioningResponse;
using video_widevine::SignedProvisioningMessage;
CdmResponseType ClientIdentification::InitForProvisioning(
CryptoSession* crypto_session) {
CdmResponseType ClientIdentification::InitForProvisioningRequest(
const std::string& client_token, CryptoSession* crypto_session) {
if (crypto_session == nullptr) {
LOGE("Crypto session not provided");
return PARAMETER_NULL;
}
is_license_request_ = false;
client_token_ = client_token;
crypto_session_ = crypto_session;
return NO_ERROR;
}
@@ -118,7 +119,15 @@ CdmResponseType ClientIdentification::Prepare(
client_id->set_type(
video_widevine::ClientIdentification::DRM_DEVICE_CERTIFICATE);
client_id->set_token(client_token_);
} else if (!client_token_.empty()) {
// A token has been provided via InitForProvisioningRequest. This can only
// happen in provisioning 4 (second stage) where an OEM cert is provided.
client_id->set_type(
video_widevine::ClientIdentification::OEM_DEVICE_CERTIFICATE);
client_id->set_token(client_token_);
} else if (!is_okp_request_) {
// An OTA Keybox Provisioning request does not have a client id.
// Otherwise this is a regular provisioning request.
video_widevine::ClientIdentification::TokenType token_type;
if (!GetProvisioningTokenType(&token_type)) {
LOGE("Failed to get provisioning token type");
@@ -127,13 +136,18 @@ CdmResponseType ClientIdentification::Prepare(
client_id->set_type(token_type);
std::string token;
CdmResponseType status = crypto_session_->GetProvisioningToken(&token);
std::string additional_token;
CdmResponseType status =
crypto_session_->GetProvisioningToken(&token, &additional_token);
if (status != NO_ERROR) {
LOGE("Failed to get provisioning token: status = %d",
static_cast<int>(status));
return status;
}
client_id->set_token(token);
if (!additional_token.empty()) {
client_id->mutable_device_credentials()->set_token(additional_token);
}
}
ClientIdentification_NameValue* client_info;
@@ -295,8 +309,7 @@ CdmResponseType ClientIdentification::Prepare(
}
if (is_license_request_) {
client_capabilities->set_can_update_srm(
crypto_session_->IsSrmUpdateSupported());
client_capabilities->set_can_update_srm(false);
uint16_t srm_version;
if (crypto_session_->GetSrmVersion(&srm_version) == NO_ERROR)
client_capabilities->set_srm_version(srm_version);
@@ -344,6 +357,10 @@ bool ClientIdentification::GetProvisioningTokenType(
*token_type =
video_widevine::ClientIdentification::OEM_DEVICE_CERTIFICATE;
return true;
case kClientTokenBootCertChain:
*token_type =
video_widevine::ClientIdentification::BOOT_CERTIFICATE_CHAIN;
return true;
case kClientTokenDrmCert:
default:
// shouldn't happen

View File

@@ -31,7 +31,7 @@
// Stringify turns macro arguments into static C strings.
// Example: STRINGIFY(this_argument) -> "this_argument"
#define STRINGIFY(PARAM...) #PARAM
#define STRINGIFY(PARAM) #PARAM
#define RETURN_IF_NULL(PARAM, ret_value) \
if ((PARAM) == nullptr) { \
@@ -51,6 +51,16 @@
return ret_value; \
}
#ifdef HAS_DUAL_KEY
/**
* Internal only OEMCrypto method. This is called before parsing the license
* response to indicate the response uses dual keys. If this isn't called,
* it is using single keys.
*/
extern "C" OEMCryptoResult OEMCrypto_UseSecondaryKey(OEMCrypto_SESSION session,
bool dual_key);
#endif
namespace wvcdm {
namespace {
using UsageTableLock = std::unique_lock<std::recursive_mutex>;
@@ -83,7 +93,7 @@ constexpr size_t kMaxSubsampleRegionSizes[] = {
};
// The +1 in this calculation is because the bounds RESOURCE_RATING_TIER_MAX and
// RESOURCE_RATING_TIER_MIN are inclusive.
static_assert(ArraySize(kMaxSubsampleRegionSizes) ==
static_assert(wvutil::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");
@@ -128,8 +138,8 @@ CdmResponseType MapOEMCryptoResult(OEMCryptoResult result,
void AdvanceDestBuffer(OEMCrypto_DestBufferDesc* dest_buffer, size_t bytes) {
switch (dest_buffer->type) {
case OEMCrypto_BufferType_Clear:
dest_buffer->buffer.clear.address += bytes;
dest_buffer->buffer.clear.address_length -= bytes;
dest_buffer->buffer.clear.clear_buffer += bytes;
dest_buffer->buffer.clear.clear_buffer_length -= bytes;
return;
case OEMCrypto_BufferType_Secure:
@@ -176,8 +186,8 @@ size_t GenericEncryptionBlockSize(CdmEncryptionAlgorithm algorithm) {
} // namespace
// CryptoSession variables allocation.
shared_mutex CryptoSession::static_field_mutex_;
shared_mutex CryptoSession::oem_crypto_mutex_;
wvutil::shared_mutex CryptoSession::static_field_mutex_;
wvutil::shared_mutex CryptoSession::oem_crypto_mutex_;
bool CryptoSession::initialized_ = false;
bool CryptoSession::needs_keybox_provisioning_ = false;
int CryptoSession::session_count_ = 0;
@@ -233,7 +243,7 @@ void GenerateMacContext(const std::string& input_context,
deriv_context->assign(kSigningKeyLabel);
deriv_context->append(1, '\0');
deriv_context->append(input_context);
deriv_context->append(EncodeUint32(kSigningKeySizeBits * 2));
deriv_context->append(wvutil::EncodeUint32(kSigningKeySizeBits * 2));
}
void GenerateEncryptContext(const std::string& input_context,
@@ -249,12 +259,12 @@ void GenerateEncryptContext(const std::string& input_context,
deriv_context->assign(kEncryptionKeyLabel);
deriv_context->append(1, '\0');
deriv_context->append(input_context);
deriv_context->append(EncodeUint32(kEncryptionKeySizeBits));
deriv_context->append(wvutil::EncodeUint32(kEncryptionKeySizeBits));
}
OEMCryptoCipherMode ToOEMCryptoCipherMode(CdmCipherMode cipher_mode) {
return cipher_mode == kCipherModeCtr ? OEMCrypto_CipherMode_CTR
: OEMCrypto_CipherMode_CBC;
return cipher_mode == kCipherModeCtr ? OEMCrypto_CipherMode_CENC
: OEMCrypto_CipherMode_CBCS;
}
CryptoSession::CryptoSession(metrics::CryptoMetrics* metrics)
@@ -306,6 +316,9 @@ CdmResponseType CryptoSession::GetProvisioningMethod(
case OEMCrypto_DrmCertificate:
type = kClientTokenDrmCert;
break;
case OEMCrypto_BootCertificateChain:
type = kClientTokenBootCertChain;
break;
case OEMCrypto_ProvisioningError:
default:
if (static_cast<int>(method) == 0 && needs_keybox_provisioning_) {
@@ -361,10 +374,18 @@ void CryptoSession::Init() {
void CryptoSession::ReinitializeForTest() {
if (initialized_) {
initialized_ = false;
if (OEMCrypto_SUCCESS != OEMCrypto_Terminate()) return;
OEMCryptoResult status = OEMCrypto_Terminate();
if (OEMCrypto_SUCCESS != status) {
LOGE("OEMCrypto_Terminate failed: %d", status);
return;
}
}
// Give up if we cannot initialize at all.
if (OEMCrypto_SUCCESS != OEMCrypto_Initialize()) return;
OEMCryptoResult status = OEMCrypto_Initialize();
if (OEMCrypto_SUCCESS != status) {
LOGE("OEMCrypto_Initialize failed: %d", status);
return;
}
initialized_ = true;
// For integration and unit tests we will install a test keybox and do not
// need to do keybox provisioning.
@@ -562,10 +583,12 @@ CdmResponseType CryptoSession::GetTokenFromOemCert(std::string* token) {
}
}
CdmResponseType CryptoSession::GetProvisioningToken(std::string* token) {
if (token == nullptr) {
CdmResponseType CryptoSession::GetProvisioningToken(
std::string* token, std::string* additional_token) {
if (token == nullptr || additional_token == nullptr) {
metrics_->crypto_session_get_token_.Increment(PARAMETER_NULL);
RETURN_IF_NULL(token, PARAMETER_NULL);
RETURN_IF_NULL(additional_token, PARAMETER_NULL);
}
if (!IsInitialized()) {
@@ -579,6 +602,8 @@ CdmResponseType CryptoSession::GetProvisioningToken(std::string* token) {
status = GetTokenFromKeybox(token);
} else if (pre_provision_token_type_ == kClientTokenOemCert) {
status = GetTokenFromOemCert(token);
} else if (pre_provision_token_type_ == kClientTokenBootCertChain) {
status = GetBootCertificateChain(token, additional_token);
}
metrics_->crypto_session_get_token_.Increment(status);
return status;
@@ -595,41 +620,28 @@ CdmSecurityLevel CryptoSession::GetSecurityLevel(
LOGV("Getting security level: requested_security_level = %s",
SecurityLevelToString(requested_security_level));
RETURN_IF_UNINITIALIZED(kSecurityLevelUninitialized);
const char* const level = WithOecReadLock("GetSecurityLevel", [&] {
return OEMCrypto_SecurityLevel(requested_security_level);
});
if (level == nullptr) {
LOGE("Security level is null: requested_security_level = %d",
const OEMCrypto_Security_Level level = WithOecReadLock(
"GetSecurityLevel",
[&] { return OEMCrypto_SecurityLevel(requested_security_level); });
if (level == 0) {
LOGE("Security level is unknown: requested_security_level = %d",
static_cast<int>(requested_security_level));
return kSecurityLevelUnknown;
}
// Check length in the event of a bad pointer.
// |kMaxSecurityLevelLength| is a value larger than expected to
// be able to detect an overrun.
constexpr size_t kMaxSecurityLevelLength = 5;
const size_t length = strnlen(level, kMaxSecurityLevelLength);
constexpr size_t kExpectedSecurityLevelLength = 2;
if (length != kExpectedSecurityLevelLength) {
LOGE(
"Unexpected security level length: "
"length = %zu, requested_security_level = %s",
length, SecurityLevelToString(requested_security_level));
return kSecurityLevelUnknown;
}
const std::string security_level(level);
if (security_level == "L1") {
if (level == OEMCrypto_Level1) {
return kSecurityLevelL1;
}
if (security_level == "L2") {
if (level == OEMCrypto_Level2) {
return kSecurityLevelL2;
}
if (security_level == "L3") {
if (level == OEMCrypto_Level3) {
return kSecurityLevelL3;
}
LOGE(
"Ill-formed security level: "
"level = \"%s\", requested_security_level = %s",
security_level.c_str(), SecurityLevelToString(requested_security_level));
"level = \"L%u\", requested_security_level = %s",
static_cast<unsigned int>(level),
SecurityLevelToString(requested_security_level));
return kSecurityLevelUnknown;
}
@@ -815,6 +827,11 @@ CdmResponseType CryptoSession::GetSystemIdInternal(uint32_t* system_id) {
// Drm certificate.
return NO_ERROR;
}
if (pre_provision_token_type_ == kClientTokenBootCertChain) {
// A system id can not be inferred from BCC. If the provisioning process has
// come to the second stage, we may read system id from the stored OEM cert.
return NO_ERROR;
}
LOGE("Unsupported pre-provision token type: %d",
static_cast<int>(pre_provision_token_type_));
return UNKNOWN_CLIENT_TOKEN_TYPE;
@@ -938,10 +955,10 @@ CdmResponseType CryptoSession::Open(SecurityLevel requested_security_level) {
metrics_->oemcrypto_get_random_.Increment(random_sts);
uint64_t request_id_index =
request_id_index_source_.fetch_add(1, std::memory_order_relaxed);
request_id_ = HexEncode(reinterpret_cast<uint8_t*>(&request_id_base),
sizeof(request_id_base)) +
HexEncode(reinterpret_cast<uint8_t*>(&request_id_index),
sizeof(request_id_index));
request_id_ = wvutil::HexEncode(reinterpret_cast<uint8_t*>(&request_id_base),
sizeof(request_id_base)) +
wvutil::HexEncode(reinterpret_cast<uint8_t*>(&request_id_index),
sizeof(request_id_index));
// Initialize key session
WithOecSessionLock("Open() calling key_session_.reset()", [&] {
@@ -1058,6 +1075,19 @@ CdmResponseType CryptoSession::PrepareAndSignLicenseRequest(
"PrepareAndSignLicenseRequest");
}
CdmResponseType CryptoSession::UseSecondaryKey(bool dual_key) {
#ifdef HAS_DUAL_KEY
OEMCryptoResult sts;
WithOecSessionLock("UseSecondaryKey", [&] {
sts = OEMCrypto_UseSecondaryKey(oec_session_id_, dual_key);
});
return MapOEMCryptoResult(sts, LOAD_KEY_ERROR, "UseSecondaryKey");
#else
return NO_ERROR;
#endif
}
CdmResponseType CryptoSession::LoadKeys(
const std::string& message, const std::string& signature,
const std::string& mac_key_iv, const std::string& mac_key,
@@ -1388,6 +1418,122 @@ CdmResponseType CryptoSession::LoadCertificatePrivateKey(
"LoadCertificatePrivateKey");
}
CdmResponseType CryptoSession::GetBootCertificateChain(
std::string* bcc, std::string* additional_signature) {
RETURN_IF_NULL(bcc, PARAMETER_NULL);
RETURN_IF_NULL(additional_signature, PARAMETER_NULL);
RETURN_IF_UNINITIALIZED(CRYPTO_SESSION_NOT_INITIALIZED);
LOGV("GetBootCertificateChain");
if (pre_provision_token_type_ != kClientTokenBootCertChain) {
return PROVISIONING_TYPE_IS_NOT_BOOT_CERTIFICATE_CHAIN_ERROR;
}
size_t bcc_length = 0;
size_t additional_signature_length = 0;
OEMCryptoResult sts;
WithOecReadLock("GetBootCertificateChain Attempt 1", [&] {
sts = OEMCrypto_GetBootCertificateChain(nullptr, &bcc_length, nullptr,
&additional_signature_length);
});
if (sts == OEMCrypto_ERROR_SHORT_BUFFER) {
bcc->resize(bcc_length);
additional_signature->resize(additional_signature_length);
WithOecReadLock("GetBootCertificateChain Attempt 2", [&] {
sts = OEMCrypto_GetBootCertificateChain(
reinterpret_cast<uint8_t*>(&bcc->front()), &bcc_length,
reinterpret_cast<uint8_t*>(&additional_signature->front()),
&additional_signature_length);
});
}
return MapOEMCryptoResult(sts, GET_BOOT_CERTIFICATE_CHAIN_ERROR,
"GetBootCertificateChain");
}
CdmResponseType CryptoSession::GenerateCertificateKeyPair(
std::string* public_key, std::string* public_key_signature,
std::string* wrapped_private_key, CryptoWrappedKey::Type* key_type) {
LOGV("Generating certificate key pair: id = %u", oec_session_id_);
RETURN_IF_NULL(public_key, PARAMETER_NULL);
RETURN_IF_NULL(public_key_signature, PARAMETER_NULL);
RETURN_IF_NULL(wrapped_private_key, PARAMETER_NULL);
RETURN_IF_NULL(key_type, PARAMETER_NULL);
RETURN_IF_UNINITIALIZED(CRYPTO_SESSION_NOT_INITIALIZED);
// Round 1, get the size of all the fields.
size_t public_key_length = 0;
size_t public_key_signature_length = 0;
size_t wrapped_private_key_length = 0;
OEMCrypto_PrivateKeyType oemcrypto_key_type;
OEMCryptoResult status;
WithOecSessionLock("GenerateCertificateKeyPair Attempt 1", [&] {
M_TIME(status = OEMCrypto_GenerateCertificateKeyPair(
oec_session_id_, nullptr, &public_key_length, nullptr,
&public_key_signature_length, nullptr,
&wrapped_private_key_length, &oemcrypto_key_type),
metrics_, oemcrypto_generate_certificate_key_pair_, status);
});
if (status != OEMCrypto_ERROR_SHORT_BUFFER) {
return MapOEMCryptoResult(status, GENERATE_CERTIFICATE_KEY_PAIR_ERROR,
"GenerateCertificateKeyPair");
}
public_key->resize(public_key_length);
public_key_signature->resize(public_key_signature_length);
wrapped_private_key->resize(wrapped_private_key_length);
WithOecSessionLock("GenerateCertificateKeyPair Attempt 2", [&] {
M_TIME(
status = OEMCrypto_GenerateCertificateKeyPair(
oec_session_id_, reinterpret_cast<uint8_t*>(&public_key->front()),
&public_key_length,
reinterpret_cast<uint8_t*>(&public_key_signature->front()),
&public_key_signature_length,
reinterpret_cast<uint8_t*>(&wrapped_private_key->front()),
&wrapped_private_key_length, &oemcrypto_key_type),
metrics_, oemcrypto_generate_certificate_key_pair_, status);
});
public_key->resize(public_key_length);
public_key_signature->resize(public_key_signature_length);
wrapped_private_key->resize(wrapped_private_key_length);
if (oemcrypto_key_type == OEMCrypto_RSA_Private_Key) {
*key_type = CryptoWrappedKey::kRsa;
} else if (oemcrypto_key_type == OEMCrypto_ECC_Private_Key) {
*key_type = CryptoWrappedKey::kEcc;
} else {
LOGE("Unexpected key type returned from GenerateCertificateKeyPair: %d",
static_cast<int>(oemcrypto_key_type));
return MapOEMCryptoResult(status,
GENERATE_CERTIFICATE_KEY_PAIR_UNKNOWN_TYPE_ERROR,
"GenerateCertificateKeyPair");
}
return MapOEMCryptoResult(status, GENERATE_CERTIFICATE_KEY_PAIR_ERROR,
"GenerateCertificateKeyPair");
}
CdmResponseType CryptoSession::LoadOemCertificatePrivateKey(
const CryptoWrappedKey& private_key) {
LOGV("Load OEM cert and private key: id = %u", oec_session_id_);
const OEMCrypto_PrivateKeyType key_type =
(private_key.type() == CryptoWrappedKey::kEcc)
? OEMCrypto_ECC_Private_Key
: OEMCrypto_RSA_Private_Key;
const std::string& wrapped_private_key = private_key.key();
OEMCryptoResult status;
WithOecSessionLock("InstallOemPrivateKey", [&] {
M_TIME(status = OEMCrypto_InstallOemPrivateKey(
oec_session_id_, key_type,
reinterpret_cast<const uint8_t*>(wrapped_private_key.data()),
wrapped_private_key.size()),
metrics_, oemcrypto_install_oem_private_key_, status);
});
return MapOEMCryptoResult(status, LOAD_OEM_CERTIFICATE_PRIVATE_KEY_ERROR,
"InstallOemPrivateKey");
}
// Private.
CdmResponseType CryptoSession::SelectKey(const std::string& key_id,
CdmCipherMode cipher_mode) {
@@ -1502,7 +1648,7 @@ size_t CryptoSession::GetMaxSubsampleRegionSize() {
// Subtract RESOURCE_RATING_TIER_MIN to get a 0-based index into the
// table.
const uint32_t index = tier - RESOURCE_RATING_TIER_MIN;
if (index < ArraySize(kMaxSubsampleRegionSizes)) {
if (index < wvutil::ArraySize(kMaxSubsampleRegionSizes)) {
max_subsample_region_size_ = kMaxSubsampleRegionSizes[index];
}
}
@@ -1563,16 +1709,16 @@ CdmResponseType CryptoSession::Decrypt(
output_descriptor.type = output_descriptor_type;
switch (output_descriptor.type) {
case OEMCrypto_BufferType_Clear:
output_descriptor.buffer.clear.address =
output_descriptor.buffer.clear.clear_buffer =
static_cast<uint8_t*>(sample.decrypt_buffer) +
sample.decrypt_buffer_offset;
output_descriptor.buffer.clear.address_length =
output_descriptor.buffer.clear.clear_buffer_length =
sample.decrypt_buffer_size - sample.decrypt_buffer_offset;
break;
case OEMCrypto_BufferType_Secure:
output_descriptor.buffer.secure.handle = sample.decrypt_buffer;
output_descriptor.buffer.secure.secure_buffer = sample.decrypt_buffer;
output_descriptor.buffer.secure.offset = sample.decrypt_buffer_offset;
output_descriptor.buffer.secure.handle_length =
output_descriptor.buffer.secure.secure_buffer_length =
sample.decrypt_buffer_size;
break;
case OEMCrypto_BufferType_Direct:
@@ -1824,7 +1970,7 @@ CdmResponseType CryptoSession::GenerateUsageReport(
(*usage_report) =
std::string(reinterpret_cast<const char*>(&buffer[0]), buffer.size());
Unpacked_PST_Report pst_report(&buffer[0]);
wvutil::Unpacked_PST_Report pst_report(&buffer[0]);
*usage_duration_status = kUsageDurationsInvalid;
if (usage_length < pst_report.report_size()) {
LOGE(
@@ -1852,7 +1998,7 @@ CdmResponseType CryptoSession::GenerateUsageReport(
pst_report.seconds_since_first_decrypt());
LOGV("OEMCrypto_PST_Report.seconds_since_last_decrypt: %" PRId64 "\n",
pst_report.seconds_since_last_decrypt());
LOGV("OEMCrypto_PST_Report: %s\n", b2a_hex(*usage_report).c_str());
LOGV("OEMCrypto_PST_Report: %s\n", wvutil::b2a_hex(*usage_report).c_str());
if (kInactiveUnused == pst_report.status()) {
*usage_duration_status = kUsageDurationPlaybackNotBegun;
@@ -2096,29 +2242,6 @@ CdmResponseType CryptoSession::GetSrmVersion(uint16_t* srm_version) {
}
}
bool CryptoSession::IsSrmUpdateSupported() {
LOGV("Checking if SRM update is supported");
RETURN_IF_UNINITIALIZED(false);
return WithOecReadLock("IsSrmUpdateSupported",
[&] { return OEMCrypto_IsSRMUpdateSupported(); });
}
CdmResponseType CryptoSession::LoadSrm(const std::string& srm) {
LOGV("Loading SRM");
RETURN_IF_UNINITIALIZED(CRYPTO_SESSION_NOT_INITIALIZED);
if (srm.empty()) {
LOGE("SRM is empty");
return INVALID_SRM_LIST;
}
const OEMCryptoResult status = WithOecWriteLock("LoadSrm", [&] {
return OEMCrypto_LoadSRM(reinterpret_cast<const uint8_t*>(srm.data()),
srm.size());
});
return MapOEMCryptoResult(status, LOAD_SRM_ERROR, "LoadSRM");
}
bool CryptoSession::GetResourceRatingTier(uint32_t* tier) {
LOGV("Getting resource rating tier");
RETURN_IF_NOT_OPEN(false);
@@ -2160,16 +2283,28 @@ bool CryptoSession::GetBuildInformation(SecurityLevel security_level,
RETURN_IF_UNINITIALIZED(false);
RETURN_IF_NULL(info, false);
const char* build_information;
OEMCryptoResult build_information;
std::string buf;
size_t buf_length = 0;
WithOecReadLock("GetBuildInformation", [&] {
build_information = OEMCrypto_BuildInformation(security_level);
build_information =
OEMCrypto_BuildInformation(&buf[0], &buf_length, security_level);
});
if (build_information == nullptr) {
LOGE("OEMCrypto_BuildInformation failed: Returned null");
if (build_information == OEMCrypto_ERROR_SHORT_BUFFER) {
buf.resize(buf_length);
WithOecReadLock("GetBuildInformation Attempt 2", [&] {
build_information =
OEMCrypto_BuildInformation(&buf[0], &buf_length, security_level);
});
}
if (build_information == OEMCrypto_SUCCESS) {
*info = buf;
} else {
LOGE("Unexpected return value");
return false;
}
info->assign(build_information);
return true;
}
@@ -2892,9 +3027,9 @@ OEMCryptoResult CryptoSession::DecryptSample(
fake_sample.buffers.input_data += length;
AdvanceDestBuffer(&fake_sample.buffers.output_descriptor, length);
if (cipher_mode == kCipherModeCtr) {
AdvanceIvCtr(&fake_sample.iv,
original_subsample.block_offset +
original_subsample.num_bytes_encrypted);
wvutil::AdvanceIvCtr(&fake_sample.iv,
original_subsample.block_offset +
original_subsample.num_bytes_encrypted);
}
}
}
@@ -3049,7 +3184,8 @@ OEMCryptoResult CryptoSession::LegacyDecryptInChunks(
if (cipher_mode == kCipherModeCtr) {
// For 'cenc', update the IV depending on how many encrypted blocks
// we passed.
AdvanceIvCtr(&fake_sample.iv, chunk_size + fake_subsample.block_offset);
wvutil::AdvanceIvCtr(&fake_sample.iv,
chunk_size + fake_subsample.block_offset);
} else if (cipher_mode == kCipherModeCbc) {
// For 'cbcs', use the last ciphertext block as the next IV. The last
// block that was encrypted is probably not the last block of the
@@ -3154,7 +3290,7 @@ template <class Func>
auto CryptoSession::WithStaticFieldWriteLock(const char* tag, Func body)
-> decltype(body()) {
LOGV("Static field write lock: %s", tag);
std::unique_lock<shared_mutex> auto_lock(static_field_mutex_);
std::unique_lock<wvutil::shared_mutex> auto_lock(static_field_mutex_);
return body();
}
@@ -3162,7 +3298,7 @@ template <class Func>
auto CryptoSession::WithStaticFieldReadLock(const char* tag, Func body)
-> decltype(body()) {
LOGV("Static field read lock: %s", tag);
shared_lock<shared_mutex> auto_lock(static_field_mutex_);
wvutil::shared_lock<wvutil::shared_mutex> auto_lock(static_field_mutex_);
return body();
}
@@ -3170,7 +3306,7 @@ template <class Func>
auto CryptoSession::WithOecWriteLock(const char* tag, Func body)
-> decltype(body()) {
LOGV("OEMCrypto write lock: %s", tag);
std::unique_lock<shared_mutex> auto_lock(oem_crypto_mutex_);
std::unique_lock<wvutil::shared_mutex> auto_lock(oem_crypto_mutex_);
return body();
}
@@ -3178,7 +3314,7 @@ template <class Func>
auto CryptoSession::WithOecReadLock(const char* tag, Func body)
-> decltype(body()) {
LOGV("OEMCrypto read lock: %s", tag);
shared_lock<shared_mutex> auto_lock(oem_crypto_mutex_);
wvutil::shared_lock<wvutil::shared_mutex> auto_lock(oem_crypto_mutex_);
return body();
}
@@ -3186,7 +3322,7 @@ template <class Func>
auto CryptoSession::WithOecSessionLock(const char* tag, Func body)
-> decltype(body()) {
LOGV("OEMCrypto session lock: %s", tag);
shared_lock<shared_mutex> oec_auto_lock(oem_crypto_mutex_);
wvutil::shared_lock<wvutil::shared_mutex> oec_auto_lock(oem_crypto_mutex_);
std::unique_lock<std::mutex> session_auto_lock(oem_crypto_session_mutex_);
return body();
}

View File

@@ -31,6 +31,7 @@ using video_widevine_client::sdk::License;
using video_widevine_client::sdk::License_LicenseState_ACTIVE;
using video_widevine_client::sdk::License_LicenseState_RELEASING;
using video_widevine_client::sdk::NameValue;
using video_widevine_client::sdk::OemCertificate;
using video_widevine_client::sdk::UsageInfo;
using video_widevine_client::sdk::UsageInfo_DrmUsageCertificate;
using video_widevine_client::sdk::UsageInfo_ProviderSession;
@@ -45,7 +46,7 @@ using video_widevine_client::sdk::
// Stringify turns macro arguments into static C strings.
// Example: STRINGIFY(this_argument) -> "this_argument"
#define STRINGIFY(PARAM...) #PARAM
#define STRINGIFY(PARAM) #PARAM
#define RETURN_CERTIFICATE_STATE_CANNOT_HANDLE_IF_NULL(PARAM) \
if ((PARAM) == nullptr) { \
@@ -129,8 +130,8 @@ bool ExtractFromDeviceCertificate(const DeviceCertificate& device_certificate,
RETURN_FALSE_IF_NULL(certificate);
RETURN_FALSE_IF_NULL(private_key);
const bool has_certificate = device_certificate.has_certificate();
const bool has_key = device_certificate.has_wrapped_private_key();
bool has_certificate = device_certificate.has_certificate();
bool has_key = device_certificate.has_wrapped_private_key();
// If no certificate information, nothing to be done. DeviceCertificate
// is a legacy DRM certificate
@@ -364,7 +365,7 @@ const char* DeviceFiles::ResponseTypeToString(ResponseType type) {
// static
std::set<std::string> DeviceFiles::reserved_license_ids_;
DeviceFiles::DeviceFiles(FileSystem* file_system)
DeviceFiles::DeviceFiles(wvutil::FileSystem* file_system)
: file_system_(file_system),
security_level_(kSecurityLevelUninitialized),
initialized_(false) {}
@@ -373,7 +374,7 @@ DeviceFiles::~DeviceFiles() {}
bool DeviceFiles::Init(CdmSecurityLevel security_level) {
if (!file_system_) {
LOGE("Invalid FileSystem given");
LOGE("Invalid wvutil::FileSystem given");
return false;
}
@@ -427,7 +428,7 @@ bool DeviceFiles::StoreCertificate(const std::string& certificate,
return false;
if (default_certificate) {
Clock clock;
wvutil::Clock clock;
device_certificate->set_acquisition_time_seconds(clock.GetCurrentTime());
}
/* TODO(b/192430982): Renable expiration of legacy DRM certificates
@@ -436,9 +437,9 @@ bool DeviceFiles::StoreCertificate(const std::string& certificate,
// stored, this is a certificate of type kCertificateLegacy.
// The only time when a legacy certificate is stored is when it does not
// have an expiration time. Set expiration time to 6 months +- 2 months.
Clock clock;
wvutil::Clock clock;
const int64_t current_time = clock.GetCurrentTime();
CdmRandomGenerator rng(current_time & 0xffffffff);
wvutil::CdmRandomGenerator rng(current_time & 0xffffffff);
device_certificate->set_expiration_time_seconds(
current_time + kFourMonthsInSeconds +
@@ -537,7 +538,7 @@ DeviceFiles::CertificateState DeviceFiles::RetrieveCertificate(
&creation_time_seconds, &expiration_time_seconds))
return kCertificateInvalid;
Clock clock;
wvutil::Clock clock;
const int64_t current_time = clock.GetCurrentTime();
switch (certificate_type) {
@@ -670,6 +671,126 @@ bool DeviceFiles::RemoveCertificate() {
return true;
}
bool DeviceFiles::RemoveOemCertificate() {
RETURN_FALSE_IF_UNINITIALIZED()
std::string certificate_file_name;
if (GetOemCertificateFileName(&certificate_file_name)) {
return RemoveFile(certificate_file_name);
}
return true;
}
bool DeviceFiles::StoreOemCertificate(const std::string& certificate,
const CryptoWrappedKey& private_key) {
RETURN_FALSE_IF_UNINITIALIZED();
if (certificate.empty()) {
LOGE("Missing certificate information");
return false;
}
if (!private_key.IsValid()) {
LOGE("Private key is invalid");
return false;
}
std::string certificate_file_name;
if (!GetOemCertificateFileName(&certificate_file_name)) {
LOGE("Unable to get certificate file name");
return false;
}
// Fill in file information
video_widevine_client::sdk::File file;
file.set_type(video_widevine_client::sdk::File::OEM_CERTIFICATE);
file.set_version(video_widevine_client::sdk::File::VERSION_1);
OemCertificate* oem_certificate = file.mutable_oem_certificate();
oem_certificate->set_certificate(certificate);
oem_certificate->set_wrapped_private_key(private_key.key());
switch (private_key.type()) {
case wvcdm::CryptoWrappedKey::kRsa:
oem_certificate->set_key_type(OemCertificate::RSA);
break;
case wvcdm::CryptoWrappedKey::kEcc:
oem_certificate->set_key_type(OemCertificate::ECC);
break;
case wvcdm::CryptoWrappedKey::kUninitialized:
default:
LOGE("Unexpected key type: %d", private_key.type());
return false;
}
std::string serialized_file;
file.SerializeToString(&serialized_file);
return StoreFileWithHash(certificate_file_name, serialized_file) == kNoError;
}
DeviceFiles::CertificateState DeviceFiles::RetrieveOemCertificate(
std::string* certificate, CryptoWrappedKey* wrapped_private_key) {
RETURN_CERTIFICATE_STATE_CANNOT_HANDLE_IF_UNINITIALIZED();
RETURN_CERTIFICATE_STATE_CANNOT_HANDLE_IF_NULL(certificate);
RETURN_CERTIFICATE_STATE_CANNOT_HANDLE_IF_NULL(wrapped_private_key);
std::string certificate_file_name;
if (!GetOemCertificateFileName(&certificate_file_name)) {
LOGW("Unable to find certificate file name");
return kCannotHandle;
}
video_widevine_client::sdk::File file;
if (RetrieveHashedFile(certificate_file_name, &file) != kNoError) {
LOGW("Unable to retrieve certificate file");
return kCertificateNotFound;
}
if (file.type() != video_widevine_client::sdk::File::OEM_CERTIFICATE) {
LOGE("Certificate file is of incorrect file type: type = %d",
static_cast<int>(file.type()));
return kCertificateInvalid;
}
if (file.version() != video_widevine_client::sdk::File::VERSION_1) {
LOGE("Certificate file is of incorrect file version: version = %d",
static_cast<int>(file.version()));
return kCertificateInvalid;
}
if (!file.has_oem_certificate()) {
LOGE("Certificate not present");
return kCertificateInvalid;
}
const OemCertificate& oem_certificate = file.oem_certificate();
if (oem_certificate.certificate().empty() ||
oem_certificate.wrapped_private_key().empty()) {
LOGE("Empty certificate or private key");
return kCertificateInvalid;
}
*certificate = oem_certificate.certificate();
wrapped_private_key->Clear();
wrapped_private_key->set_key(oem_certificate.wrapped_private_key());
switch (oem_certificate.key_type()) {
case OemCertificate::RSA:
wrapped_private_key->set_type(wvcdm::CryptoWrappedKey::kRsa);
break;
case OemCertificate::ECC:
wrapped_private_key->set_type(wvcdm::CryptoWrappedKey::kEcc);
break;
default:
LOGW("Unknown key type, defaulting to RSA: type = %d",
oem_certificate.key_type());
wrapped_private_key->set_type(wvcdm::CryptoWrappedKey::kRsa);
break;
}
return kCertificateValid;
}
bool DeviceFiles::HasOemCertificate() {
RETURN_FALSE_IF_UNINITIALIZED();
std::string certificate_file_name;
if (!GetOemCertificateFileName(&certificate_file_name)) {
return false;
}
return FileExists(certificate_file_name);
}
bool DeviceFiles::StoreLicense(const CdmLicenseData& license_data,
ResponseType* result) {
RETURN_FALSE_IF_NULL(result);
@@ -1015,7 +1136,7 @@ bool DeviceFiles::DeleteUsageInfo(const std::string& usage_info_file_name,
if (!found) {
LOGE("Unable to find provider session token: pst = %s",
b2a_hex(provider_session_token).c_str());
wvutil::b2a_hex(provider_session_token).c_str());
return false;
}
@@ -1812,8 +1933,8 @@ DeviceFiles::ResponseType DeviceFiles::StoreFileRaw(
path += name;
auto file =
file_system_->Open(path, FileSystem::kCreate | FileSystem::kTruncate);
auto file = file_system_->Open(
path, wvutil::FileSystem::kCreate | wvutil::FileSystem::kTruncate);
if (!file) {
LOGE("Failed to open file: path = %s", path.c_str());
return kFileOpenFailed;
@@ -1871,7 +1992,7 @@ DeviceFiles::ResponseType DeviceFiles::RetrieveHashedFile(
return kInvalidFileSize;
}
auto file = file_system_->Open(path, FileSystem::kReadOnly);
auto file = file_system_->Open(path, wvutil::FileSystem::kReadOnly);
if (!file) {
return kFileOpenFailed;
}
@@ -1970,23 +2091,30 @@ ssize_t DeviceFiles::GetFileSize(const std::string& name) {
}
bool DeviceFiles::GetCertificateFileName(CertificateType certificate_type,
std::string* file_name) {
RETURN_FALSE_IF_NULL(file_name);
std::string* certificate_file_name) {
RETURN_FALSE_IF_NULL(certificate_file_name);
switch (certificate_type) {
case kCertificateDefault:
*file_name = kCertificateFileName;
*certificate_file_name = wvutil::kCertificateFileName;
return true;
case kCertificateLegacy:
*file_name = kLegacyCertificateFileName;
*certificate_file_name = wvutil::kLegacyCertificateFileName;
return true;
case kCertificateAtsc:
*file_name = kAtscCertificateFileName;
*certificate_file_name = wvutil::kAtscCertificateFileName;
return true;
default:
return false;
}
}
bool DeviceFiles::GetOemCertificateFileName(
std::string* certificate_file_name) {
RETURN_FALSE_IF_NULL(certificate_file_name);
*certificate_file_name = wvutil::kOemCertificateFileName;
return true;
}
std::string DeviceFiles::GetUsageTableFileName() { return kUsageTableFileName; }
std::string DeviceFiles::GetHlsAttributesFileNameExtension() {
@@ -2008,7 +2136,7 @@ std::string DeviceFiles::GetUsageInfoFileName(const std::string& app_id) {
std::string DeviceFiles::GetOkpInfoFileName() { return kOkpInfoFileName; }
std::string DeviceFiles::GetFileNameSafeHash(const std::string& input) {
return Base64SafeEncode(Md5Hash(input));
return wvutil::Base64SafeEncode(Md5Hash(input));
}
} // namespace wvcdm

View File

@@ -20,6 +20,17 @@ message NameValue {
optional string value = 2;
}
message OemCertificate {
enum PrivateKeyType {
RSA = 0;
ECC = 1;
}
optional bytes certificate = 1;
optional bytes wrapped_private_key = 2;
optional PrivateKeyType key_type = 3 [default = RSA];
}
// DRM certificate.
message DeviceCertificate {
enum PrivateKeyType {
RSA = 0;
@@ -173,6 +184,7 @@ message File {
HLS_ATTRIBUTES = 4;
USAGE_TABLE_INFO = 5;
OKP_INFO = 6;
OEM_CERTIFICATE = 7;
}
enum FileVersion { VERSION_1 = 1; }
@@ -185,6 +197,7 @@ message File {
optional HlsAttributes hls_attributes = 6;
optional UsageTableInfo usage_table_info = 7;
optional OtaKeyboxProvisioningInfo okp_info = 8;
optional OemCertificate oem_certificate = 9;
}
message HashedFile {

View File

@@ -9,9 +9,25 @@
#include "log.h"
namespace wvcdm {
namespace {
constexpr int kInvalidKeySessionId = 0;
} // namespace
EntitlementKeySession::EntitlementKeySession(CryptoSessionId oec_session_id,
metrics::CryptoMetrics* metrics)
: ContentKeySession(oec_session_id, metrics), entitled_keys_() {}
: ContentKeySession(oec_session_id, metrics),
key_session_id_(kInvalidKeySessionId) {}
EntitlementKeySession::~EntitlementKeySession() {
if (key_session_id_ != kInvalidKeySessionId) {
OEMCryptoResult result =
OEMCrypto_RemoveEntitledKeySession(key_session_id_);
if (result != OEMCrypto_SUCCESS) {
LOGW("OEMCrypto_RemoveEntitledKeySession failed: status = %d",
static_cast<int>(result));
}
}
}
OEMCryptoResult EntitlementKeySession::LoadKeys(
const std::string& message, const std::string& signature,
@@ -49,6 +65,23 @@ OEMCryptoResult EntitlementKeySession::SelectKey(const std::string& key_id,
return OEMCrypto_ERROR_NO_CONTENT_KEY;
}
OEMCryptoResult result;
// Entitled key session not created. Create one now.
if (key_session_id_ == kInvalidKeySessionId) {
result =
OEMCrypto_CreateEntitledKeySession(oec_session_id_, &key_session_id_);
if (result == OEMCrypto_ERROR_NOT_IMPLEMENTED) {
LOGW("Entitled key session is not supported.");
// Use oec session id as the key session id for backward compatibility.
key_session_id_ = oec_session_id_;
} else if (result != OEMCrypto_SUCCESS ||
key_session_id_ == kInvalidKeySessionId) {
LOGE("OEMCrypto_CreateEntitledKeySession failed: status = %d",
static_cast<int>(result));
return result;
}
}
CryptoKey entitled_content_key = entitled_keys_[key_id];
if (current_loaded_content_keys_[entitled_content_key.entitlement_key_id()] !=
key_id) {
@@ -61,10 +94,9 @@ OEMCryptoResult EntitlementKeySession::SelectKey(const std::string& key_id,
std::string message;
OEMCrypto_EntitledContentKeyObject entitled_key =
MakeOecEntitledKey(entitled_content_key, message);
OEMCryptoResult result = OEMCrypto_SUCCESS;
M_TIME(
result = OEMCrypto_LoadEntitledContentKeys(
oec_session_id_, reinterpret_cast<const uint8_t*>(message.data()),
key_session_id_, reinterpret_cast<const uint8_t*>(message.data()),
message.size(), 1, &entitled_key),
metrics_, oemcrypto_load_entitled_keys_, result);
if (result != OEMCrypto_SUCCESS) {
@@ -76,7 +108,28 @@ OEMCryptoResult EntitlementKeySession::SelectKey(const std::string& key_id,
current_loaded_content_keys_[entitled_content_key.entitlement_key_id()] =
key_id;
}
return ContentKeySession::SelectKey(key_id, cipher_mode);
M_TIME(result = OEMCrypto_SelectKey(
key_session_id_, reinterpret_cast<const uint8_t*>(key_id.data()),
key_id.size(), ToOEMCryptoCipherMode(cipher_mode)),
metrics_, oemcrypto_select_key_, result);
return result;
}
OEMCryptoResult EntitlementKeySession::Decrypt(
const OEMCrypto_SampleDescription* samples, size_t samples_length,
const OEMCrypto_CENCEncryptPatternDesc& pattern) {
size_t total_size = 0;
for (size_t i = 0; i < samples_length; ++i) {
total_size += samples[i].buffers.input_data_length;
}
OEMCryptoResult sts;
M_TIME(sts = OEMCrypto_DecryptCENC(key_session_id_, samples, samples_length,
&pattern),
metrics_, oemcrypto_decrypt_cenc_, sts,
metrics::Pow2Bucket(total_size));
return sts;
}
OEMCrypto_EntitledContentKeyObject EntitlementKeySession::MakeOecEntitledKey(

View File

@@ -452,7 +452,7 @@ bool InitializationData::ConstructWidevineInitData(
}
std::vector<uint8_t> json_init_data =
Base64Decode(uri.substr(pos + kBase64String.size()));
wvutil::Base64Decode(uri.substr(pos + kBase64String.size()));
if (json_init_data.size() == 0) {
LOGV("Base64 decode of json data failed");
return false;
@@ -519,7 +519,7 @@ bool InitializationData::ConstructWidevineInitData(
std::string base64_content_id(json_string, tokens[i].start,
tokens[i].end - tokens[i].start);
std::vector<uint8_t> content_id_data =
Base64Decode(base64_content_id);
wvutil::Base64Decode(base64_content_id);
content_id.assign(reinterpret_cast<const char*>(&content_id_data[0]),
content_id_data.size());
}
@@ -529,7 +529,7 @@ bool InitializationData::ConstructWidevineInitData(
if (tokens[i].type == JSMN_ARRAY) {
number_of_key_ids = tokens[i].size;
} else if (tokens[i].type == JSMN_STRING) {
std::string key_id(a2bs_hex(json_string.substr(
std::string key_id(wvutil::a2bs_hex(json_string.substr(
tokens[i].start, tokens[i].end - tokens[i].start)));
if (key_id.size() == 16) key_ids.push_back(key_id);
--number_of_key_ids;
@@ -600,7 +600,7 @@ bool InitializationData::ExtractHexAttribute(const std::string& attribute_list,
for (size_t i = 2; i < val.size(); ++i) {
if (!isxdigit(val[i])) return false;
}
*value = a2b_hex(val.substr(2, val.size() - 2));
*value = wvutil::a2b_hex(val.substr(2, val.size() - 2));
return result;
}
@@ -695,7 +695,7 @@ void InitializationData::DumpToLogs() const {
video_widevine::WidevinePsshData pssh;
if (!pssh.ParseFromString(data())) {
LOGD("InitData: invalid pssh: %s", b2a_hex(data()).c_str());
LOGD("InitData: invalid pssh: %s", wvutil::b2a_hex(data()).c_str());
return;
}
if (pssh.has_content_id()) {
@@ -729,14 +729,15 @@ void InitializationData::DumpToLogs() const {
LOGD("InitData: Key Sequence %u", pssh.key_sequence());
for (int i = 0; i < pssh.key_ids_size(); i++) {
LOGD("InitData: key_id %d: %s", i, b2a_hex(pssh.key_ids(i)).c_str());
LOGD("InitData: key_id %d: %s", i,
wvutil::b2a_hex(pssh.key_ids(i)).c_str());
}
for (int i = 0; i < pssh.entitled_keys_size(); i++) {
video_widevine::WidevinePsshData_EntitledKey key = pssh.entitled_keys(i);
LOGD("InitData: entitlement_key_id %d: %s -> %s", i,
b2a_hex(key.entitlement_key_id()).c_str(),
b2a_hex(key.key_id()).c_str());
wvutil::b2a_hex(key.entitlement_key_id()).c_str(),
wvutil::b2a_hex(key.key_id()).c_str());
}
}

View File

@@ -182,10 +182,10 @@ CdmLicense::CdmLicense(const CdmSessionId& session_id)
is_offline_(false),
supports_core_messages_(true),
use_privacy_mode_(false),
clock_(new Clock()),
clock_(new wvutil::Clock()),
license_key_type_(kLicenseKeyTypeContent) {}
CdmLicense::CdmLicense(const CdmSessionId& session_id, Clock* clock)
CdmLicense::CdmLicense(const CdmSessionId& session_id, wvutil::Clock* clock)
: crypto_session_(nullptr),
policy_engine_(nullptr),
session_id_(session_id),
@@ -606,7 +606,9 @@ CdmResponseType CdmLicense::HandleKeyResponse(
mac_key_iv.assign(license.key(i).iv());
// Strip off PKCS#5 padding
mac_keys.assign(license.key(i).key().data(), kLicenseMacKeySize);
mac_keys.assign(
license.key(i).key().data(),
std::min(kLicenseMacKeySize, license.key(i).key().size()));
}
}
if (license.policy().can_renew() ||
@@ -641,18 +643,6 @@ CdmResponseType CdmLicense::HandleKeyResponse(
if (license.has_provider_client_token())
provider_client_token_ = license.provider_client_token();
if (license.has_srm_update()) {
status = crypto_session_->LoadSrm(license.srm_update());
switch (status) {
case NO_ERROR:
break;
case SYSTEM_INVALIDATED_ERROR:
return status;
default:
break; // Ignore
}
}
if (license.id().type() == video_widevine::OFFLINE &&
license.policy().can_persist())
is_offline_ = true;
@@ -672,6 +662,11 @@ CdmResponseType CdmLicense::HandleKeyResponse(
renew_with_client_id_ = license.policy().always_include_client_id();
}
// If the field is not set, it will default to false.
status =
crypto_session_->UseSecondaryKey(signed_response.using_secondary_key());
if (status != NO_ERROR) return status;
CdmResponseType resp = NO_CONTENT_KEY;
if (kLicenseKeyTypeEntitlement == key_type) {
resp = HandleEntitlementKeyResponse(is_restore, signed_message,
@@ -764,6 +759,11 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse(
}
CdmResponseType status;
// If the field is not set, it will default to false.
status =
crypto_session_->UseSecondaryKey(signed_response.using_secondary_key());
if (status != NO_ERROR) return status;
if (supports_core_messages()) {
status =
crypto_session_->LoadRenewal(signed_message, core_message, signature);

View File

@@ -117,6 +117,17 @@ message ProxyInfo {
message License {
message Policy {
// Client-side watermarking restrictions for the license.
enum WatermarkingControl {
// Watermarking may or may not be used, provider does not care.
WATERMARKING_CONTROL_UNSPECIFIED = 0;
// Watermarking must not be used. The device must disable watermarking
// if it supports it.
WATERMARKING_FORBIDDEN = 1;
// Watermarking is required if the device supports it.
WATERMARKING_REQUIRED = 2;
}
// Indicates that playback of the content is allowed.
optional bool can_play = 1 [default = false];
@@ -182,6 +193,10 @@ message License {
// soft_enforce_playback_duration must be true. Otherwise, subsequent
// playbacks will not be allowed once rental duration expires. Optional.
optional bool soft_enforce_rental_duration = 15 [default = true];
// Optional requirement to indicate watermarking is allowed.
optional WatermarkingControl watermarking_control = 16
[default = WATERMARKING_CONTROL_UNSPECIFIED];
}
message KeyContainer {
@@ -644,6 +659,7 @@ message ProvisioningRequest {
message AndroidAttestationOtaKeyboxRequest {
// The request contains custom serialized and signed data for the
// Android Attestation OTA request.
// see: go/wv_android_ota
optional bytes ota_request = 1;
}

View File

@@ -126,6 +126,10 @@ typedef OEMCryptoResult (*L1_LoadEntitledContentKeys_t)(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
size_t key_array_length,
const OEMCrypto_EntitledContentKeyObject* key_array);
typedef OEMCryptoResult (*L1_LoadEntitledContentKeys_V16_t)(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
size_t key_array_length,
const OEMCrypto_EntitledContentKeyObject_V16* key_array);
typedef OEMCryptoResult (*L1_LoadEntitledContentKeys_V14_t)(
OEMCrypto_SESSION session, size_t num_keys,
const OEMCrypto_EntitledContentKeyObject_V14* key_array);
@@ -220,7 +224,7 @@ typedef OEMCryptoResult (*L1_DeriveKeysFromSessionKey_t)(
size_t enc_key_context_length);
typedef uint32_t (*L1_APIVersion_t)();
typedef uint8_t (*L1_SecurityPatchLevel_t)();
typedef const char* (*L1_SecurityLevel_t)();
typedef const char* (*L1_SecurityLevel_V16_t)();
typedef OEMCryptoResult (*L1_GetHDCPCapability_V9_t)(uint8_t* current,
uint8_t* maximum);
typedef OEMCryptoResult (*L1_GetHDCPCapability_t)(
@@ -297,7 +301,7 @@ typedef OEMCryptoResult (*L1_ShrinkUsageTableHeader_t)(
typedef OEMCryptoResult (*L1_MoveEntry_t)(OEMCrypto_SESSION session,
uint32_t new_index);
typedef uint32_t (*L1_GetAnalogOutputFlags_t)(void);
typedef const char* (*L1_BuildInformation_t)(void);
typedef const char* (*L1_BuildInformation_V16_t)(void);
typedef uint32_t (*L1_ResourceRatingTier_t)(void);
typedef uint32_t (*L1_SupportsDecryptHash_t)(void);
typedef OEMCryptoResult (*L1_SetDecryptHash_t)(OEMCrypto_SESSION session,
@@ -331,6 +335,40 @@ typedef OEMCryptoResult (*L1_ProcessOTAKeybox_t)(OEMCrypto_SESSION session,
const uint8_t* buffer,
size_t buffer_length,
uint32_t use_test_key);
typedef OEMCryptoResult (*L1_CreateEntitledKeySession_t)(
OEMCrypto_SESSION oec_session, OEMCrypto_SESSION* key_session);
typedef OEMCryptoResult (*L1_RemoveEntitledKeySession_t)(
OEMCrypto_SESSION key_session);
typedef OEMCryptoResult (*L1_GetBootCertificateChain_t)(
uint8_t* bcc, size_t* bcc_size, uint8_t* additional_signature,
size_t* additional_signature_size);
typedef OEMCryptoResult (*L1_GenerateCertificateKeyPair_t)(
OEMCrypto_SESSION session, uint8_t* public_key, size_t* public_key_size,
uint8_t* public_key_signature, size_t* public_key_signature_size,
uint8_t* wrapped_private_key, size_t* wrapped_private_key_size,
OEMCrypto_PrivateKeyType* key_type);
typedef OEMCryptoResult (*L1_InstallOemPrivateKey_t)(
OEMCrypto_SESSION session, OEMCrypto_PrivateKeyType key_type,
const uint8_t* wrapped_private_key, size_t wrapped_private_key_length);
typedef OEMCryptoResult (*L1_ReassociateEntitledKeySession_t)(
OEMCrypto_SESSION key_session, OEMCrypto_SESSION oec_session);
typedef OEMCryptoResult (*L1_LoadCasECMKeys_t)(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
const OEMCrypto_EntitledContentKeyObject* even_key,
const OEMCrypto_EntitledContentKeyObject* odd_key);
typedef OEMCryptoResult (*L1_ProductionReady_t)();
typedef OEMCryptoResult (*L1_Idle_t)(OEMCrypto_IdleState state,
uint32_t os_specific_code);
typedef OEMCryptoResult (*L1_Wake_t)();
typedef OEMCryptoResult (*L1_BuildInformation_t)(char* buffer,
size_t* buffer_length);
typedef OEMCrypto_Security_Level (*L1_SecurityLevel_t)();
typedef OEMCryptoResult (*L1_ReuseUsageEntry_t)(OEMCrypto_SESSION session,
uint32_t usage_entry_number);
typedef OEMCryptoResult (*L1_GetDTCP2Capability_t)(
OEMCrypto_DTCP2_Capability* capability);
typedef OEMCrypto_WatermarkingSupport (*L1_GetWatermarkingSupport_t)();
struct FunctionPointers {
wvcdm::CdmSecurityLevel security_level;
uint32_t version;
@@ -349,6 +387,7 @@ struct FunctionPointers {
L1_LoadLicense_t LoadLicense;
L1_LoadKeys_t LoadKeys;
L1_LoadEntitledContentKeys_t LoadEntitledContentKeys;
L1_LoadEntitledContentKeys_V16_t LoadEntitledContentKeys_V16;
L1_LoadEntitledContentKeys_V14_t LoadEntitledContentKeys_V14;
L1_LoadRenewal_t LoadRenewal;
L1_RefreshKeys_t RefreshKeys;
@@ -374,7 +413,7 @@ struct FunctionPointers {
L1_DeriveKeysFromSessionKey_t DeriveKeysFromSessionKey;
L1_APIVersion_t APIVersion;
L1_SecurityPatchLevel_t SecurityPatchLevel;
L1_SecurityLevel_t SecurityLevel;
L1_SecurityLevel_V16_t SecurityLevel_V16;
L1_GetHDCPCapability_t GetHDCPCapability;
L1_SupportsUsageTable_t SupportsUsageTable;
L1_IsAntiRollbackHwPresent_t IsAntiRollbackHwPresent;
@@ -405,7 +444,7 @@ struct FunctionPointers {
L1_ShrinkUsageTableHeader_t ShrinkUsageTableHeader;
L1_MoveEntry_t MoveEntry;
L1_GetAnalogOutputFlags_t GetAnalogOutputFlags;
L1_BuildInformation_t BuildInformation;
L1_BuildInformation_V16_t BuildInformation_V16;
L1_ResourceRatingTier_t ResourceRatingTier;
L1_SupportsDecryptHash_t SupportsDecryptHash;
L1_SetDecryptHash_t SetDecryptHash;
@@ -416,6 +455,21 @@ struct FunctionPointers {
L1_LoadProvisioning_t LoadProvisioning;
L1_MinorAPIVersion_t MinorAPIVersion;
L1_OPK_SerializationVersion_t OPK_SerializationVersion;
L1_CreateEntitledKeySession_t CreateEntitledKeySession;
L1_RemoveEntitledKeySession_t RemoveEntitledKeySession;
L1_GetBootCertificateChain_t GetBootCertificateChain;
L1_GenerateCertificateKeyPair_t GenerateCertificateKeyPair;
L1_InstallOemPrivateKey_t InstallOemPrivateKey;
L1_ReassociateEntitledKeySession_t ReassociateEntitledKeySession;
L1_LoadCasECMKeys_t LoadCasECMKeys;
L1_ProductionReady_t ProductionReady;
L1_Idle_t Idle;
L1_Wake_t Wake;
L1_SecurityLevel_t SecurityLevel;
L1_BuildInformation_t BuildInformation;
L1_ReuseUsageEntry_t ReuseUsageEntry;
L1_GetDTCP2Capability_t GetDTCP2Capability;
L1_GetWatermarkingSupport_t GetWatermarkingSupport;
L1_LoadKeys_V8_t LoadKeys_V8;
L1_GenerateRSASignature_V8_t GenerateRSASignature_V8;
@@ -452,7 +506,7 @@ class WatchDog {
status_ = OEMCrypto_SUCCESS;
gave_up_ = false;
sandbox_id_ = sandbox_id;
uid_ = wvcdm::GetIpcCallingUid();
uid_ = wvutil::GetIpcCallingUid();
}
// Deleted by either thread.
@@ -467,7 +521,7 @@ class WatchDog {
// Function called by new worker thread.
static void RunWatchDog(void* watcher) {
WatchDog* dog = reinterpret_cast<WatchDog*>(watcher);
wvcdm::SetLoggingUid(dog->uid_);
wvutil::SetLoggingUid(dog->uid_);
dog->DoInit();
dog->SignalDoneAndCleanUp();
}
@@ -492,7 +546,7 @@ class WatchDog {
// Check to see if the failure file was created before that last abort.
void CheckForPreviousFailure(
wvcdm::metrics::OemCryptoDynamicAdapterMetrics* metrics) {
wvcdm::FileSystem file_system;
wvutil::FileSystem file_system;
std::string filename = FailureFilename();
if (!file_system.Exists(filename)) return;
auto file = file_system.Open(filename, file_system.kReadOnly);
@@ -513,7 +567,7 @@ class WatchDog {
// Save the failure file before we abort.
void SaveFailureInformation() {
wvcdm::FileSystem file_system;
wvutil::FileSystem file_system;
std::string filename = FailureFilename();
LOGD("failure filename = %s", filename.c_str());
auto file =
@@ -659,7 +713,7 @@ std::string GetAllowTestKeyboxFile() {
uint32_t GetDebugIgnoreKeyboxCount() {
const std::string filename = GetIgnoreCountFile();
wvcdm::FileSystem file_system;
wvutil::FileSystem file_system;
if (!file_system.Exists(filename)) {
return 0;
}
@@ -690,7 +744,7 @@ uint32_t GetDebugIgnoreKeyboxCount() {
OEMCryptoResult SetDebugIgnoreKeyboxCount(uint32_t count) {
const std::string filename = GetIgnoreCountFile();
wvcdm::FileSystem file_system;
wvutil::FileSystem file_system;
auto file =
file_system.Open(filename, file_system.kCreate | file_system.kTruncate);
if (!file) {
@@ -711,7 +765,7 @@ OEMCryptoResult SetDebugIgnoreKeyboxCount(uint32_t count) {
bool GetAllowTestKeybox() {
const std::string filename = GetAllowTestKeyboxFile();
wvcdm::FileSystem file_system;
wvutil::FileSystem file_system;
if (!file_system.Exists(filename)) {
return 0;
}
@@ -733,7 +787,7 @@ bool GetAllowTestKeybox() {
OEMCryptoResult SetAllowTestKeybox(bool allow) {
const std::string filename = GetAllowTestKeyboxFile();
wvcdm::FileSystem file_system;
wvutil::FileSystem file_system;
auto file =
file_system.Open(filename, file_system.kCreate | file_system.kTruncate);
if (!file) {
@@ -758,7 +812,7 @@ struct LevelSession {
// For backwards compatibility, we need to remember the session's nonce
// so that we can pass it to the ODK library.
uint32_t nonce;
LevelSession() : fcn(0), session(0), nonce(0) {}
LevelSession() : fcn(nullptr), session(0), nonce(0) {}
};
#define QUOTE_DEFINE(A) #A
@@ -835,8 +889,15 @@ class Adapter {
watcher->CheckForPreviousFailure(&metrics);
watcher->StartThread();
if (level3_.BuildInformation) {
LOGI("Level 3 Build Info (v%d): %s", level3_.version,
level3_.BuildInformation());
OEMCryptoResult build_information;
std::string buf;
size_t buf_length = 0;
build_information = level3_.BuildInformation(&buf[0], &buf_length);
if (build_information == OEMCrypto_ERROR_SHORT_BUFFER) {
buf.resize(buf_length);
build_information = level3_.BuildInformation(&buf[0], &buf_length);
}
LOGI("Level 3 Build Info (v%d): %s", level3_.version, buf.c_str());
}
OEMCryptoResult result = watcher->WaitForStatusAndCleanUp(&metrics);
if (Level3_IsInApp()) {
@@ -871,8 +932,15 @@ class Adapter {
if (LoadLevel1(&metrics)) {
LOGD("OEMCrypto_Initialize Level 1 success. I will use level 1.");
if (level1_.BuildInformation) {
LOGI("Level 1 Build Info (v%d): %s", level1_.version,
level1_.BuildInformation());
OEMCryptoResult build_information;
std::string buf;
size_t buf_length = 0;
build_information = level1_.BuildInformation(&buf[0], &buf_length);
if (build_information == OEMCrypto_ERROR_SHORT_BUFFER) {
buf.resize(buf_length);
build_information = level1_.BuildInformation(&buf[0], &buf_length);
}
LOGI("Level 1 Build Info (v%d): %s", level1_.version, buf.c_str());
}
} else {
level1_failed_ = true;
@@ -916,7 +984,7 @@ class Adapter {
}
level1_valid_ = true;
const uint32_t kMinimumVersion = 8;
const uint32_t kMaximumVersion = 16;
const uint32_t kMaximumVersion = 17;
level1_.version = kMinimumVersion;
LOOKUP_ALL(8, Initialize, OEMCrypto_Initialize);
LOOKUP_ALL(8, APIVersion, OEMCrypto_APIVersion);
@@ -990,7 +1058,7 @@ class Adapter {
LOOKUP( 9, 9, GetHDCPCapability_V9, OEMCrypto_GetHDCPCapability_V9);
LOOKUP_ALL(10, GetHDCPCapability, OEMCrypto_GetHDCPCapability);
LOOKUP_ALL(14, GetAnalogOutputFlags, OEMCrypto_GetAnalogOutputFlags);
LOOKUP_ALL(15, BuildInformation, OEMCrypto_BuildInformation);
LOOKUP_ALL(15, BuildInformation_V16, OEMCrypto_BuildInformation_V16);
LOOKUP_ALL(15, ResourceRatingTier, OEMCrypto_ResourceRatingTier);
LOOKUP_ALL( 8, GetKeyData, OEMCrypto_GetKeyData);
LOOKUP_ALL(10, GetMaxNumberOfSessions, OEMCrypto_GetMaxNumberOfSessions);
@@ -1003,7 +1071,7 @@ class Adapter {
LOOKUP_ALL( 8, InstallKeyboxOrOEMCert, OEMCrypto_InstallKeyboxOrOEMCert);
LOOKUP_ALL(10, IsAntiRollbackHwPresent, OEMCrypto_IsAntiRollbackHwPresent);
LOOKUP_ALL( 8, IsKeyboxOrOEMCertValid, OEMCrypto_IsKeyboxOrOEMCertValid);
LOOKUP_ALL(13, IsSRMUpdateSupported, OEMCrypto_IsSRMUpdateSupported);
LOOKUP(13, 16, IsSRMUpdateSupported, OEMCrypto_IsSRMUpdateSupported);
LOOKUP( 8, 15, LoadDeviceRSAKey, OEMCrypto_LoadDeviceRSAKey);
LOOKUP_ALL(16, LoadDRMPrivateKey, OEMCrypto_LoadDRMPrivateKey);
LOOKUP( 8, 8, LoadKeys_V8, OEMCrypto_LoadKeys_V8);
@@ -1014,8 +1082,9 @@ class Adapter {
LOOKUP_ALL(15, LoadKeys, OEMCrypto_LoadKeys);
LOOKUP_ALL(16, LoadLicense, OEMCrypto_LoadLicense);
LOOKUP(14, 14, LoadEntitledContentKeys_V14,OEMCrypto_LoadEntitledContentKeys_V14);
LOOKUP_ALL(15, LoadEntitledContentKeys, OEMCrypto_LoadEntitledContentKeys);
LOOKUP_ALL(13, LoadSRM, OEMCrypto_LoadSRM);
LOOKUP(15, 16, LoadEntitledContentKeys_V16,OEMCrypto_LoadEntitledContentKeys_V16);
LOOKUP_ALL(17, LoadEntitledContentKeys, OEMCrypto_LoadEntitledContentKeys);
LOOKUP(13, 16, LoadSRM, OEMCrypto_LoadSRM);
LOOKUP(10, 13, LoadTestKeybox_V13, OEMCrypto_LoadTestKeybox_V13);
LOOKUP_ALL(14, LoadTestKeybox, OEMCrypto_LoadTestKeybox);
LOOKUP_ALL(10, LoadTestRSAKey, OEMCrypto_LoadTestRSAKey);
@@ -1031,7 +1100,7 @@ class Adapter {
LOOKUP_ALL( 9, ReportUsage, OEMCrypto_ReportUsage);
LOOKUP_ALL( 8, RewrapDeviceRSAKey, OEMCrypto_RewrapDeviceRSAKey);
LOOKUP_ALL(12, RewrapDeviceRSAKey30, OEMCrypto_RewrapDeviceRSAKey30);
LOOKUP_ALL( 8, SecurityLevel, OEMCrypto_SecurityLevel);
LOOKUP_ALL( 8, SecurityLevel_V16, OEMCrypto_SecurityLevel_V16);
LOOKUP_ALL(11, SecurityPatchLevel, OEMCrypto_Security_Patch_Level);
LOOKUP( 8, 13, SelectKey_V13, OEMCrypto_SelectKey_V13);
LOOKUP_ALL(14, SelectKey, OEMCrypto_SelectKey);
@@ -1050,14 +1119,29 @@ class Adapter {
LOOKUP_ALL(16, LoadProvisioning, OEMCrypto_LoadProvisioning);
LOOKUP_ALL(16, MinorAPIVersion, OEMCrypto_MinorAPIVersion);
LOOKUP_ALL(16, OPK_SerializationVersion, OEMCrypto_OPK_SerializationVersion);
LOOKUP_ALL(17, CreateEntitledKeySession, OEMCrypto_CreateEntitledKeySession);
LOOKUP_ALL(17, RemoveEntitledKeySession, OEMCrypto_RemoveEntitledKeySession);
LOOKUP_ALL(17, GetBootCertificateChain, OEMCrypto_GetBootCertificateChain);
LOOKUP_ALL(17, GenerateCertificateKeyPair, OEMCrypto_GenerateCertificateKeyPair);
LOOKUP_ALL(17, InstallOemPrivateKey, OEMCrypto_InstallOemPrivateKey);
LOOKUP_ALL(17, ReassociateEntitledKeySession, OEMCrypto_ReassociateEntitledKeySession);
LOOKUP_ALL(17, LoadCasECMKeys, OEMCrypto_LoadCasECMKeys);
LOOKUP_ALL(17, ProductionReady, OEMCrypto_ProductionReady);
LOOKUP_ALL(16, GenerateOTARequest, OEMCrypto_GenerateOTARequest);
LOOKUP_ALL(16, ProcessOTAKeybox, OEMCrypto_ProcessOTAKeybox);
LOOKUP_ALL(17, Idle, OEMCrypto_Idle);
LOOKUP_ALL(17, Wake, OEMCrypto_Wake);
LOOKUP_ALL(17, SecurityLevel, OEMCrypto_SecurityLevel);
LOOKUP_ALL(17, BuildInformation, OEMCrypto_BuildInformation);
LOOKUP_ALL(17, ReuseUsageEntry, OEMCrypto_ReuseUsageEntry);
LOOKUP_ALL(17, GetDTCP2Capability, OEMCrypto_GetDTCP2Capability);
LOOKUP_ALL(17, GetWatermarkingSupport, OEMCrypto_GetWatermarkingSupport);
// clang-format on
// There was a mistake in version 16.3 of the header that did not rename
// OEMCrypto_AllocateSecureBuffer or OEMCrypto_FreeSecureBuffer
if (level1_.AllocateSecureBuffer == NULL ||
level1_.FreeSecureBuffer == NULL) {
if (level1_.AllocateSecureBuffer == nullptr ||
level1_.FreeSecureBuffer == nullptr) {
level1_.AllocateSecureBuffer = (L1_AllocateSecureBuffer_t)dlsym(
level1_library_, "OEMCrypto_AllocateSecureBuffer");
level1_.FreeSecureBuffer = (L1_FreeSecureBuffer_t)dlsym(
@@ -1065,17 +1149,17 @@ class Adapter {
}
if (level1_.SecurityLevel) {
const char* level = level1_.SecurityLevel();
if (strncmp(level, "L1", 2) == 0) {
OEMCrypto_Security_Level level = level1_.SecurityLevel();
if (level == OEMCrypto_Level1) {
level1_.security_level = wvcdm::kSecurityLevelL1;
} else if (strncmp(level, "L3", 2) == 0) {
} else if (level == OEMCrypto_Level3) {
// It is possible that the dynamically loaded OEMCrypto is really L3.
// We might want to use it if it has better performance than the built
// in L3.
LOGW("L1 OEMCrypto is really L3");
level1_.security_level = wvcdm::kSecurityLevelL3;
} else {
LOGE("Unknown security level %.2s", level);
LOGE("Unknown security level %d", level);
level1_.security_level = wvcdm::kSecurityLevelUnknown;
}
}
@@ -1153,6 +1237,19 @@ class Adapter {
level3_.MaximumUsageTableHeaderSize = Level3_MaximumUsageTableHeaderSize;
level3_.AllocateSecureBuffer = Level3_AllocateSecureBuffer;
level3_.FreeSecureBuffer = Level3_FreeSecureBuffer;
level3_.CreateEntitledKeySession = Level3_CreateEntitledKeySession;
level3_.RemoveEntitledKeySession = Level3_RemoveEntitledKeySession;
level3_.GetBootCertificateChain = Level3_GetBootCertificateChain;
level3_.GenerateCertificateKeyPair = Level3_GenerateCertificateKeyPair;
level3_.InstallOemPrivateKey = Level3_InstallOemPrivateKey;
level3_.ReassociateEntitledKeySession = Level3_ReassociateEntitledKeySession;
level3_.LoadCasECMKeys = Level3_LoadCasECMKeys;
level3_.ProductionReady = Level3_ProductionReady;
level3_.Idle = Level3_Idle;
level3_.Wake = Level3_Wake;
level3_.ReuseUsageEntry = Level3_ReuseUsageEntry;
level3_.GetDTCP2Capability = Level3_GetDTCP2Capability;
level3_.GetWatermarkingSupport = Level3_GetWatermarkingSupport;
// clang-format on
level3_.version = Level3_APIVersion();
@@ -1222,6 +1319,45 @@ class Adapter {
return result;
}
OEMCryptoResult CreateEntitledKeySession(OEMCrypto_SESSION oec_session,
OEMCrypto_SESSION* key_session) {
LevelSession pair = GetSession(oec_session);
if (!pair.fcn) return OEMCrypto_ERROR_INVALID_SESSION;
if (pair.fcn->version < 16) return OEMCrypto_ERROR_NOT_IMPLEMENTED;
if (pair.fcn->CreateEntitledKeySession == nullptr)
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
OEMCryptoResult result =
pair.fcn->CreateEntitledKeySession(pair.session, key_session);
if (result == OEMCrypto_SUCCESS) {
// Copy everything from |pair| except session field.
LevelSession new_session;
new_session.fcn = pair.fcn;
new_session.nonce = pair.nonce;
new_session.session = *key_session;
std::unique_lock<std::mutex> auto_lock(session_map_lock_);
// Make sure session is not already in my list of sessions.
while (session_map_.find(*key_session) != session_map_.end()) {
(*key_session)++;
}
session_map_[*key_session] = new_session;
}
return result;
}
OEMCryptoResult RemoveEntitledKeySession(OEMCrypto_SESSION key_session) {
LevelSession pair = GetSession(key_session);
if (!pair.fcn) return OEMCrypto_ERROR_INVALID_SESSION;
if (pair.fcn->version < 16) return OEMCrypto_ERROR_NOT_IMPLEMENTED;
if (pair.fcn->CreateEntitledKeySession == nullptr)
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
OEMCryptoResult result = pair.fcn->RemoveEntitledKeySession(key_session);
if (result == OEMCrypto_SUCCESS) {
std::unique_lock<std::mutex> auto_lock(session_map_lock_);
session_map_.erase(key_session);
}
return result;
}
// Check the system ID of the keybox. This should only be called if the device
// uses provisioning 2.0.
bool UsingTestKeybox() {
@@ -1244,6 +1380,19 @@ class Adapter {
// If level 1 not initialized, then return level 3's answer.
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
// Figure out provisioning method. Defaults to keybox.
const OEMCrypto_ProvisioningMethod provisioning_method =
level1_.GetProvisioningMethod ? level1_.GetProvisioningMethod()
: OEMCrypto_Keybox;
wvcdm::metrics::OemCryptoDynamicAdapterMetrics& metrics =
wvcdm::metrics::GetDynamicAdapterMetricsInstance();
// If it is provisioning 4.0, we do not need to install a keybox. We assume
// the cert is always valid.
if (provisioning_method == OEMCrypto_BootCertificateChain) {
metrics.OemCryptoDynamicAdapterMetrics::SetInitializationMode(
wvcdm::metrics::OEMCrypto_INITIALIZED_USING_L1_WITH_PROVISIONING_4_0);
return OEMCrypto_SUCCESS;
}
if (!level1_.IsKeyboxOrOEMCertValid) {
// TODO(b/189989043): add metrics.
LOGE("L1 invalid function pointers. Falling back to L3");
@@ -1252,12 +1401,6 @@ class Adapter {
// 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;
// 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.
@@ -1362,7 +1505,7 @@ class Adapter {
// Try to install a keybox from the file system.
OEMCryptoResult TryToInstallKeybox() {
if (!level1_.InstallKeyboxOrOEMCert) return OEMCrypto_ERROR_NOT_IMPLEMENTED;
wvcdm::FileSystem file_system;
wvutil::FileSystem file_system;
std::string filename;
if (!wvcdm::Properties::GetFactoryKeyboxPath(&filename)) {
// No keybox or cert file found. Give up.
@@ -1526,20 +1669,25 @@ uint8_t OEMCrypto_Security_Patch_Level(SecurityLevel level) {
return fcn->SecurityPatchLevel();
}
const char* OEMCrypto_SecurityLevel(SecurityLevel level) {
if (!gAdapter) return "";
OEMCrypto_Security_Level OEMCrypto_SecurityLevel(SecurityLevel level) {
if (!gAdapter) return OEMCrypto_Level_Unknown;
const FunctionPointers* fcn = gAdapter->GetFunctionPointers(level);
if (!fcn) return "";
switch (fcn->security_level) {
case kSecurityLevelL1:
return "L1";
case kSecurityLevelL2:
return "L2";
case kSecurityLevelL3:
return "L3";
default:
return "";
if (!fcn) return OEMCrypto_Level_Unknown;
if (fcn->SecurityLevel == nullptr) {
if (fcn->SecurityLevel_V16 == nullptr) {
return OEMCrypto_Level_Unknown;
}
if (!strncmp("L1", fcn->SecurityLevel_V16(), 2)) {
return OEMCrypto_Level1;
} else if (!strncmp("L2", fcn->SecurityLevel_V16(), 2)) {
return OEMCrypto_Level2;
} else if (!strncmp("L3", fcn->SecurityLevel_V16(), 2)) {
return OEMCrypto_Level3;
} else {
return OEMCrypto_Level_Unknown;
}
}
return fcn->SecurityLevel();
}
OEMCryptoResult OEMCrypto_GetHDCPCapability(
@@ -1577,13 +1725,27 @@ uint32_t OEMCrypto_GetAnalogOutputFlags(SecurityLevel level) {
return fcn->GetAnalogOutputFlags();
}
const char* OEMCrypto_BuildInformation(SecurityLevel level) {
if (!gAdapter) return "<not initialized>";
OEMCryptoResult OEMCrypto_BuildInformation(char* buffer, size_t* buffer_length,
SecurityLevel level) {
if (!gAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
const FunctionPointers* fcn = gAdapter->GetFunctionPointers(level);
if (!fcn) return "<not initialized>";
if (fcn->version < 14) return "pre v15";
if (fcn->BuildInformation == nullptr) return "unknown";
return fcn->BuildInformation();
if (!fcn) return OEMCrypto_ERROR_INVALID_SESSION;
if (fcn->version < 14) return OEMCrypto_ERROR_NOT_IMPLEMENTED;
if (fcn->BuildInformation == nullptr) {
if (fcn->BuildInformation_V16 == nullptr) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
const char* build_info = fcn->BuildInformation_V16();
size_t max_length = strnlen(build_info, 128);
if (*buffer_length < max_length) {
*buffer_length = max_length;
return OEMCrypto_ERROR_SHORT_BUFFER;
}
*buffer_length = max_length;
memcpy(buffer, build_info, *buffer_length);
return OEMCrypto_SUCCESS;
}
return fcn->BuildInformation(buffer, buffer_length);
}
uint32_t OEMCrypto_ResourceRatingTier(SecurityLevel level) {
@@ -1600,7 +1762,7 @@ uint32_t OEMCrypto_SupportsDecryptHash(SecurityLevel level) {
const FunctionPointers* fcn = gAdapter->GetFunctionPointers(level);
if (!fcn) return OEMCrypto_Hash_Not_Supported;
if (fcn->version < 15) return OEMCrypto_Hash_Not_Supported;
if (fcn->BuildInformation == nullptr) return OEMCrypto_Hash_Not_Supported;
if (fcn->SupportsDecryptHash == nullptr) return OEMCrypto_Hash_Not_Supported;
return fcn->SupportsDecryptHash();
}
@@ -1886,8 +2048,9 @@ extern "C" uint32_t OEMCrypto_GetAnalogOutputFlags() {
return OEMCrypto_GetAnalogOutputFlags(kLevelDefault);
}
extern "C" const char* OEMCrypto_BuildInformation() {
return OEMCrypto_BuildInformation(kLevelDefault);
extern "C" OEMCryptoResult OEMCrypto_BuildInformation(char* buffer,
size_t* buffer_length) {
return OEMCrypto_BuildInformation(buffer, buffer_length, kLevelDefault);
}
extern "C" uint32_t OEMCrypto_ResourceRatingTier() {
@@ -1930,8 +2093,8 @@ extern "C" OEMCryptoResult OEMCrypto_LoadKeys_Back_Compat(
key_array_v10[i].key_control =
PointerOrNull(message + key_array[i].key_control.offset,
key_array[i].key_control.length);
if (cipher_modes[i] == OEMCrypto_CipherMode_CBC) {
LOGE("CBC Mode not supported.");
if (cipher_modes[i] == OEMCrypto_CipherMode_CBCS) {
LOGE("CBCS Mode not supported.");
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
}
@@ -2153,7 +2316,7 @@ extern "C" OEMCryptoResult OEMCrypto_LoadKeys(
key_array_v13[i].key_control =
PointerOrNull(message + key_array[i].key_control.offset,
key_array[i].key_control.length);
key_array_v13[i].cipher_mode = OEMCrypto_CipherMode_CTR;
key_array_v13[i].cipher_mode = OEMCrypto_CipherMode_CENC;
}
OEMCrypto_KeyObject_V13* key_array_v13_ptr = nullptr;
if (num_keys > 0) key_array_v13_ptr = &key_array_v13[0];
@@ -2231,7 +2394,7 @@ extern "C" OEMCryptoResult OEMCrypto_LoadEntitledContentKeys(
const OEMCrypto_EntitledContentKeyObject* key_array) {
if (!gAdapter.get()) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
LevelSession pair = gAdapter->GetSession(session);
if (!pair.fcn) return OEMCrypto_ERROR_INVALID_SESSION;
if (!pair.fcn) return OEMCrypto_ERROR_INVALID_ENTITLED_KEY_SESSION;
if (pair.fcn->version < 14) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
@@ -2265,6 +2428,22 @@ extern "C" OEMCryptoResult OEMCrypto_LoadEntitledContentKeys(
return pair.fcn->LoadEntitledContentKeys_V14(pair.session, key_array_length,
ecko_array_v14_ptr);
}
if (pair.fcn->version < 17) {
if (key_array == nullptr) {
return pair.fcn->LoadEntitledContentKeys_V16(
session, message, message_length, key_array_length, nullptr);
}
std::vector<OEMCrypto_EntitledContentKeyObject_V16> key_array_v16(
key_array_length);
for (size_t i = 0; i < key_array_length; i++) {
key_array_v16[i].entitlement_key_id = key_array[i].entitlement_key_id;
key_array_v16[i].content_key_id = key_array[i].content_key_id;
key_array_v16[i].content_key_data_iv = key_array[i].content_key_data_iv;
key_array_v16[i].content_key_data = key_array[i].content_key_data;
}
return pair.fcn->LoadEntitledContentKeys_V16(
session, message, message_length, key_array_length, &key_array_v16[0]);
}
if (pair.fcn->LoadEntitledContentKeys == nullptr) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
@@ -2693,7 +2872,7 @@ extern "C" uint8_t OEMCrypto_Security_Patch_Level() {
return OEMCrypto_Security_Patch_Level(kLevelDefault);
}
extern "C" const char* OEMCrypto_SecurityLevel() {
extern "C" OEMCrypto_Security_Level OEMCrypto_SecurityLevel() {
return OEMCrypto_SecurityLevel(kLevelDefault);
}
@@ -3037,6 +3216,91 @@ extern "C" OEMCryptoResult OEMCrypto_FreeSecureBuffer(
return pair.fcn->FreeSecureBuffer(pair.session, output_descriptor, secure_fd);
}
extern "C" OEMCryptoResult OEMCrypto_CreateEntitledKeySession(
OEMCrypto_SESSION oec_session, OEMCrypto_SESSION* key_session) {
if (!gAdapter) return OEMCrypto_ERROR_OPEN_SESSION_FAILED;
return gAdapter->CreateEntitledKeySession(oec_session, key_session);
}
extern "C" OEMCryptoResult OEMCrypto_RemoveEntitledKeySession(
OEMCrypto_SESSION key_session) {
if (!gAdapter) return OEMCrypto_ERROR_OPEN_SESSION_FAILED;
return gAdapter->RemoveEntitledKeySession(key_session);
}
extern "C" OEMCryptoResult OEMCrypto_GetBootCertificateChain(
uint8_t* bcc, size_t* bcc_size, uint8_t* additional_signature,
size_t* additional_signature_size) {
if (!gAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
const FunctionPointers* fcn = gAdapter->GetFunctionPointers(kLevelDefault);
if (!fcn) return OEMCrypto_ERROR_NOT_IMPLEMENTED;
if (fcn->GetBootCertificateChain == nullptr)
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
return fcn->GetBootCertificateChain(bcc, bcc_size, additional_signature,
additional_signature_size);
}
extern "C" OEMCryptoResult OEMCrypto_GenerateCertificateKeyPair(
OEMCrypto_SESSION session, uint8_t* public_key, size_t* public_key_size,
uint8_t* public_key_signature, size_t* public_key_signature_size,
uint8_t* wrapped_private_key, size_t* wrapped_private_key_size,
OEMCrypto_PrivateKeyType* key_type) {
if (!gAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
LevelSession pair = gAdapter->GetSession(session);
if (!pair.fcn) return OEMCrypto_ERROR_INVALID_SESSION;
if (pair.fcn->GenerateCertificateKeyPair == nullptr)
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
return pair.fcn->GenerateCertificateKeyPair(
pair.session, public_key, public_key_size, public_key_signature,
public_key_signature_size, wrapped_private_key, wrapped_private_key_size,
key_type);
}
extern "C" OEMCryptoResult OEMCrypto_InstallOemPrivateKey(
OEMCrypto_SESSION session, OEMCrypto_PrivateKeyType key_type,
const uint8_t* wrapped_private_key, size_t wrapped_private_key_length) {
if (!gAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
LevelSession pair = gAdapter->GetSession(session);
if (!pair.fcn) return OEMCrypto_ERROR_INVALID_SESSION;
if (pair.fcn->InstallOemPrivateKey == nullptr)
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
return pair.fcn->InstallOemPrivateKey(
pair.session, key_type, wrapped_private_key, wrapped_private_key_length);
}
extern "C" OEMCryptoResult OEMCrypto_ReassociateEntitledKeySession(
OEMCrypto_SESSION key_session, OEMCrypto_SESSION oec_session) {
if (!gAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
const FunctionPointers* fcn = gAdapter->GetFunctionPointers(kLevelDefault);
if (!fcn) return OEMCrypto_ERROR_NOT_IMPLEMENTED;
if (fcn->version < 17) return OEMCrypto_ERROR_NOT_IMPLEMENTED;
if (fcn->ReassociateEntitledKeySession == nullptr)
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
return fcn->ReassociateEntitledKeySession(key_session, oec_session);
}
extern "C" OEMCryptoResult OEMCrypto_LoadCasECMKeys(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
const OEMCrypto_EntitledContentKeyObject* even_key,
const OEMCrypto_EntitledContentKeyObject* odd_key) {
if (!gAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
const FunctionPointers* fcn = gAdapter->GetFunctionPointers(kLevelDefault);
if (!fcn) return OEMCrypto_ERROR_NOT_IMPLEMENTED;
if (fcn->version < 17) return OEMCrypto_ERROR_NOT_IMPLEMENTED;
if (fcn->LoadCasECMKeys == nullptr) return OEMCrypto_ERROR_NOT_IMPLEMENTED;
return fcn->LoadCasECMKeys(session, message, message_length, even_key,
odd_key);
}
extern "C" OEMCryptoResult OEMCrypto_ProductionReady() {
if (!gAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
const FunctionPointers* fcn = gAdapter->GetFunctionPointers(kLevelDefault);
if (!fcn) return OEMCrypto_ERROR_NOT_IMPLEMENTED;
if (fcn->version < 17) return OEMCrypto_ERROR_NOT_IMPLEMENTED;
if (fcn->ProductionReady == nullptr) return OEMCrypto_ERROR_NOT_IMPLEMENTED;
return fcn->ProductionReady();
}
extern "C" OEMCryptoResult OEMCrypto_OPK_SerializationVersion(
uint32_t* ree_major, uint32_t* ree_minor, uint32_t* tee_major,
uint32_t* tee_minor) {
@@ -3096,3 +3360,53 @@ extern "C" OEMCryptoResult OEMCrypto_ProcessOTAKeybox(OEMCrypto_SESSION session,
}
return result;
}
extern "C" OEMCryptoResult OEMCrypto_Idle(OEMCrypto_IdleState state,
uint32_t os_specific_code) {
if (!gAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
const FunctionPointers* fcn = gAdapter->GetFunctionPointers(kLevelDefault);
if (!fcn) return OEMCrypto_ERROR_NOT_IMPLEMENTED;
if (fcn->version < 17) return OEMCrypto_ERROR_NOT_IMPLEMENTED;
if (fcn->Idle == nullptr) return OEMCrypto_ERROR_NOT_IMPLEMENTED;
return fcn->Idle(state, os_specific_code);
}
extern "C" OEMCryptoResult OEMCrypto_Wake() {
if (!gAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
const FunctionPointers* fcn = gAdapter->GetFunctionPointers(kLevelDefault);
if (!fcn) return OEMCrypto_ERROR_NOT_IMPLEMENTED;
if (fcn->version < 17) return OEMCrypto_ERROR_NOT_IMPLEMENTED;
if (fcn->Wake == nullptr) return OEMCrypto_ERROR_NOT_IMPLEMENTED;
return fcn->Wake();
}
extern "C" OEMCryptoResult OEMCrypto_ReuseUsageEntry(
OEMCrypto_SESSION session, uint32_t usage_entry_number) {
if (!gAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
const FunctionPointers* fcn = gAdapter->GetFunctionPointers(kLevelDefault);
if (!fcn) return OEMCrypto_ERROR_NOT_IMPLEMENTED;
if (fcn->version < 17) return OEMCrypto_ERROR_NOT_IMPLEMENTED;
if (fcn->ReuseUsageEntry == nullptr) return OEMCrypto_ERROR_NOT_IMPLEMENTED;
return fcn->ReuseUsageEntry(session, usage_entry_number);
}
extern "C" OEMCryptoResult OEMCrypto_GetDTCP2Capability(
OEMCrypto_DTCP2_Capability* capability) {
if (!gAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
const FunctionPointers* fcn = gAdapter->GetFunctionPointers(kLevelDefault);
if (!fcn) return OEMCrypto_ERROR_NOT_IMPLEMENTED;
if (fcn->version < 17) return OEMCrypto_ERROR_NOT_IMPLEMENTED;
if (fcn->GetDTCP2Capability == nullptr)
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
return fcn->GetDTCP2Capability(capability);
}
extern "C" OEMCrypto_WatermarkingSupport OEMCrypto_GetWatermarkingSupport() {
if (!gAdapter) return OEMCrypto_WatermarkingError;
const FunctionPointers* fcn = gAdapter->GetFunctionPointers(kLevelDefault);
if (!fcn) return OEMCrypto_WatermarkingError;
if (fcn->version < 17) return OEMCrypto_WatermarkingError;
if (fcn->GetWatermarkingSupport == nullptr)
return OEMCrypto_WatermarkingError;
return fcn->GetWatermarkingSupport();
}

View File

@@ -20,7 +20,7 @@ constexpr int64_t kErrorTime = -1;
// static
std::unique_ptr<SystemFallbackPolicy> SystemFallbackPolicy::Create() {
std::unique_ptr<FileSystem> fs(new FileSystem());
std::unique_ptr<wvutil::FileSystem> fs(new wvutil::FileSystem());
std::unique_ptr<DeviceFiles> device_files(new DeviceFiles(fs.get()));
if (!device_files->Init(kSecurityLevelL1)) {
LOGE("Failed to initialize device files");
@@ -35,7 +35,7 @@ std::unique_ptr<SystemFallbackPolicy> SystemFallbackPolicy::Create() {
// static
std::unique_ptr<SystemFallbackPolicy> SystemFallbackPolicy::CreateForTesting(
Clock* clock) {
wvutil::Clock* clock) {
std::unique_ptr<SystemFallbackPolicy> policy(new SystemFallbackPolicy());
if (clock != nullptr) {
policy->SetClockForTesting(clock);
@@ -46,7 +46,7 @@ std::unique_ptr<SystemFallbackPolicy> SystemFallbackPolicy::CreateForTesting(
// static
std::unique_ptr<SystemFallbackPolicy> SystemFallbackPolicy::CreateForTesting(
const SystemFallbackInfo& info, Clock* clock) {
const SystemFallbackInfo& info, wvutil::Clock* clock) {
std::unique_ptr<SystemFallbackPolicy> policy(new SystemFallbackPolicy());
if (clock != nullptr) {
policy->SetClockForTesting(clock);
@@ -207,7 +207,7 @@ int64_t SystemFallbackPolicy::GenerateInitialBackoffDuration() {
return kFastBackoffDuration;
}
// Use a random backoff period to avoid server spam across all devices.
return static_cast<int64_t>(CdmRandom::RandomInRange(
return static_cast<int64_t>(wvutil::CdmRandom::RandomInRange(
kMinInitialBackoffDuration, kMaxInitialBackoffDuration));
}

View File

@@ -169,7 +169,7 @@ CdmResponseType OtaKeyboxProvisioner::GetProvisioningRequest(
client_id->Clear();
}
LOGI("OTA request generated");
LOGV("ota_request_data = %s", b2a_hex(ota_request_data).c_str());
LOGV("ota_request_data = %s", wvutil::b2a_hex(ota_request_data).c_str());
OtaRequest* ota_request = prov_request.mutable_android_ota_keybox_request();
ota_request->set_ota_request(ota_request_data);
@@ -183,7 +183,7 @@ CdmResponseType OtaKeyboxProvisioner::GetProvisioningRequest(
signed_request.SerializeToString(request);
if (!wvcdm::Properties::provisioning_messages_are_binary()) {
*request = Base64SafeEncodeNoPad(
*request = wvutil::Base64SafeEncodeNoPad(
std::vector<uint8_t>(request->begin(), request->end()));
}
@@ -264,7 +264,7 @@ CdmResponseType OtaKeyboxProvisioner::HandleProvisioningResponse(
kProductionKeybox, ota_response_data);
if (result == NO_ERROR) {
LOGI("OTA response successfully processed");
LOGV("ota_response_data = %s", b2a_hex(ota_response_data).c_str());
LOGV("ota_response_data = %s", wvutil::b2a_hex(ota_response_data).c_str());
fallback_policy_->MarkProvisioned();
response_received_ = true;
} else {

View File

@@ -37,7 +37,7 @@ PolicyEngine::PolicyEngine(CdmSessionId session_id,
event_listener_(event_listener),
license_keys_(new LicenseKeys(crypto_session->GetSecurityLevel())),
policy_timers_(new PolicyTimersV15),
clock_(new Clock) {
clock_(new wvutil::Clock) {
InitDevice(crypto_session);
}
@@ -48,7 +48,7 @@ bool PolicyEngine::CanDecryptContent(const KeyId& key_id) {
return license_keys_->CanDecryptContent(key_id);
} else {
LOGE("Provided content key is not in license: key_id = %s",
b2a_hex(key_id).c_str());
wvutil::b2a_hex(key_id).c_str());
return false;
}
}
@@ -424,7 +424,7 @@ int64_t PolicyEngine::GetCurrentTime() {
return current_time;
}
void PolicyEngine::set_clock(Clock* clock) { clock_.reset(clock); }
void PolicyEngine::set_clock(wvutil::Clock* clock) { clock_.reset(clock); }
void PolicyEngine::SetSecurityLevelForTest(CdmSecurityLevel security_level) {
license_keys_->SetSecurityLevelForTest(security_level);

View File

@@ -153,7 +153,7 @@ SecurityLevel CdmSecurityLevelToRequestedLevel(
} // namespace
UsageTableHeader::UsageTableHeader() : clock_ref_(&clock_) {
file_system_.reset(new FileSystem());
file_system_.reset(new wvutil::FileSystem());
device_files_.reset(new DeviceFiles(file_system_.get()));
}

View File

@@ -4,7 +4,6 @@
#include "wv_cdm_types.h"
#include <stdio.h>
#include <threads.h>
#include "wv_cdm_constants.h"
@@ -43,6 +42,8 @@ const char* CdmClientTokenTypeToString(CdmClientTokenType type) {
return "DrmCert";
case kClientTokenOemCert:
return "OemCert";
case kClientTokenBootCertChain:
return "BootCertChain";
case kClientTokenUninitialized:
return "Uninitialized";
}