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

View File

@@ -26,7 +26,8 @@ class BufferReader {
BufferReader(const uint8_t* buf, size_t size)
: 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,
// and advance the stream pointer.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -641,6 +641,11 @@ CdmResponseType CryptoSession::Decrypt(const CdmDecryptionParameters& params) {
buffer_descriptor.type =
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) {
case OEMCrypto_BufferType_Clear:
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.
// 4 byte size of PSSH data, exclusive. (N)
// N byte PSSH data.
while (1) {
while (!reader.IsEOF()) {
size_t start_pos = reader.pos();
// atom size, used for skipping.
@@ -128,6 +128,7 @@ bool InitializationData::ExtractWidevinePssh(const CdmInitData& init_data,
"the atom.");
return false;
}
LOGV("CdmEngine::ExtractWidevinePssh: Skipping non-Widevine PSSH.");
continue;
}

View File

@@ -327,6 +327,11 @@ CdmResponseType CdmLicense::PrepareKeyUpdateRequest(
return INVALID_PARAMETERS_LIC_2;
}
if (is_renewal && !policy_engine_->CanRenew()) {
LOGE("CdmLicense::PrepareKeyUpdateRequest: license renewal prohibited");
return LICENSE_RENEWAL_PROHIBITED;
}
LicenseRequest license_request;
if (is_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) {
CdmSecurityLevel level = kSecurityLevelL1;
EXPECT_CALL(*crypto_session_, Open(Eq(kLevelDefault)))
.WillOnce(Return(UNKNOWN_ERROR));

View File

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

View File

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

View File

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

View File

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

View File

@@ -16,7 +16,6 @@ namespace wvcdm {
namespace {
const uint32_t kAesBlockSize = 16;
const std::string kAesKey = a2bs_hex("000102030405060708090a0b0c0d0e0f");
const std::string kAesIv = a2bs_hex("000102030405060708090a0b0c0d0e0f");
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";
break;
case DUPLICATE_SESSION_ID_SPECIFIED: *os << "DUPLICATE_SESSION_ID_SPECIFIED";
case LICENSE_RENEWAL_PROHIBITED: *os << "LICENSE_RENEWAL_PROHIBITED";
break;
default:
*os << "Unknown CdmResponseType";
break;