Source release v3.0.2

This commit is contained in:
Joey Parrish
2015-10-08 16:57:59 -07:00
parent b5d6be97cb
commit 7a64ef6641
43 changed files with 644 additions and 7803 deletions

6
.gitignore vendored
View File

@@ -1,5 +1,3 @@
_auto_*
# GYP-generated files. # GYP-generated files.
Makefile Makefile
*.Makefile *.Makefile
@@ -14,3 +12,7 @@ out/
# Ignoring backup files. # Ignoring backup files.
*~ *~
# Configure logs from protobuf.
config.status
config.log

Binary file not shown.

View File

@@ -88,6 +88,7 @@ def ImportPlatform(name, gyp_args):
if hasattr(target, 'export_variables'): if hasattr(target, 'export_variables'):
for k, v in target.export_variables.iteritems(): for k, v in target.export_variables.iteritems():
if not os.environ.get(k):
os.environ[k] = v os.environ[k] = v
print ' set %s to %s' % (k, v) print ' set %s to %s' % (k, v)

View File

@@ -130,6 +130,20 @@ class CDM_EXPORT Cdm : public ITimerClient {
// See Cdm::createSession(). // See Cdm::createSession().
class IEventListener { class IEventListener {
public: public:
// A URL to be added to a renewal request message.
// This call will immediately precede the onMessage() call.
// Do not override this call if the URL is not needed.
//
// WARNING: this call exists temporarily to allow interoperation with
// older versions of Chromium and the prefixed EME API. This call will
// be removed in a future release. Therefore: (1) Do not use this call
// unless you are certain that it is needed on your platform for your
// application, and (2) If it is needed, figure how move to a new version
// of Chromium and the unprefixed EME API as soon as possible.
// TODO: Remove this call (see b/24776024).
virtual void onMessageUrl(const std::string& session_id,
const std::string& server_url) {}
// A message (license request, renewal, etc.) to be dispatched to the // A message (license request, renewal, etc.) to be dispatched to the
// application's license server. // application's license server.
// The response, if successful, should be provided back to the CDM via a // The response, if successful, should be provided back to the CDM via a

View File

@@ -1,2 +1,2 @@
// Widevine CE CDM Version // Widevine CE CDM Version
#define CDM_VERSION "v3.0.1-0-g41710d9-ce" #define CDM_VERSION "v3.0.2-0-g161de1b-ce"

View File

@@ -288,13 +288,20 @@ Cdm::Status CdmImpl::generateRequest(const std::string& session_id,
return kInvalidAccess; return kInvalidAccess;
} }
InitializationData init_data_obj(init_data_type_name, init_data); if (init_data.empty()) {
LOGE("Empty init data is not valid.");
if (init_data_obj.IsEmpty()) {
LOGE("Failed to parse init data.");
return kInvalidAccess; return kInvalidAccess;
} }
InitializationData init_data_obj(init_data_type_name, init_data);
if (init_data_obj.IsEmpty()) {
// Note that InitializationData's idea of "empty" includes "failed to find
// and parse a Widevine PSSH". This should not happen for WebM init data,
// which requires no parsing.
LOGE("Failed to parse init data, may not contain a Widevine PSSH.");
return kNotSupported;
}
std::string key_request; std::string key_request;
CdmKeyRequestType key_request_type; CdmKeyRequestType key_request_type;
std::string ignored_server_url; std::string ignored_server_url;
@@ -404,6 +411,8 @@ Cdm::Status CdmImpl::update(const std::string& session_id,
bool predicted_to_be_server_cert_response = bool predicted_to_be_server_cert_response =
property_set_.use_privacy_mode() && property_set_.use_privacy_mode() &&
property_set_.service_certificate().empty(); property_set_.service_certificate().empty();
(void)predicted_to_be_server_cert_response;
// predicted_to_be_server_cert_response is now used when assertions are off.
// NOTE: If the CdmSession object recognizes that this is not the first // NOTE: If the CdmSession object recognizes that this is not the first
// AddKey(), it will internally delegate to RenewKey(). // AddKey(), it will internally delegate to RenewKey().
@@ -640,6 +649,11 @@ void CdmImpl::OnSessionRenewalNeeded(const CdmSessionId& session_id) {
LOGI("A license renewal has been generated."); LOGI("A license renewal has been generated.");
MessageType message_type = kLicenseRenewal; MessageType message_type = kLicenseRenewal;
// Post the server_url before providing the message.
// For systems that still require the server URL,
// the listener will add the URL to its renewal request.
listener_->onMessageUrl(session_id, server_url);
listener_->onMessage(session_id, message_type, message); listener_->onMessage(session_id, message_type, message);
} }
@@ -650,7 +664,6 @@ void CdmImpl::OnSessionKeysChange(const CdmSessionId& session_id,
CdmKeyStatusMap::const_iterator it; CdmKeyStatusMap::const_iterator it;
for (it = keys_status.begin(); it != keys_status.end(); ++it) { for (it = keys_status.begin(); it != keys_status.end(); ++it) {
KeyStatus status;
switch (it->second) { switch (it->second) {
case kKeyStatusUsable: case kKeyStatusUsable:
map[it->first] = kUsable; map[it->first] = kUsable;
@@ -835,7 +848,8 @@ int64_t Clock::GetCurrentTime() {
return host.clock->now() / 1000; return host.clock->now() / 1000;
} }
struct File::Impl { class File::Impl {
public:
std::string name; std::string name;
bool read_only; bool read_only;
bool truncate; bool truncate;

View File

@@ -8,7 +8,8 @@
namespace wvcdm { namespace wvcdm {
struct Lock::Impl { class Lock::Impl {
public:
pthread_mutex_t mutex; pthread_mutex_t mutex;
}; };

View File

@@ -76,6 +76,16 @@ const std::string kCencPersistentInitData = a2bs_hex(
// pssh data: // pssh data:
"08011a0d7769646576696e655f746573" "08011a0d7769646576696e655f746573"
"74220d6f66666c696e655f636c697032"); "74220d6f66666c696e655f636c697032");
const std::string kInvalidCencInitData = a2bs_hex(
"0000000c" // blob size
"61736466" // "asdf" (wrong box type)
"01020304"); // nonsense
const std::string kNonWidevineCencInitData = a2bs_hex(
"00000020" // blob size
"70737368" // "pssh"
"00000000" // flags
"000102030405060708090a0b0c0d0e0f" // unknown system id
"00000000"); // pssh data size
const std::string kWebMInitData = a2bs_hex("deadbeefdeadbeefdeadbeefdeadbeef"); const std::string kWebMInitData = a2bs_hex("deadbeefdeadbeefdeadbeefdeadbeef");
const std::string kKeyIdsInitData = const std::string kKeyIdsInitData =
"{\"kids\":[\"67ef0gd8pvfd0\",\"77ef0gd8pvfd0\"]}"; "{\"kids\":[\"67ef0gd8pvfd0\",\"77ef0gd8pvfd0\"]}";
@@ -582,6 +592,23 @@ TEST_F(CdmTest, GenerateRequest) {
EXPECT_EQ(Cdm::kInvalidAccess, status); EXPECT_EQ(Cdm::kInvalidAccess, status);
Mock::VerifyAndClear(this); Mock::VerifyAndClear(this);
// Try to pass invalid CENC init data.
status = cdm_->createSession(Cdm::kTemporary, &session_id);
ASSERT_EQ(Cdm::kSuccess, status);
EXPECT_CALL(*this, onMessage(session_id, Cdm::kLicenseRequest, _)).Times(0);
status = cdm_->generateRequest(session_id, Cdm::kCenc, kInvalidCencInitData);
EXPECT_EQ(Cdm::kNotSupported, status);
Mock::VerifyAndClear(this);
// Try to pass non-Widevine CENC init data.
status = cdm_->createSession(Cdm::kTemporary, &session_id);
ASSERT_EQ(Cdm::kSuccess, status);
EXPECT_CALL(*this, onMessage(session_id, Cdm::kLicenseRequest, _)).Times(0);
status = cdm_->generateRequest(session_id, Cdm::kCenc,
kNonWidevineCencInitData);
EXPECT_EQ(Cdm::kNotSupported, status);
Mock::VerifyAndClear(this);
// Try a bogus session ID. // Try a bogus session ID.
EXPECT_CALL(*this, onMessage(_, _, _)).Times(0); EXPECT_CALL(*this, onMessage(_, _, _)).Times(0);
status = cdm_->generateRequest(kBogusSessionId, Cdm::kCenc, kCencInitData); status = cdm_->generateRequest(kBogusSessionId, Cdm::kCenc, kCencInitData);

View File

@@ -72,6 +72,7 @@ int main(int argc, char** argv) {
Cdm::Status status = Cdm::initialize( Cdm::Status status = Cdm::initialize(
Cdm::kNoSecureOutput, client_info, g_host, g_host, g_host, &cert_request, Cdm::kNoSecureOutput, client_info, g_host, g_host, g_host, &cert_request,
static_cast<Cdm::LogLevel>(verbosity)); static_cast<Cdm::LogLevel>(verbosity));
(void)status; // status is now used when assertions are turned off.
assert(status == Cdm::kSuccess); assert(status == Cdm::kSuccess);
assert(cert_request.needed == false); assert(cert_request.needed == false);

View File

@@ -26,7 +26,8 @@ class BufferReader {
BufferReader(const uint8_t* buf, size_t size) BufferReader(const uint8_t* buf, size_t size)
: buf_(buf), size_(buf != NULL ? size : 0), pos_(0) {} : buf_(buf), size_(buf != NULL ? size : 0), pos_(0) {}
bool HasBytes(size_t count) { return (pos() + count <= size()); } bool HasBytes(size_t count) const { return pos_ + count <= size_; }
bool IsEOF() const { return pos_ >= size_; }
// Read a value from the stream, performing endian correction, // Read a value from the stream, performing endian correction,
// and advance the stream pointer. // and advance the stream pointer.

View File

@@ -100,7 +100,8 @@ class CdmEngine {
// Query system information // Query system information
virtual CdmResponseType QueryStatus(SecurityLevel security_level, virtual CdmResponseType QueryStatus(SecurityLevel security_level,
CdmQueryMap* info); const std::string& key,
std::string* value);
// Query session information // Query session information
virtual CdmResponseType QuerySessionStatus(const CdmSessionId& session_id, virtual CdmResponseType QuerySessionStatus(const CdmSessionId& session_id,

View File

@@ -136,6 +136,7 @@ class DeviceFiles {
FRIEND_TEST(WvCdmRequestLicenseTest, UnprovisionTest); FRIEND_TEST(WvCdmRequestLicenseTest, UnprovisionTest);
FRIEND_TEST(WvCdmRequestLicenseTest, ForceL3Test); FRIEND_TEST(WvCdmRequestLicenseTest, ForceL3Test);
FRIEND_TEST(WvCdmRequestLicenseTest, UsageInfoRetryTest); FRIEND_TEST(WvCdmRequestLicenseTest, UsageInfoRetryTest);
FRIEND_TEST(WvCdmRequestLicenseTest, UsageReleaseAllTest);
FRIEND_TEST(WvCdmUsageInfoTest, UsageInfo); FRIEND_TEST(WvCdmUsageInfoTest, UsageInfo);
FRIEND_TEST(WvCdmUsageTest, WithClientId); FRIEND_TEST(WvCdmUsageTest, WithClientId);
FRIEND_TEST(WvCdmExtendedDurationTest, UsageOverflowTest); FRIEND_TEST(WvCdmExtendedDurationTest, UsageOverflowTest);

View File

@@ -85,6 +85,8 @@ class PolicyEngine {
bool IsLicenseOrPlaybackDurationExpired(int64_t current_time); bool IsLicenseOrPlaybackDurationExpired(int64_t current_time);
bool CanRenew() { return policy_.can_renew(); }
private: private:
friend class PolicyEngineTest; friend class PolicyEngineTest;

View File

@@ -203,8 +203,10 @@ enum CdmResponseType {
LICENSE_REQUEST_NONCE_GENERATION_ERROR, LICENSE_REQUEST_NONCE_GENERATION_ERROR,
LICENSE_REQUEST_SIGNING_ERROR, LICENSE_REQUEST_SIGNING_ERROR,
EMPTY_LICENSE_REQUEST, EMPTY_LICENSE_REQUEST,
EMPTY_PROVISIONING_CERTIFICATE_2, SECURE_BUFFER_REQUIRED,
DUPLICATE_SESSION_ID_SPECIFIED, DUPLICATE_SESSION_ID_SPECIFIED,
LICENSE_RENEWAL_PROHIBITED,
EMPTY_PROVISIONING_CERTIFICATE_2,
}; };
enum CdmKeyStatus { enum CdmKeyStatus {

View File

@@ -405,91 +405,115 @@ CdmResponseType CdmEngine::RenewKey(const CdmSessionId& session_id,
} }
CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level, CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
CdmQueryMap* key_info) { const std::string& key,
std::string* value) {
LOGI("CdmEngine::QueryStatus"); LOGI("CdmEngine::QueryStatus");
CryptoSession crypto_session; CryptoSession crypto_session;
if (security_level == kLevel3) { if (security_level == kLevel3) {
CdmResponseType status = crypto_session.Open(kLevel3); CdmResponseType status = crypto_session.Open(kLevel3);
if (NO_ERROR != status) return INVALID_QUERY_STATUS; if (NO_ERROR != status) return INVALID_QUERY_STATUS;
} }
switch (crypto_session.GetSecurityLevel()) {
if (key == QUERY_KEY_SECURITY_LEVEL) {
CdmSecurityLevel security_level = crypto_session.GetSecurityLevel();
switch (security_level) {
case kSecurityLevelL1: case kSecurityLevelL1:
(*key_info)[QUERY_KEY_SECURITY_LEVEL] = QUERY_VALUE_SECURITY_LEVEL_L1; *value = QUERY_VALUE_SECURITY_LEVEL_L1;
break; break;
case kSecurityLevelL2: case kSecurityLevelL2:
(*key_info)[QUERY_KEY_SECURITY_LEVEL] = QUERY_VALUE_SECURITY_LEVEL_L2; *value = QUERY_VALUE_SECURITY_LEVEL_L2;
break; break;
case kSecurityLevelL3: case kSecurityLevelL3:
(*key_info)[QUERY_KEY_SECURITY_LEVEL] = QUERY_VALUE_SECURITY_LEVEL_L3; *value = QUERY_VALUE_SECURITY_LEVEL_L3;
break; break;
case kSecurityLevelUninitialized: case kSecurityLevelUninitialized:
case kSecurityLevelUnknown: case kSecurityLevelUnknown:
(*key_info)[QUERY_KEY_SECURITY_LEVEL] = *value = QUERY_VALUE_SECURITY_LEVEL_UNKNOWN;
QUERY_VALUE_SECURITY_LEVEL_UNKNOWN;
break; break;
default: default:
return INVALID_QUERY_KEY; LOGW("CdmEngine::QueryStatus: Unknown security level: %d",
security_level);
return UNKNOWN_ERROR;
} }
} else if (key == QUERY_KEY_DEVICE_ID) {
std::string deviceId; std::string deviceId;
bool success = crypto_session.GetDeviceUniqueId(&deviceId); if (!crypto_session.GetDeviceUniqueId(&deviceId)) {
if (success) { LOGW("CdmEngine::QueryStatus: GetDeviceUniqueId failed");
(*key_info)[QUERY_KEY_DEVICE_ID] = deviceId; return UNKNOWN_ERROR;
} }
*value = deviceId;
} else if (key == QUERY_KEY_SYSTEM_ID) {
uint32_t system_id; uint32_t system_id;
success = crypto_session.GetSystemId(&system_id); if (!crypto_session.GetSystemId(&system_id)) {
if (success) { LOGW("CdmEngine::QueryStatus: GetSystemId failed");
return UNKNOWN_ERROR;
}
std::ostringstream system_id_stream; std::ostringstream system_id_stream;
system_id_stream << system_id; system_id_stream << system_id;
(*key_info)[QUERY_KEY_SYSTEM_ID] = system_id_stream.str(); *value = system_id_stream.str();
} } else if (key == QUERY_KEY_PROVISIONING_ID) {
std::string provisioning_id; std::string provisioning_id;
success = crypto_session.GetProvisioningId(&provisioning_id); if (!crypto_session.GetProvisioningId(&provisioning_id)) {
if (success) { LOGW("CdmEngine::QueryStatus: GetProvisioningId failed");
(*key_info)[QUERY_KEY_PROVISIONING_ID] = provisioning_id; return UNKNOWN_ERROR;
} }
*value = provisioning_id;
} else if (key == QUERY_KEY_CURRENT_HDCP_LEVEL ||
key == QUERY_KEY_MAX_HDCP_LEVEL) {
CryptoSession::HdcpCapability current_hdcp; CryptoSession::HdcpCapability current_hdcp;
CryptoSession::HdcpCapability max_hdcp; CryptoSession::HdcpCapability max_hdcp;
success = crypto_session.GetHdcpCapabilities(&current_hdcp, &max_hdcp); if (!crypto_session.GetHdcpCapabilities(&current_hdcp, &max_hdcp)) {
if (success) { LOGW("CdmEngine::QueryStatus: GetHdcpCapabilities failed");
(*key_info)[QUERY_KEY_CURRENT_HDCP_LEVEL] = MapHdcpVersion(current_hdcp); return UNKNOWN_ERROR;
(*key_info)[QUERY_KEY_MAX_HDCP_LEVEL] = MapHdcpVersion(max_hdcp);
} }
*value = MapHdcpVersion(key == QUERY_KEY_CURRENT_HDCP_LEVEL ? current_hdcp
: max_hdcp);
} else if (key == QUERY_KEY_USAGE_SUPPORT) {
bool supports_usage_reporting; bool supports_usage_reporting;
success = crypto_session.UsageInformationSupport(&supports_usage_reporting); if (!crypto_session.UsageInformationSupport(&supports_usage_reporting)) {
if (success) { LOGW("CdmEngine::QueryStatus: UsageInformationSupport failed");
(*key_info)[QUERY_KEY_USAGE_SUPPORT] = return UNKNOWN_ERROR;
supports_usage_reporting ? QUERY_VALUE_TRUE : QUERY_VALUE_FALSE;
} }
*value = supports_usage_reporting ? QUERY_VALUE_TRUE : QUERY_VALUE_FALSE;
} else if (key == QUERY_KEY_NUMBER_OF_OPEN_SESSIONS) {
size_t number_of_open_sessions; size_t number_of_open_sessions;
success = crypto_session.GetNumberOfOpenSessions(&number_of_open_sessions); if (!crypto_session.GetNumberOfOpenSessions(&number_of_open_sessions)) {
if (success) { LOGW("CdmEngine::QueryStatus: GetNumberOfOpenSessions failed");
return UNKNOWN_ERROR;
}
std::ostringstream open_sessions_stream; std::ostringstream open_sessions_stream;
open_sessions_stream << number_of_open_sessions; open_sessions_stream << number_of_open_sessions;
(*key_info)[QUERY_KEY_NUMBER_OF_OPEN_SESSIONS] = *value = open_sessions_stream.str();
open_sessions_stream.str(); } else if (key == QUERY_KEY_MAX_NUMBER_OF_SESSIONS) {
size_t maximum_number_of_sessions;
if (!crypto_session.GetMaxNumberOfSessions(&maximum_number_of_sessions)) {
LOGW("CdmEngine::QueryStatus: GetMaxNumberOfOpenSessions failed");
return UNKNOWN_ERROR;
} }
size_t maximum_number_of_sessions;
success = crypto_session.GetMaxNumberOfSessions(&maximum_number_of_sessions);
if (success) {
std::ostringstream max_sessions_stream; std::ostringstream max_sessions_stream;
max_sessions_stream << maximum_number_of_sessions; max_sessions_stream << maximum_number_of_sessions;
(*key_info)[QUERY_KEY_MAX_NUMBER_OF_SESSIONS] = *value = max_sessions_stream.str();
max_sessions_stream.str(); } else if (key == QUERY_KEY_OEMCRYPTO_API_VERSION) {
uint32_t api_version;
if (!crypto_session.GetApiVersion(&api_version)) {
LOGW("CdmEngine::QueryStatus: GetApiVersion failed");
return UNKNOWN_ERROR;
} }
uint32_t api_version;
success = crypto_session.GetApiVersion(&api_version);
if (success) {
std::ostringstream api_version_stream; std::ostringstream api_version_stream;
api_version_stream << api_version; api_version_stream << api_version;
(*key_info)[QUERY_KEY_OEMCRYPTO_API_VERSION] = api_version_stream.str(); *value = api_version_stream.str();
} else {
LOGW("CdmEngine::QueryStatus: Unknown status requested, key = %s",
key.c_str());
return INVALID_QUERY_KEY;
} }
return NO_ERROR; return NO_ERROR;
@@ -844,6 +868,11 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
} }
CdmResponseType CdmEngine::ReleaseAllUsageInfo(const std::string& app_id) { CdmResponseType CdmEngine::ReleaseAllUsageInfo(const std::string& app_id) {
if (NULL == usage_property_set_.get()) {
usage_property_set_.reset(new UsagePropertySet());
}
usage_property_set_->set_app_id(app_id);
CdmResponseType status = NO_ERROR; CdmResponseType status = NO_ERROR;
for (int j = kSecurityLevelL1; j < kSecurityLevelUnknown; ++j) { for (int j = kSecurityLevelL1; j < kSecurityLevelUnknown; ++j) {
DeviceFiles handle; DeviceFiles handle;
@@ -854,6 +883,14 @@ CdmResponseType CdmEngine::ReleaseAllUsageInfo(const std::string& app_id) {
"stops", j); "stops", j);
status = RELEASE_ALL_USAGE_INFO_ERROR_1; status = RELEASE_ALL_USAGE_INFO_ERROR_1;
} else { } else {
SecurityLevel security_level =
static_cast<CdmSecurityLevel>(j) == kSecurityLevelL3
? kLevel3
: kLevelDefault;
usage_property_set_->set_security_level(security_level);
usage_session_.reset(
new CdmSession(usage_property_set_.get(),
EMPTY_ORIGIN, NULL, NULL));
CdmResponseType status2 = usage_session_-> CdmResponseType status2 = usage_session_->
DeleteMultipleUsageInformation(provider_session_tokens); DeleteMultipleUsageInformation(provider_session_tokens);
if (status2 != NO_ERROR) { if (status2 != NO_ERROR) {
@@ -866,6 +903,7 @@ CdmResponseType CdmEngine::ReleaseAllUsageInfo(const std::string& app_id) {
status = RELEASE_ALL_USAGE_INFO_ERROR_2; status = RELEASE_ALL_USAGE_INFO_ERROR_2;
} }
} }
usage_session_.reset(NULL);
return status; return status;
} }

View File

@@ -48,6 +48,7 @@ CdmSession::CdmSession(CdmClientPropertySet* cdm_client_property_set,
key_set_id_ = *forced_session_id; key_set_id_ = *forced_session_id;
} else { } else {
bool ok = GenerateKeySetId(&key_set_id_); bool ok = GenerateKeySetId(&key_set_id_);
(void)ok; // ok is now used when assertions are turned off.
assert(ok); assert(ok);
} }
session_id_ = key_set_id_; session_id_ = key_set_id_;

View File

@@ -641,6 +641,11 @@ CdmResponseType CryptoSession::Decrypt(const CdmDecryptionParameters& params) {
buffer_descriptor.type = buffer_descriptor.type =
params.is_secure ? destination_buffer_type_ : OEMCrypto_BufferType_Clear; params.is_secure ? destination_buffer_type_ : OEMCrypto_BufferType_Clear;
if (params.is_secure &&
buffer_descriptor.type == OEMCrypto_BufferType_Clear) {
return SECURE_BUFFER_REQUIRED;
}
switch (buffer_descriptor.type) { switch (buffer_descriptor.type) {
case OEMCrypto_BufferType_Clear: case OEMCrypto_BufferType_Clear:
buffer_descriptor.buffer.clear.address = buffer_descriptor.buffer.clear.address =

View File

@@ -55,7 +55,7 @@ bool InitializationData::ExtractWidevinePssh(const CdmInitData& init_data,
// (optional, if version == 1) K * 16 byte key ID. // (optional, if version == 1) K * 16 byte key ID.
// 4 byte size of PSSH data, exclusive. (N) // 4 byte size of PSSH data, exclusive. (N)
// N byte PSSH data. // N byte PSSH data.
while (1) { while (!reader.IsEOF()) {
size_t start_pos = reader.pos(); size_t start_pos = reader.pos();
// atom size, used for skipping. // atom size, used for skipping.
@@ -128,6 +128,7 @@ bool InitializationData::ExtractWidevinePssh(const CdmInitData& init_data,
"the atom."); "the atom.");
return false; return false;
} }
LOGV("CdmEngine::ExtractWidevinePssh: Skipping non-Widevine PSSH.");
continue; continue;
} }

View File

@@ -327,6 +327,11 @@ CdmResponseType CdmLicense::PrepareKeyUpdateRequest(
return INVALID_PARAMETERS_LIC_2; return INVALID_PARAMETERS_LIC_2;
} }
if (is_renewal && !policy_engine_->CanRenew()) {
LOGE("CdmLicense::PrepareKeyUpdateRequest: license renewal prohibited");
return LICENSE_RENEWAL_PROHIBITED;
}
LicenseRequest license_request; LicenseRequest license_request;
if (is_renewal) if (is_renewal)
license_request.set_type(LicenseRequest::RENEWAL); license_request.set_type(LicenseRequest::RENEWAL);

View File

@@ -0,0 +1,374 @@
// Copyright 2015 Google Inc. All Rights Reserved.
//
// Description:
// Privacy crypto implementation for iOS. This fully implements the
// privacy_crypto methods. This assumes this is compiled on a Mac and is
// being compiled for iOS. Requires iOS 2.0 or later.
//
// This is never included in the default builds. If compiling using the gyp
// files, setting privacy_crypto_impl to "apple" with use this file rather
// than the openssl version.
#include "privacy_crypto.h"
#include <CommonCrypto/CommonCryptor.h>
#include <CommonCrypto/CommonDigest.h>
#include <CoreFoundation/CoreFoundation.h>
#include <memory>
#include <Security/Security.h>
#include <Security/SecKey.h>
#include "string_conversions.h"
#include "log.h"
#define KEYSTORE_NAME "com.google.widevine.publicKey"
namespace {
const int kPssSaltLength = 20;
const int kOaepMinPadding = 2 * CC_SHA1_DIGEST_LENGTH + 1;
template<typename T>
struct CFDeleter {
void operator()(T arg) {
CFRelease(arg);
}
};
template<typename T>
using CF =
std::unique_ptr<typename std::remove_pointer<T>::type, CFDeleter<T> >;
SecKeyRef ImportPublicKey(const std::string& key) {
std::string peerStr = KEYSTORE_NAME;
CF<CFDataRef> peerData(CFDataCreate(NULL,
reinterpret_cast<const UInt8*>(peerStr.c_str()), peerStr.length()));
CF<CFDataRef> keyData(CFDataCreate(NULL,
reinterpret_cast<const UInt8*>(key.c_str()), key.length()));
// Create a selector and delete all old keys.
CF<CFMutableDictionaryRef> deleteAttributes(CFDictionaryCreateMutable(
NULL, 0, NULL, NULL));
CFDictionarySetValue(deleteAttributes.get(), kSecClass, kSecClassKey);
CFDictionarySetValue(deleteAttributes.get(), kSecAttrKeyType,
kSecAttrKeyTypeRSA);
CFDictionarySetValue(deleteAttributes.get(), kSecAttrApplicationTag,
peerData.get());
SecItemDelete(deleteAttributes.get());
// Create attributes to add to the keystore.
CF<CFMutableDictionaryRef> addAttributes(CFDictionaryCreateMutable(
NULL, 0, NULL, NULL));
CFDictionarySetValue(addAttributes.get(), kSecClass, kSecClassKey);
CFDictionarySetValue(addAttributes.get(), kSecAttrKeyType,
kSecAttrKeyTypeRSA);
CFDictionarySetValue(addAttributes.get(), kSecAttrApplicationTag,
peerData.get());
CFDictionarySetValue(addAttributes.get(), kSecValueData,
keyData.get());
CFDictionarySetValue(addAttributes.get(), kSecAttrKeyClass,
kSecAttrKeyClassPublic);
CFDictionarySetValue(addAttributes.get(), kSecReturnPersistentRef,
kCFBooleanTrue);
// Add the key to the keystore.
CFTypeRef temp;
OSStatus status = SecItemAdd(addAttributes.get(), &temp);
CF<CFTypeRef> peer(temp);
if (!peer || (status != noErr && status != errSecDuplicateItem)) {
LOGE("RsaPublicKey::Init: Error adding key to keychain %d", status);
return NULL;
}
// Create attributes for for the query.
CF<CFMutableDictionaryRef> queryAttributes(CFDictionaryCreateMutable(
NULL, 0, NULL, NULL));
CFDictionarySetValue(queryAttributes.get(), kSecClass, kSecClassKey);
CFDictionarySetValue(queryAttributes.get(), kSecAttrApplicationTag,
peerData.get());
CFDictionarySetValue(queryAttributes.get(), kSecAttrKeyType,
kSecAttrKeyTypeRSA);
CFDictionarySetValue(queryAttributes.get(), kSecAttrKeyClass,
kSecAttrKeyClassPublic);
CFDictionarySetValue(queryAttributes.get(), kSecReturnRef, kCFBooleanTrue);
// Query the keychain to get the public key ref.
CFTypeRef keyRef = NULL;
status = SecItemCopyMatching(queryAttributes.get(), &keyRef);
if (status != noErr) {
LOGE("RsaPublicKey::Init: Error getting key from keystore %d", status);
return NULL;
}
return reinterpret_cast<SecKeyRef>(const_cast<void*>(keyRef));
}
// Apply a custom mask generation function (MGF) using the hash function SHA1,
// this is from OpenSSL.
void ApplyMGF1_SHA1(uint8_t *output, size_t outputLength,
const uint8_t* seed, size_t seedLength) {
size_t outputIndex = 0;
for (int i = 0; outputIndex < outputLength; i++) {
uint8_t extra[4];
extra[0] = (uint8_t)((i >> 24) & 0xFF);
extra[1] = (uint8_t)((i >> 16) & 0xFF);
extra[2] = (uint8_t)((i >> 8) & 0xFF);
extra[3] = (uint8_t)(i & 0xFF);
CC_SHA1_CTX ctx;
CC_SHA1_Init(&ctx);
CC_SHA1_Update(&ctx, seed, seedLength);
CC_SHA1_Update(&ctx, extra, 4);
if (outputIndex + CC_SHA1_DIGEST_LENGTH <= outputLength) {
CC_SHA1_Final(output + outputIndex, &ctx);
} else {
uint8_t temp[CC_SHA1_DIGEST_LENGTH];
CC_SHA1_Final(temp, &ctx);
memcpy(output + outputIndex, temp, outputLength - outputIndex);
}
outputIndex += CC_SHA1_DIGEST_LENGTH;
}
}
std::string ApplyOAEPPadding(const std::string& messageStr, size_t rsaSize) {
if (messageStr.length() > rsaSize - kOaepMinPadding ) {
LOGE("RsaPublicKey::Encrypt: message too large to be encrypted (actual %d",
" max allowed %d)", messageStr.size(),
rsaSize - kOaepMinPadding );
return "";
}
// https://tools.ietf.org/html/rfc2437#section-9.1.1.2
//
// result db
// |------------------------------------------------------------------------|
// |0| seed | pHash |000000000|1| M |
// |------------------------------------------------------------------------|
// | |<-mdLength->|<-mdLength->|<-psLen->| |<-------messageLength---------->|
// |<------------paddingLength------------>|
std::string ret;
ret.resize(rsaSize);
size_t messageLength = messageStr.length();
size_t paddingLength = rsaSize - messageLength;
size_t psLen = paddingLength - kOaepMinPadding;
const uint8_t *message = reinterpret_cast<const uint8_t*>(messageStr.data());
uint8_t *result = reinterpret_cast<uint8_t*>(&ret[0]);
uint8_t *seed = result + 1;
uint8_t *db = result + CC_SHA1_DIGEST_LENGTH + 1;
// Initialize db and message
CC_SHA1(NULL, 0, db); // Hash of empty string.
result[rsaSize - messageLength - 1] = 0x1;
memcpy(result + paddingLength, message, messageLength);
// Initialize seed
if (SecRandomCopyBytes(kSecRandomDefault, CC_SHA1_DIGEST_LENGTH, seed)) {
LOGE("RsaPublicKey::Encrypt: unable to get random data %d", errno);
return "";
}
// Create the first mask
std::vector<uint8_t> dbmask;
dbmask.resize(rsaSize - CC_SHA1_DIGEST_LENGTH - 1);
ApplyMGF1_SHA1(dbmask.data(), dbmask.size(), seed, CC_SHA1_DIGEST_LENGTH);
for (int i = 0; i < dbmask.size(); i++) {
db[i] ^= dbmask[i];
}
// Create the second mask
uint8_t seedmask[CC_SHA1_DIGEST_LENGTH];
ApplyMGF1_SHA1(seedmask, CC_SHA1_DIGEST_LENGTH, db, dbmask.size());
for (int i = 0; i < CC_SHA1_DIGEST_LENGTH; i++) {
seed[i] ^= seedmask[i];
}
return ret;
}
bool PSSVerify(const uint8_t *message, size_t messageLength,
const uint8_t *encodedMessage, size_t encodedMessageLength) {
// https://tools.ietf.org/html/rfc3447#section-9.1.2
//
// M'
// |---------------------------------------------------|
// | 00 00 00 00 00 00 00 00 | mHash | salt |
// |---------------------------------------------------|
//
// H = hash(M')
// dbMask = MGF(H)
//
// db
// |------------------------|
// | 00 00 ... 00 01 | salt |
// |------------------------|
// |<----messageLength----->|
//
// maskedDb = db ^ dbMask
// encodedMessage
// |--------------------------------------------------|
// | maskedDb | H | bc |
// |--------------------------------------------------|
if (encodedMessage[encodedMessageLength - 1] != 0xbc) {
return false;
}
const uint8_t *maskedDb = encodedMessage;
size_t dbLength = encodedMessageLength - CC_SHA1_DIGEST_LENGTH - 1;
const uint8_t *H = maskedDb + dbLength;
// Decode db
std::vector<uint8_t> dbMask;
dbMask.resize(dbLength);
ApplyMGF1_SHA1(dbMask.data(), dbMask.size(), H, CC_SHA1_DIGEST_LENGTH);
for (int i = 0; i < dbLength; i++) {
dbMask[i] ^= maskedDb[i];
}
// Verify db
for (int i = 0; i < dbLength - kPssSaltLength - 1; i++) {
if (dbMask[i] != 0) {
return false;
}
}
if (dbMask[dbLength - kPssSaltLength - 1] != 0x01) {
return false;
}
uint8_t *salt = dbMask.data() + (dbLength - kPssSaltLength);
uint8_t mHash[CC_SHA1_DIGEST_LENGTH];
CC_SHA1(message, messageLength, mHash);
// Create our version of the message data (M')
std::vector<uint8_t> dataVec;
dataVec.resize(8 + CC_SHA1_DIGEST_LENGTH + kPssSaltLength);
uint8_t *data = dataVec.data();
memcpy(data + 8, mHash, CC_SHA1_DIGEST_LENGTH);
memcpy(data + 8 + CC_SHA1_DIGEST_LENGTH, salt, kPssSaltLength);
// Verify the hash of the message data.
uint8_t H2[CC_SHA1_DIGEST_LENGTH];
CC_SHA1(data, dataVec.size(), H2);
return !memcmp(H, H2, CC_SHA1_DIGEST_LENGTH);
}
} // namespace
namespace wvcdm {
AesCbcKey::AesCbcKey() {}
AesCbcKey::~AesCbcKey() {}
bool AesCbcKey::Init(const std::string& key) {
assert(key.size() == kCCBlockSizeAES128);
this->key_ = key;
return true;
}
bool AesCbcKey::Encrypt(const std::string& in, std::string* out,
std::string* iv) {
assert(!in.empty());
assert(iv != NULL);
assert(iv->size() == kCCBlockSizeAES128);
assert(out != NULL);
assert(!key_.empty());
std::string temp;
temp.resize(in.length() + kCCBlockSizeAES128);
size_t length;
CCCryptorStatus result = CCCrypt(kCCEncrypt, kCCAlgorithmAES128,
kCCOptionPKCS7Padding, key_.c_str(), key_.length(), iv->c_str(),
in.c_str(), in.length(), &temp[0], temp.size(), &length);
if (result != kCCSuccess) {
LOGE("AesCbcKey::Encrypt: Encryption failure: %d", result);
return false;
}
out->assign(temp, 0, length);
return true;
}
RsaPublicKey::RsaPublicKey() {}
RsaPublicKey::~RsaPublicKey() {}
bool RsaPublicKey::Init(const std::string& serialized_key) {
assert(!serialized_key.empty());
this->serialized_key_ = serialized_key;
return true;
}
bool RsaPublicKey::Encrypt(const std::string& clear_message,
std::string* encrypted_message) {
assert(!clear_message.empty());
assert(encrypted_message != NULL);
SecKeyRef key = ImportPublicKey(serialized_key_);
if (!key) {
return false;
}
size_t rsaSize = SecKeyGetBlockSize(key);
std::string paddedMessage = ApplyOAEPPadding(clear_message, rsaSize);
if (paddedMessage.empty()) {
return false;
}
size_t size = paddedMessage.length();
std::string buffer;
buffer.resize(size);
OSStatus status = SecKeyEncrypt(key, kSecPaddingNone,
reinterpret_cast<const uint8_t*>(paddedMessage.c_str()),
paddedMessage.length(),
reinterpret_cast<uint8_t*>(&buffer[0]), &size);
if (status != errSecSuccess) {
LOGE("RsaPublicKey::Encrypt: Unable to encrypt data %d", status);
return false;
}
encrypted_message->assign(buffer, 0, size);
return true;
}
bool RsaPublicKey::VerifySignature(const std::string& message,
const std::string& signature) {
assert(!message.empty());
assert(!signature.empty());
SecKeyRef key = ImportPublicKey(serialized_key_);
if (!key) {
return false;
}
// "decrypt" the signature
std::vector<uint8_t> buffer;
buffer.resize(signature.length());
size_t size = buffer.size();
OSStatus status = SecKeyEncrypt(key, kSecPaddingNone,
reinterpret_cast<const uint8_t*>(signature.c_str()),
signature.length(),
buffer.data(), &size);
if (status != errSecSuccess) {
LOGE("RsaPublicKey::VerifySignature: Unable to decrypt signature %d",
status);
return false;
}
// Verify the signature
if (!PSSVerify(reinterpret_cast<const uint8_t*>(message.c_str()),
message.length(),
buffer.data(), buffer.size())) {
LOGE("RsaPublicKey::VerifySignature: Unable to verify signature %d",
status);
return false;
}
return true;
}
} // namespace wvcdm

View File

@@ -230,7 +230,6 @@ TEST_F(CdmSessionTest, ReInitFail) {
} }
TEST_F(CdmSessionTest, InitFailCryptoError) { TEST_F(CdmSessionTest, InitFailCryptoError) {
CdmSecurityLevel level = kSecurityLevelL1;
EXPECT_CALL(*crypto_session_, Open(Eq(kLevelDefault))) EXPECT_CALL(*crypto_session_, Open(Eq(kLevelDefault)))
.WillOnce(Return(UNKNOWN_ERROR)); .WillOnce(Return(UNKNOWN_ERROR));

View File

@@ -146,7 +146,7 @@ ConfigTestEnv::ConfigTestEnv(LicenseServerId server_id, bool streaming,
if (!streaming) { if (!streaming) {
key_id_ = license_servers[server_id].offline_key_id; key_id_ = license_servers[server_id].offline_key_id;
if (wvcdm::kGooglePlayServer == server_id) { if (kGooglePlayServer == server_id) {
if (renew) { if (renew) {
client_auth_.append(kGpClientOfflineRenewalQueryParameters); client_auth_.append(kGpClientOfflineRenewalQueryParameters);
} else if (release) { } else if (release) {

View File

@@ -29,7 +29,7 @@ class ConfigTestEnv {
ConfigTestEnv(LicenseServerId server_id, bool streaming); ConfigTestEnv(LicenseServerId server_id, bool streaming);
ConfigTestEnv(LicenseServerId server_id, bool streaming, bool renew, ConfigTestEnv(LicenseServerId server_id, bool streaming, bool renew,
bool release); bool release);
~ConfigTestEnv(){}; ~ConfigTestEnv() {};
const std::string& client_auth() const { return client_auth_; } const std::string& client_auth() const { return client_auth_; }
const KeyId& key_id() const { return key_id_; } const KeyId& key_id() const { return key_id_; }

View File

@@ -1469,7 +1469,6 @@ class DeviceFilesTest : public ::testing::Test {
CdmAppParameterMap app_parameters; CdmAppParameterMap app_parameters;
size_t start_pos = 0; size_t start_pos = 0;
size_t len = str.length(); size_t len = str.length();
bool more = true;
while (start_pos < len) { while (start_pos < len) {
size_t name_end_pos = str.find(' ', start_pos); size_t name_end_pos = str.find(' ', start_pos);
if (name_end_pos == std::string::npos) return app_parameters; if (name_end_pos == std::string::npos) return app_parameters;
@@ -1841,7 +1840,6 @@ TEST_F(DeviceFilesTest, RetrieveLicenses) {
DeviceFiles device_files; DeviceFiles device_files;
EXPECT_TRUE(device_files.Init(kSecurityLevelL1)); EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
device_files.SetTestFile(&file); device_files.SetTestFile(&file);
DeviceFiles::LicenseState license_state;
CdmInitData pssh_data; CdmInitData pssh_data;
CdmKeyMessage key_request; CdmKeyMessage key_request;
CdmKeyResponse key_response; CdmKeyResponse key_response;

View File

@@ -47,6 +47,7 @@ SSL_CTX* InitSslContext() {
return ctx; return ctx;
} }
#if 0
// unused, may be useful for debugging SSL-related issues. // unused, may be useful for debugging SSL-related issues.
void ShowServerCertificate(const SSL* ssl) { void ShowServerCertificate(const SSL* ssl) {
// gets the server certificate // gets the server certificate
@@ -64,6 +65,7 @@ void ShowServerCertificate(const SSL* ssl) {
LOGE("Failed to get server certificate"); LOGE("Failed to get server certificate");
} }
} }
#endif
// Wait for a socket to be ready for reading or writing. // Wait for a socket to be ready for reading or writing.
// Establishing a connection counts as "ready for write". // Establishing a connection counts as "ready for write".

View File

@@ -16,7 +16,6 @@ namespace wvcdm {
namespace { namespace {
const uint32_t kAesBlockSize = 16;
const std::string kAesKey = a2bs_hex("000102030405060708090a0b0c0d0e0f"); const std::string kAesKey = a2bs_hex("000102030405060708090a0b0c0d0e0f");
const std::string kAesIv = a2bs_hex("000102030405060708090a0b0c0d0e0f"); const std::string kAesIv = a2bs_hex("000102030405060708090a0b0c0d0e0f");
const std::string kCencInitDataHdr = a2bs_hex( const std::string kCencInitDataHdr = a2bs_hex(

View File

@@ -338,6 +338,8 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
case EMPTY_LICENSE_REQUEST: *os << "EMPTY_LICENSE_REQUEST"; case EMPTY_LICENSE_REQUEST: *os << "EMPTY_LICENSE_REQUEST";
break; break;
case DUPLICATE_SESSION_ID_SPECIFIED: *os << "DUPLICATE_SESSION_ID_SPECIFIED"; case DUPLICATE_SESSION_ID_SPECIFIED: *os << "DUPLICATE_SESSION_ID_SPECIFIED";
case LICENSE_RENEWAL_PROHIBITED: *os << "LICENSE_RENEWAL_PROHIBITED";
break;
default: default:
*os << "Unknown CdmResponseType"; *os << "Unknown CdmResponseType";
break; break;

View File

@@ -1,53 +0,0 @@
#!/bin/bash
ROOT=$(dirname "$0")
if [ ! -e "$ROOT"/third_party ]; then
# Potentially run from a different folder before packaging.
ROOT=$(dirname "$ROOT")
fi
if [ ! -e "$ROOT"/third_party ]; then
echo "Unable to find third_party sources!" 1>&2
exit 1
fi
ROOT=$(realpath "$ROOT")
TMP=$(mktemp -d)
trap "rm -rf $TMP" EXIT
set -x
set -e
cd $TMP
# Check out and install gyp locally
svn checkout https://gyp.googlecode.com/svn/trunk/ gyp -r1846
rm -rf "$ROOT"/third_party/gyp
mv gyp/pylib/gyp "$ROOT"/third_party/
# Check out and install stringencoders locally
wget https://stringencoders.googlecode.com/files/stringencoders-v3.10.3.tar.gz
tar xzf stringencoders-v3.10.3.tar.gz
(
cd stringencoders-v3.10.3
./configure --with-b64wchars='-_='
make modp_b64w_data.h
make src/modp_b64w.c
mkdir -p "$ROOT"/third_party/stringencoders/src
cp src/modp_b64w.c "$ROOT"/third_party/stringencoders/src/modp_b64w.cpp
cp src/modp_b64w.h modp_b64w_data.h "$ROOT"/third_party/stringencoders/src/
>"$ROOT"/third_party/stringencoders/src/config.h
)
# Check out and install gmock locally
wget https://googlemock.googlecode.com/files/gmock-1.7.0.zip
unzip gmock-1.7.0.zip
rm -rf "$ROOT"/third_party/gmock
mv gmock-1.7.0 "$ROOT"/third_party/gmock
# Check out and install protobuf locally
wget https://protobuf.googlecode.com/files/protobuf-2.5.0.tar.gz
tar xzf protobuf-2.5.0.tar.gz
(cd protobuf-2.5.0 && ./configure)
rm -rf "$ROOT"/third_party/protobuf
mv protobuf-2.5.0 "$ROOT"/third_party/protobuf

View File

@@ -243,7 +243,7 @@ typedef enum OEMCrypto_HDCP_Capability {
HDCP_V1 = 1, // HDCP version 1.0 HDCP_V1 = 1, // HDCP version 1.0
HDCP_V2 = 2, // HDCP version 2.0 HDCP_V2 = 2, // HDCP version 2.0
HDCP_V2_1 = 3, // HDCP version 2.1 HDCP_V2_1 = 3, // HDCP version 2.1
HDCP_V2_2 = 4, // HDCP version 2.2 HDCP_V2_2 = 4, // HDCP version 2.2 Type 1.
HDCP_NO_DIGITAL_OUTPUT = 0xff // No digital output. HDCP_NO_DIGITAL_OUTPUT = 0xff // No digital output.
} OEMCrypto_HDCP_Capability; } OEMCrypto_HDCP_Capability;

View File

@@ -72,9 +72,9 @@ typedef struct {
// Note: The API does not specify a maximum key id length. We specify a // Note: The API does not specify a maximum key id length. We specify a
// maximum just for these tests, so that we have a fixed message size. // maximum just for these tests, so that we have a fixed message size.
const size_t kTestKeyIdMaxLength = 48; const size_t kTestKeyIdMaxLength = 16;
// Most content will use a key id that is 12 bytes long. // Most content will use a key id that is 16 bytes long.
const int kDefaultKeyIdLength = 12; const int kDefaultKeyIdLength = 16;
typedef struct { typedef struct {
uint8_t key_id[kTestKeyIdMaxLength]; uint8_t key_id[kTestKeyIdMaxLength];
size_t key_id_length; size_t key_id_length;
@@ -101,11 +101,6 @@ struct RSAPrivateKeyMessage {
uint32_t nonce; uint32_t nonce;
}; };
struct PaddedPSTReport {
OEMCrypto_PST_Report report;
uint8_t padding[256];
};
// These are test keyboxes. They will not be accepted by production systems. // These are test keyboxes. They will not be accepted by production systems.
// By using known keyboxes for these tests, the results for a given set of // By using known keyboxes for these tests, the results for a given set of
// inputs to a test are predictable and can be compared to the actual results. // inputs to a test are predictable and can be compared to the actual results.
@@ -364,239 +359,6 @@ static const uint8_t kTestRSAPKCS8PrivateKeyInfo2_2048[] = {
0x72, 0x2c, 0xf7, 0xc1, 0x22, 0x36, 0xd9, 0x18, 0x72, 0x2c, 0xf7, 0xc1, 0x22, 0x36, 0xd9, 0x18,
0x56, 0xfe, 0x39, 0x28, 0x33, 0xe0, 0xdb, 0x03 }; 0x56, 0xfe, 0x39, 0x28, 0x33, 0xe0, 0xdb, 0x03 };
// A 2048 bit RSA Public key
// Used to verify the functions that manipulate RSA keys.
static const uint8_t kTestRSAPublicKey2_2048[] = {
0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01,
0x00, 0xa7, 0x00, 0x36, 0x60, 0x65, 0xdc, 0xbd,
0x54, 0x5a, 0x2a, 0x40, 0xb4, 0xe1, 0x15, 0x94,
0x58, 0x11, 0x4f, 0x94, 0x58, 0xdd, 0xde, 0xa7,
0x1f, 0x3c, 0x2c, 0xe0, 0x88, 0x09, 0x29, 0x61,
0x57, 0x67, 0x5e, 0x56, 0x7e, 0xee, 0x27, 0x8f,
0x59, 0x34, 0x9a, 0x2a, 0xaa, 0x9d, 0xb4, 0x4e,
0xfa, 0xa7, 0x6a, 0xd4, 0xc9, 0x7a, 0x53, 0xc1,
0x4e, 0x9f, 0xe3, 0x34, 0xf7, 0x3d, 0xb7, 0xc9,
0x10, 0x47, 0x4f, 0x28, 0xda, 0x3f, 0xce, 0x31,
0x7b, 0xfd, 0x06, 0x10, 0xeb, 0xf7, 0xbe, 0x92,
0xf9, 0xaf, 0xfb, 0x3e, 0x68, 0xda, 0xee, 0x1a,
0x64, 0x4c, 0xf3, 0x29, 0xf2, 0x73, 0x9e, 0x39,
0xd8, 0xf6, 0x6f, 0xd8, 0xb2, 0x80, 0x82, 0x71,
0x8e, 0xb5, 0xa4, 0xf2, 0xc2, 0x3e, 0xcd, 0x0a,
0xca, 0xb6, 0x04, 0xcd, 0x9a, 0x13, 0x8b, 0x54,
0x73, 0x54, 0x25, 0x54, 0x8c, 0xbe, 0x98, 0x7a,
0x67, 0xad, 0xda, 0xb3, 0x4e, 0xb3, 0xfa, 0x82,
0xa8, 0x4a, 0x67, 0x98, 0x56, 0x57, 0x54, 0x71,
0xcd, 0x12, 0x7f, 0xed, 0xa3, 0x01, 0xc0, 0x6a,
0x8b, 0x24, 0x03, 0x96, 0x88, 0xbe, 0x97, 0x66,
0x2a, 0xbc, 0x53, 0xc9, 0x83, 0x06, 0x51, 0x5a,
0x88, 0x65, 0x13, 0x18, 0xe4, 0x3a, 0xed, 0x6b,
0xf1, 0x61, 0x5b, 0x4c, 0xc8, 0x1e, 0xf4, 0xc2,
0xae, 0x08, 0x5e, 0x2d, 0x5f, 0xf8, 0x12, 0x7f,
0xa2, 0xfc, 0xbb, 0x21, 0x18, 0x30, 0xda, 0xfe,
0x40, 0xfb, 0x01, 0xca, 0x2e, 0x37, 0x0e, 0xce,
0xdd, 0x76, 0x87, 0x82, 0x46, 0x0b, 0x3a, 0x77,
0x8f, 0xc0, 0x72, 0x07, 0x2c, 0x7f, 0x9d, 0x1e,
0x86, 0x5b, 0xed, 0x27, 0x29, 0xdf, 0x03, 0x97,
0x62, 0xef, 0x44, 0xd3, 0x5b, 0x3d, 0xdb, 0x9c,
0x5e, 0x1b, 0x7b, 0x39, 0xb4, 0x0b, 0x6d, 0x04,
0x6b, 0xbb, 0xbb, 0x2c, 0x5f, 0xcf, 0xb3, 0x7a,
0x05, 0x02, 0x03, 0x01, 0x00, 0x01 };
// A second 2048 bit RSA key in PKCS#8 PrivateKeyInfo format
// Used to verify the functions that manipulate RSA keys.
static const uint8_t kTestRSAPKCS8PrivateKeyInfo3_2048[] = {
0x30, 0x82, 0x04, 0xbe, 0x02, 0x01, 0x00, 0x30,
0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82,
0x04, 0xa8, 0x30, 0x82, 0x04, 0xa4, 0x02, 0x01,
0x00, 0x02, 0x82, 0x01, 0x01, 0x00, 0xa5, 0xd0,
0xd7, 0x3e, 0x0e, 0x2d, 0xfb, 0x43, 0x51, 0x99,
0xea, 0x40, 0x1e, 0x2d, 0x89, 0xe4, 0xa2, 0x3e,
0xfc, 0x51, 0x3d, 0x0e, 0x83, 0xa7, 0xe0, 0xa5,
0x41, 0x04, 0x1e, 0x14, 0xc5, 0xa7, 0x5c, 0x61,
0x36, 0x44, 0xb3, 0x08, 0x05, 0x5b, 0x14, 0xde,
0x01, 0x0c, 0x32, 0x3c, 0x9a, 0x91, 0x00, 0x50,
0xa8, 0x1d, 0xcc, 0x9f, 0x8f, 0x35, 0xb7, 0xc2,
0x75, 0x08, 0x32, 0x8b, 0x10, 0x3a, 0x86, 0xf9,
0xd7, 0x78, 0xa3, 0x9d, 0x74, 0x10, 0xc6, 0x24,
0xb1, 0x7f, 0xa5, 0xbf, 0x5f, 0xc2, 0xd7, 0x15,
0xa3, 0x1d, 0xe0, 0x15, 0x6b, 0x1b, 0x0e, 0x38,
0xba, 0x34, 0xbc, 0x95, 0x47, 0x94, 0x40, 0x70,
0xac, 0x99, 0x1f, 0x0b, 0x8e, 0x56, 0x93, 0x36,
0x2b, 0x6d, 0x04, 0xe7, 0x95, 0x1a, 0x37, 0xda,
0x16, 0x57, 0x99, 0xee, 0x03, 0x68, 0x16, 0x31,
0xaa, 0xc3, 0xb7, 0x92, 0x75, 0x53, 0xfc, 0xf6,
0x20, 0x55, 0x44, 0xf8, 0xd4, 0x8d, 0x78, 0x15,
0xc7, 0x1a, 0xb6, 0xde, 0x6c, 0xe8, 0x49, 0x5d,
0xaf, 0xa8, 0x4e, 0x6f, 0x7c, 0xe2, 0x6a, 0x4c,
0xd5, 0xe7, 0x8c, 0x8f, 0x0b, 0x5d, 0x3a, 0x09,
0xd6, 0xb3, 0x44, 0xab, 0xe0, 0x35, 0x52, 0x7c,
0x66, 0x85, 0xa4, 0x40, 0xd7, 0x20, 0xec, 0x24,
0x05, 0x06, 0xd9, 0x84, 0x51, 0x5a, 0xd2, 0x38,
0xd5, 0x1d, 0xea, 0x70, 0x2a, 0x21, 0xe6, 0x82,
0xfd, 0xa4, 0x46, 0x1c, 0x4f, 0x59, 0x6e, 0x29,
0x3d, 0xae, 0xb8, 0x8e, 0xee, 0x77, 0x1f, 0x15,
0x33, 0xcf, 0x94, 0x1d, 0x87, 0x3c, 0x37, 0xc5,
0x89, 0xe8, 0x7d, 0x85, 0xb3, 0xbc, 0xe8, 0x62,
0x6a, 0x84, 0x7f, 0xfe, 0x9a, 0x85, 0x3f, 0x39,
0xe8, 0xaa, 0x16, 0xa6, 0x8f, 0x87, 0x7f, 0xcb,
0xc1, 0xd6, 0xf2, 0xec, 0x2b, 0xa7, 0xdd, 0x49,
0x98, 0x7b, 0x6f, 0xdd, 0x69, 0x6d, 0x02, 0x03,
0x01, 0x00, 0x01, 0x02, 0x82, 0x01, 0x00, 0x43,
0x8f, 0x19, 0x83, 0xb1, 0x27, 0x4e, 0xee, 0x98,
0xba, 0xcb, 0x54, 0xa0, 0x77, 0x11, 0x6d, 0xd4,
0x25, 0x31, 0x8c, 0xb0, 0x01, 0xcf, 0xe6, 0x80,
0x83, 0x14, 0x40, 0x67, 0x39, 0x33, 0x67, 0x03,
0x1e, 0xa0, 0x8b, 0xd1, 0x1d, 0xfd, 0x80, 0xa4,
0xb9, 0xe7, 0x57, 0x5e, 0xc8, 0x8e, 0x79, 0x71,
0xd5, 0x6b, 0x09, 0xe9, 0x2b, 0x41, 0xa0, 0x33,
0x64, 0xc9, 0x66, 0x33, 0xa1, 0xb1, 0x55, 0x07,
0x55, 0x98, 0x53, 0x10, 0xe6, 0xc0, 0x39, 0x6d,
0x61, 0xd9, 0xe8, 0x16, 0x52, 0x28, 0xe4, 0x2b,
0xda, 0x27, 0x01, 0xaf, 0x21, 0x4a, 0xe8, 0x55,
0x1d, 0x0b, 0xd1, 0x1c, 0xdc, 0xfd, 0xb3, 0x0b,
0xa6, 0x5c, 0xcc, 0x6e, 0x77, 0xb8, 0xe0, 0xd1,
0x4e, 0x0a, 0xd7, 0x7a, 0x5e, 0x18, 0xc3, 0xfb,
0xe9, 0xa1, 0x9c, 0xc3, 0x9c, 0xd4, 0x4a, 0x7e,
0x70, 0x72, 0x11, 0x18, 0x24, 0x56, 0x24, 0xdf,
0xf8, 0xba, 0xac, 0x5b, 0x54, 0xd3, 0xc4, 0x65,
0x69, 0xc8, 0x79, 0x94, 0x16, 0x88, 0x9a, 0x68,
0x1c, 0xbc, 0xd4, 0xca, 0xec, 0x5e, 0x07, 0x4a,
0xc9, 0x54, 0x7a, 0x4b, 0xdb, 0x19, 0x88, 0xf6,
0xbe, 0x50, 0x9d, 0x9e, 0x9d, 0x88, 0x5b, 0x4a,
0x23, 0x86, 0x2b, 0xa9, 0xa6, 0x6c, 0x70, 0x7d,
0xe1, 0x11, 0xba, 0xbf, 0x03, 0x2e, 0xf1, 0x46,
0x7e, 0x1b, 0xed, 0x06, 0x11, 0x57, 0xad, 0x4a,
0xcb, 0xe5, 0xb1, 0x11, 0x05, 0x0a, 0x30, 0xb1,
0x73, 0x79, 0xcd, 0x7a, 0x04, 0xcc, 0x70, 0xe9,
0x95, 0xe4, 0x27, 0xc2, 0xd5, 0x2d, 0x92, 0x44,
0xdf, 0xb4, 0x94, 0xa8, 0x73, 0xa1, 0x4a, 0xc3,
0xcc, 0xc4, 0x0e, 0x8d, 0xa1, 0x6a, 0xc2, 0xd8,
0x03, 0x7f, 0xfa, 0xa7, 0x76, 0x0d, 0xad, 0x87,
0x88, 0xa0, 0x77, 0xaf, 0x3b, 0x23, 0xd1, 0x66,
0x0b, 0x31, 0x2b, 0xaf, 0xef, 0xd5, 0x41, 0x02,
0x81, 0x81, 0x00, 0xdb, 0xc1, 0xe7, 0xdd, 0xba,
0x3c, 0x1f, 0x9c, 0x64, 0xca, 0xa0, 0x63, 0xdb,
0xd2, 0x47, 0x5c, 0x6e, 0x8a, 0xa3, 0x16, 0xd5,
0xda, 0xc2, 0x25, 0x64, 0x0a, 0x02, 0xbc, 0x7d,
0x7f, 0x50, 0xab, 0xe0, 0x66, 0x03, 0x53, 0x7d,
0x77, 0x6d, 0x6c, 0x61, 0x58, 0x09, 0x73, 0xcd,
0x18, 0xe9, 0x53, 0x0b, 0x5c, 0xa2, 0x71, 0x14,
0x02, 0xfd, 0x55, 0xda, 0xe9, 0x77, 0x24, 0x7c,
0x2a, 0x4e, 0xb9, 0xd9, 0x5d, 0x58, 0xf6, 0x26,
0xd0, 0xd8, 0x3d, 0xcf, 0x8c, 0x89, 0x65, 0x6c,
0x35, 0x19, 0xb6, 0x63, 0xff, 0xa0, 0x71, 0x49,
0xcd, 0x6d, 0x5b, 0x3d, 0x8f, 0xea, 0x6f, 0xa9,
0xba, 0x43, 0xe5, 0xdd, 0x39, 0x3a, 0x78, 0x8f,
0x07, 0xb8, 0xab, 0x58, 0x07, 0xb7, 0xd2, 0xf8,
0x07, 0x02, 0x9b, 0x79, 0x26, 0x32, 0x22, 0x38,
0x91, 0x01, 0x90, 0x81, 0x29, 0x94, 0xad, 0x77,
0xeb, 0x86, 0xb9, 0x02, 0x81, 0x81, 0x00, 0xc1,
0x29, 0x88, 0xbd, 0x96, 0x31, 0x33, 0x7b, 0x77,
0x5d, 0x32, 0x12, 0x5e, 0xdf, 0x28, 0x0c, 0x96,
0x0d, 0xa8, 0x22, 0xdf, 0xd3, 0x35, 0xd7, 0xb0,
0x41, 0xcb, 0xe7, 0x94, 0x8a, 0xa4, 0xed, 0xd2,
0xfb, 0xd2, 0xf3, 0xf2, 0x95, 0xff, 0xd8, 0x33,
0x3f, 0x8c, 0xd7, 0x65, 0xe4, 0x0c, 0xcc, 0xfe,
0x32, 0x66, 0xfa, 0x50, 0xe2, 0xcf, 0xf0, 0xbe,
0x05, 0xb1, 0xbc, 0xbe, 0x44, 0x09, 0xb4, 0xfe,
0x95, 0x06, 0x18, 0xd7, 0x59, 0xc6, 0xef, 0x2d,
0x22, 0xa0, 0x73, 0x5e, 0x77, 0xdf, 0x8d, 0x09,
0x2c, 0xb8, 0xcc, 0xeb, 0x10, 0x4d, 0xa7, 0xd0,
0x4b, 0x46, 0xba, 0x7d, 0x8b, 0x6a, 0x55, 0x47,
0x55, 0xd3, 0xd7, 0xb1, 0x88, 0xfd, 0x27, 0x3e,
0xf9, 0x5b, 0x7b, 0xae, 0x6d, 0x08, 0x9f, 0x0c,
0x2a, 0xe1, 0xdd, 0xb9, 0xe3, 0x55, 0x13, 0x55,
0xa3, 0x6d, 0x06, 0xbb, 0xe0, 0x1e, 0x55, 0x02,
0x81, 0x80, 0x61, 0x73, 0x3d, 0x64, 0xff, 0xdf,
0x05, 0x8d, 0x8e, 0xcc, 0xa4, 0x0f, 0x64, 0x3d,
0x7d, 0x53, 0xa9, 0xd9, 0x64, 0xb5, 0x0d, 0xa4,
0x72, 0x8f, 0xae, 0x2b, 0x1a, 0x47, 0x87, 0xc7,
0x5b, 0x78, 0xbc, 0x8b, 0xc0, 0x51, 0xd7, 0xc3,
0x8c, 0x0c, 0x91, 0xa6, 0x3e, 0x9a, 0xd1, 0x8a,
0x88, 0x7d, 0x40, 0xfe, 0x95, 0x32, 0x5b, 0xd3,
0x6f, 0x90, 0x11, 0x01, 0x92, 0xc9, 0xe5, 0x1d,
0xc5, 0xc7, 0x78, 0x72, 0x82, 0xae, 0xb5, 0x4b,
0xcb, 0x78, 0xad, 0x7e, 0xfe, 0xb6, 0xb1, 0x23,
0x63, 0x01, 0x94, 0x9a, 0x99, 0x05, 0x63, 0xda,
0xea, 0xf1, 0x98, 0xfd, 0x26, 0xd2, 0xd9, 0x8b,
0x35, 0xec, 0xcb, 0x0b, 0x43, 0xb8, 0x8e, 0x84,
0xb8, 0x09, 0x93, 0x81, 0xe8, 0xac, 0x6f, 0x3c,
0x7c, 0x95, 0x81, 0x45, 0xc4, 0xd9, 0x94, 0x08,
0x09, 0x8f, 0x91, 0x17, 0x65, 0x4c, 0xff, 0x6e,
0xbc, 0x51, 0x02, 0x81, 0x81, 0x00, 0xc1, 0x0d,
0x9d, 0xd8, 0xbd, 0xaf, 0x56, 0xe0, 0xe3, 0x1f,
0x85, 0xd7, 0xce, 0x72, 0x02, 0x38, 0xf2, 0x0f,
0x9c, 0x27, 0x9e, 0xc4, 0x1d, 0x60, 0x00, 0x8d,
0x02, 0x19, 0xe5, 0xdf, 0xdb, 0x8e, 0xc5, 0xfb,
0x61, 0x8e, 0xe6, 0xb8, 0xfc, 0x07, 0x3c, 0xd1,
0x1b, 0x16, 0x7c, 0x83, 0x3c, 0x37, 0xf5, 0x26,
0xb2, 0xbd, 0x22, 0xf2, 0x4d, 0x19, 0x33, 0x11,
0xc5, 0xdd, 0xf9, 0xdb, 0x4e, 0x48, 0x52, 0xd8,
0xe6, 0x4b, 0x15, 0x90, 0x68, 0xbe, 0xca, 0xc1,
0x7c, 0xd3, 0x51, 0x6b, 0x45, 0x46, 0x54, 0x11,
0x1a, 0x71, 0xd3, 0xcd, 0x6b, 0x8f, 0x79, 0x22,
0x83, 0x02, 0x08, 0x4f, 0xba, 0x6a, 0x98, 0xed,
0x32, 0xd8, 0xb4, 0x5b, 0x51, 0x88, 0x53, 0xec,
0x2c, 0x7e, 0xa4, 0x89, 0xdc, 0xbf, 0xf9, 0x0d,
0x32, 0xc8, 0xc3, 0xec, 0x6d, 0x2e, 0xf1, 0xbc,
0x70, 0x4e, 0xf6, 0x9e, 0xbc, 0x31, 0x02, 0x81,
0x81, 0x00, 0xd3, 0x35, 0x1b, 0x19, 0x75, 0x3f,
0x61, 0xf2, 0x55, 0x03, 0xce, 0x25, 0xa9, 0xdf,
0x0c, 0x0a, 0x3b, 0x47, 0x42, 0xdc, 0x38, 0x4b,
0x13, 0x4d, 0x1f, 0x86, 0x58, 0x4f, 0xd8, 0xee,
0xfa, 0x76, 0x15, 0xfb, 0x6e, 0x55, 0x31, 0xf2,
0xd2, 0x62, 0x32, 0xa5, 0xc4, 0x23, 0x5e, 0x08,
0xa9, 0x83, 0x07, 0xac, 0x8c, 0xa3, 0x7e, 0x18,
0xc0, 0x1c, 0x57, 0x63, 0x8d, 0x05, 0x17, 0x47,
0x1b, 0xd3, 0x74, 0x73, 0x20, 0x04, 0xfb, 0xc8,
0x1a, 0x43, 0x04, 0x36, 0xc8, 0x19, 0xbe, 0xdc,
0xa6, 0xe5, 0x0f, 0x25, 0x62, 0x24, 0x96, 0x92,
0xb6, 0xb3, 0x97, 0xad, 0x57, 0x9a, 0x90, 0x37,
0x4e, 0x31, 0x44, 0x74, 0xfa, 0x7c, 0xb4, 0xea,
0xfc, 0x15, 0xa7, 0xb0, 0x51, 0xcc, 0xee, 0x1e,
0xed, 0x5b, 0x98, 0x18, 0x0e, 0x65, 0xb6, 0x4b,
0x69, 0x0b, 0x21, 0xdc, 0x86, 0x17, 0x6e, 0xc8,
0xee, 0x24 };
// A second 2048 bit RSA Public key
// Used to verify the functions that manipulate RSA keys.
static const uint8_t kTestRSAPublicKey3_2048[] = {
0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01,
0x00, 0xa5, 0xd0, 0xd7, 0x3e, 0x0e, 0x2d, 0xfb,
0x43, 0x51, 0x99, 0xea, 0x40, 0x1e, 0x2d, 0x89,
0xe4, 0xa2, 0x3e, 0xfc, 0x51, 0x3d, 0x0e, 0x83,
0xa7, 0xe0, 0xa5, 0x41, 0x04, 0x1e, 0x14, 0xc5,
0xa7, 0x5c, 0x61, 0x36, 0x44, 0xb3, 0x08, 0x05,
0x5b, 0x14, 0xde, 0x01, 0x0c, 0x32, 0x3c, 0x9a,
0x91, 0x00, 0x50, 0xa8, 0x1d, 0xcc, 0x9f, 0x8f,
0x35, 0xb7, 0xc2, 0x75, 0x08, 0x32, 0x8b, 0x10,
0x3a, 0x86, 0xf9, 0xd7, 0x78, 0xa3, 0x9d, 0x74,
0x10, 0xc6, 0x24, 0xb1, 0x7f, 0xa5, 0xbf, 0x5f,
0xc2, 0xd7, 0x15, 0xa3, 0x1d, 0xe0, 0x15, 0x6b,
0x1b, 0x0e, 0x38, 0xba, 0x34, 0xbc, 0x95, 0x47,
0x94, 0x40, 0x70, 0xac, 0x99, 0x1f, 0x0b, 0x8e,
0x56, 0x93, 0x36, 0x2b, 0x6d, 0x04, 0xe7, 0x95,
0x1a, 0x37, 0xda, 0x16, 0x57, 0x99, 0xee, 0x03,
0x68, 0x16, 0x31, 0xaa, 0xc3, 0xb7, 0x92, 0x75,
0x53, 0xfc, 0xf6, 0x20, 0x55, 0x44, 0xf8, 0xd4,
0x8d, 0x78, 0x15, 0xc7, 0x1a, 0xb6, 0xde, 0x6c,
0xe8, 0x49, 0x5d, 0xaf, 0xa8, 0x4e, 0x6f, 0x7c,
0xe2, 0x6a, 0x4c, 0xd5, 0xe7, 0x8c, 0x8f, 0x0b,
0x5d, 0x3a, 0x09, 0xd6, 0xb3, 0x44, 0xab, 0xe0,
0x35, 0x52, 0x7c, 0x66, 0x85, 0xa4, 0x40, 0xd7,
0x20, 0xec, 0x24, 0x05, 0x06, 0xd9, 0x84, 0x51,
0x5a, 0xd2, 0x38, 0xd5, 0x1d, 0xea, 0x70, 0x2a,
0x21, 0xe6, 0x82, 0xfd, 0xa4, 0x46, 0x1c, 0x4f,
0x59, 0x6e, 0x29, 0x3d, 0xae, 0xb8, 0x8e, 0xee,
0x77, 0x1f, 0x15, 0x33, 0xcf, 0x94, 0x1d, 0x87,
0x3c, 0x37, 0xc5, 0x89, 0xe8, 0x7d, 0x85, 0xb3,
0xbc, 0xe8, 0x62, 0x6a, 0x84, 0x7f, 0xfe, 0x9a,
0x85, 0x3f, 0x39, 0xe8, 0xaa, 0x16, 0xa6, 0x8f,
0x87, 0x7f, 0xcb, 0xc1, 0xd6, 0xf2, 0xec, 0x2b,
0xa7, 0xdd, 0x49, 0x98, 0x7b, 0x6f, 0xdd, 0x69,
0x6d, 0x02, 0x03, 0x01, 0x00, 0x01 };
DeviceFeatures global_features; DeviceFeatures global_features;
void DeviceFeatures::Initialize(bool is_cast_receiver, bool force_load_test_keybox) { void DeviceFeatures::Initialize(bool is_cast_receiver, bool force_load_test_keybox) {
@@ -1364,7 +1126,8 @@ class Session {
int status = RSA_public_encrypt(session_key.size(), &session_key[0], int status = RSA_public_encrypt(session_key.size(), &session_key[0],
&(enc_session_key->front()), public_rsa_, &(enc_session_key->front()), public_rsa_,
RSA_PKCS1_OAEP_PADDING); RSA_PKCS1_OAEP_PADDING);
if (static_cast<unsigned>(status) != RSA_size(public_rsa_)) { int size = static_cast<int>(RSA_size(public_rsa_));
if (status != size) {
cout << "GenerateRSASessionKey error encrypting session key. "; cout << "GenerateRSASessionKey error encrypting session key. ";
dump_openssl_error(); dump_openssl_error();
return false; return false;
@@ -1403,16 +1166,17 @@ class Session {
size_t length = 0; size_t length = 0;
OEMCryptoResult sts = OEMCrypto_ReportUsage( OEMCryptoResult sts = OEMCrypto_ReportUsage(
session_id(), reinterpret_cast<const uint8_t*>(pst.c_str()), session_id(), reinterpret_cast<const uint8_t*>(pst.c_str()),
pst.length(), &pst_report_.report, &length); pst.length(), pst_report(), &length);
if (expect_success) { if (expect_success) {
ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts);
} }
if (sts == OEMCrypto_ERROR_SHORT_BUFFER) { if (sts == OEMCrypto_ERROR_SHORT_BUFFER) {
ASSERT_GE(sizeof(PaddedPSTReport), length); ASSERT_LE(sizeof(OEMCrypto_PST_Report), length);
pst_report_buffer_.resize(length);
} }
sts = OEMCrypto_ReportUsage(session_id(), sts = OEMCrypto_ReportUsage(session_id(),
reinterpret_cast<const uint8_t*>(pst.c_str()), reinterpret_cast<const uint8_t*>(pst.c_str()),
pst.length(), &pst_report_.report, &length); pst.length(), pst_report(), &length);
if (!expect_success) { if (!expect_success) {
ASSERT_NE(OEMCrypto_SUCCESS, sts); ASSERT_NE(OEMCrypto_SUCCESS, sts);
return; return;
@@ -1421,16 +1185,19 @@ class Session {
vector<uint8_t> computed_signature(SHA_DIGEST_LENGTH); vector<uint8_t> computed_signature(SHA_DIGEST_LENGTH);
unsigned int sig_len = SHA_DIGEST_LENGTH; unsigned int sig_len = SHA_DIGEST_LENGTH;
HMAC(EVP_sha1(), &mac_key_client_[0], mac_key_client_.size(), HMAC(EVP_sha1(), &mac_key_client_[0], mac_key_client_.size(),
reinterpret_cast<uint8_t*>(&pst_report_.report) + SHA_DIGEST_LENGTH, reinterpret_cast<uint8_t*>(pst_report()) + SHA_DIGEST_LENGTH,
length - SHA_DIGEST_LENGTH, &computed_signature[0], &sig_len); length - SHA_DIGEST_LENGTH, &computed_signature[0], &sig_len);
EXPECT_EQ(0, memcmp(&computed_signature[0], pst_report_.report.signature, EXPECT_EQ(0, memcmp(&computed_signature[0], pst_report()->signature,
SHA_DIGEST_LENGTH)); SHA_DIGEST_LENGTH));
EXPECT_GE(kInactive, pst_report_.report.status); EXPECT_GE(kInactive, pst_report()->status);
EXPECT_GE(kHardwareSecureClock, pst_report_.report.clock_security_level); EXPECT_GE(kHardwareSecureClock, pst_report()->clock_security_level);
EXPECT_EQ(pst.length(), pst_report_.report.pst_length); EXPECT_EQ(pst.length(), pst_report()->pst_length);
EXPECT_EQ(0, memcmp(pst.c_str(), pst_report_.report.pst, pst.length())); EXPECT_EQ(0, memcmp(pst.c_str(), pst_report()->pst, pst.length()));
}
OEMCrypto_PST_Report* pst_report() {
return reinterpret_cast<OEMCrypto_PST_Report*>(&pst_report_buffer_[0]);
} }
OEMCrypto_PST_Report* pst_report() { return &pst_report_.report; }
void DeleteEntry(const std::string& pst) { void DeleteEntry(const std::string& pst) {
uint8_t* pst_ptr = encrypted_license_.pst; uint8_t* pst_ptr = encrypted_license_.pst;
@@ -1465,7 +1232,7 @@ class Session {
vector<uint8_t> enc_key_; vector<uint8_t> enc_key_;
uint32_t nonce_; uint32_t nonce_;
RSA* public_rsa_; RSA* public_rsa_;
PaddedPSTReport pst_report_; vector<uint8_t> pst_report_buffer_;
MessageData license_; MessageData license_;
MessageData encrypted_license_; MessageData encrypted_license_;
OEMCrypto_KeyObject key_array_[kNumKeys]; OEMCrypto_KeyObject key_array_[kNumKeys];
@@ -2445,7 +2212,6 @@ INSTANTIATE_TEST_CASE_P(TestRefreshEachKeys, SessionTestRefreshKeyTest,
// Decrypt Tests // Decrypt Tests
// //
TEST_F(OEMCryptoSessionTests, Decrypt) { TEST_F(OEMCryptoSessionTests, Decrypt) {
OEMCryptoResult sts;
Session s; Session s;
s.open(); s.open();
@@ -2526,7 +2292,6 @@ TEST_F(OEMCryptoSessionTests, DecryptPerformance) {
} }
TEST_F(OEMCryptoSessionTests, DecryptZeroDuration) { TEST_F(OEMCryptoSessionTests, DecryptZeroDuration) {
OEMCryptoResult sts;
Session s; Session s;
s.open(); s.open();
@@ -2592,9 +2357,9 @@ class OEMCryptoSessionTestsDecryptEdgeCases : public OEMCryptoSessionTests {
s.FillSimpleMessage(kDuration, 0, 0); s.FillSimpleMessage(kDuration, 0, 0);
s.EncryptAndSign(); s.EncryptAndSign();
s.LoadTestKeys(); s.LoadTestKeys();
// Select the key (from FillSimpleMessage) sts = OEMCrypto_SelectKey(s.session_id(),
vector<uint8_t> keyId = wvcdm::a2b_hex("000000000000000000000000"); s.license().keys[0].key_id,
sts = OEMCrypto_SelectKey(s.session_id(), &keyId[0], keyId.size()); s.license().keys[0].key_id_length);
ASSERT_EQ(OEMCrypto_SUCCESS, sts); ASSERT_EQ(OEMCrypto_SUCCESS, sts);
// We decrypt three subsamples. each with a block offset. // We decrypt three subsamples. each with a block offset.
@@ -2797,7 +2562,6 @@ TEST_F(OEMCryptoSessionTests, DecryptUnencryptedNoKey) {
} }
TEST_F(OEMCryptoSessionTests, DecryptSecureToClear) { TEST_F(OEMCryptoSessionTests, DecryptSecureToClear) {
OEMCryptoResult sts;
Session s; Session s;
s.open(); s.open();
s.GenerateTestSessionKeys(); s.GenerateTestSessionKeys();
@@ -2809,7 +2573,6 @@ TEST_F(OEMCryptoSessionTests, DecryptSecureToClear) {
} }
TEST_F(OEMCryptoSessionTests, KeyDuration) { TEST_F(OEMCryptoSessionTests, KeyDuration) {
OEMCryptoResult sts;
Session s; Session s;
s.open(); s.open();
s.GenerateTestSessionKeys(); s.GenerateTestSessionKeys();
@@ -2846,7 +2609,8 @@ class OEMCryptoLoadsCertificate : public OEMCryptoSessionTestKeyboxTest {
s.RewrapRSAKey(encrypted, signature, wrapped_key, force); s.RewrapRSAKey(encrypted, signature, wrapped_key, force);
// Verify that the clear key is not contained in the wrapped key. // Verify that the clear key is not contained in the wrapped key.
// It should be encrypted. // It should be encrypted.
ASSERT_EQ(NULL, find(*wrapped_key, encoded_rsa_key_)); } ASSERT_EQ(NULL, find(*wrapped_key, encoded_rsa_key_));
}
std::vector<uint8_t> encoded_rsa_key_; std::vector<uint8_t> encoded_rsa_key_;
}; };
@@ -4622,7 +4386,6 @@ TEST_F(GenericCryptoTest, KeyDurationDecrypt) {
} }
TEST_F(GenericCryptoTest, KeyDurationSign) { TEST_F(GenericCryptoTest, KeyDurationSign) {
OEMCryptoResult sts;
EncryptAndLoadKeys(); EncryptAndLoadKeys();
unsigned int key_index = 2; unsigned int key_index = 2;
@@ -4655,7 +4418,6 @@ TEST_F(GenericCryptoTest, KeyDurationSign) {
} }
TEST_F(GenericCryptoTest, KeyDurationVerify) { TEST_F(GenericCryptoTest, KeyDurationVerify) {
OEMCryptoResult sts;
EncryptAndLoadKeys(); EncryptAndLoadKeys();
unsigned int key_index = 3; unsigned int key_index = 3;
@@ -4692,10 +4454,20 @@ class GenericCryptoKeyIdLengthTest : public GenericCryptoTest {
session_.FillSimpleMessage(kDuration, wvoec_mock::kControlAllowDecrypt, session_.FillSimpleMessage(kDuration, wvoec_mock::kControlAllowDecrypt,
kNoNonce); kNoNonce);
// We are testing that the key ids do not have to have the same length. // We are testing that the key ids do not have to have the same length.
session_.SetKeyId(0, "123456789012"); // 12 bytes (default key id length). session_.SetKeyId(0, "123456789012"); // 12 bytes (common key id length).
session_.SetKeyId(1, "12345"); // short key id. session_.SetKeyId(1, "12345"); // short key id.
session_.SetKeyId(2, "123456789012-very-long-key-id"); session_.SetKeyId(2, "1234567890123456"); // 16 byte key id. (default)
ASSERT_EQ(2, kLongKeyId); session_.SetKeyId(3, "12345678901234"); // 14 byte. (uncommon)
ASSERT_EQ(2u, kLongKeyId);
}
// Make all four keys have the same length.
void SetUniformKeyIdLength(size_t key_id_length) {
for(unsigned int i = 0; i < 4; i++) {
string key_id;
key_id.resize(key_id_length, i + 'a');
session_.SetKeyId(i, key_id);
}
} }
void TestWithKey(unsigned int key_index) { void TestWithKey(unsigned int key_index) {
@@ -4738,6 +4510,16 @@ TEST_F(GenericCryptoKeyIdLengthTest, LongKeyId) {
TestWithKey(2); TestWithKey(2);
} }
TEST_F(GenericCryptoKeyIdLengthTest, UniformShortKeyId) {
SetUniformKeyIdLength(5);
TestWithKey(2);
}
TEST_F(GenericCryptoKeyIdLengthTest, UniformLongKeyId) {
SetUniformKeyIdLength(kTestKeyIdMaxLength);
TestWithKey(2);
}
TEST_F(OEMCryptoClientTest, UpdateUsageTableTest) { TEST_F(OEMCryptoClientTest, UpdateUsageTableTest) {
EXPECT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); EXPECT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable());
} }

View File

@@ -1,5 +1,5 @@
export_variables = { export_variables = {
'CC': 'gcc', 'CC': 'clang',
'CXX': 'g++', 'CXX': 'clang++',
'AR': 'ar', 'AR': 'ar',
} }

View File

@@ -12,6 +12,9 @@
'cflags': [ 'cflags': [
'-fPIC', '-fPIC',
'-fno-exceptions', '-fno-exceptions',
# Enable all warnings, and treat warnings as errors.
'-Wall',
'-Werror',
], ],
# These are flags passed to the compiler for plain C only. # These are flags passed to the compiler for plain C only.

View File

@@ -1,4 +0,0 @@
#!/bin/bash
set -ex
./build.py x86-64 -r
./out/x86-64/Release/widevine_ce_cdm_unittest

View File

@@ -1 +0,0 @@
# dummy

View File

@@ -1 +0,0 @@
# dummy

View File

@@ -31,6 +31,20 @@
'<(protobuf_source)/src/google/protobuf/stubs/stringprintf.cc', '<(protobuf_source)/src/google/protobuf/stubs/stringprintf.cc',
], ],
}, },
'target_defaults': {
# These flags silence warnings that appear in protobuf upstream.
# We will not maintain a divergent copy of protobuf to fix them.
'cflags': [
# Ignore unknown warning options, to support both gcc & clang at once.
'-Wno-unknown-warning-option',
# GCC:
'-Wno-sign-compare',
'-Wno-unused-local-typedefs',
# Clang:
'-Wno-unused-const-variable',
'-Wno-unused-function',
],
},
'targets': [ 'targets': [
{ {
'target_name': 'protobuf_lite', 'target_name': 'protobuf_lite',

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1 +0,0 @@
# dummy

View File

@@ -1 +0,0 @@
# dummy

View File

@@ -1 +0,0 @@
// dummy