Merge "Add decrypt hash support"
This commit is contained in:
@@ -274,6 +274,16 @@ class CdmEngine {
|
|||||||
|
|
||||||
virtual size_t SessionSize() const { return session_map_.Size(); }
|
virtual size_t SessionSize() const { return session_map_.Size(); }
|
||||||
|
|
||||||
|
static CdmResponseType ParseDecryptHashString(const std::string& hash_string,
|
||||||
|
CdmSessionId* id,
|
||||||
|
uint32_t* frame_number,
|
||||||
|
std::string* hash);
|
||||||
|
virtual CdmResponseType SetDecryptHash(const CdmSessionId& session_id,
|
||||||
|
uint32_t frame_number,
|
||||||
|
const std::string& hash);
|
||||||
|
virtual CdmResponseType GetDecryptHashError(const CdmSessionId& session_id,
|
||||||
|
std::string* hash_error_string);
|
||||||
|
|
||||||
// Is the key known to any session?
|
// Is the key known to any session?
|
||||||
virtual bool IsKeyLoaded(const KeyId& key_id);
|
virtual bool IsKeyLoaded(const KeyId& key_id);
|
||||||
virtual bool FindSessionForKey(const KeyId& key_id, CdmSessionId* sessionId);
|
virtual bool FindSessionForKey(const KeyId& key_id, CdmSessionId* sessionId);
|
||||||
|
|||||||
@@ -193,6 +193,11 @@ class CdmSession {
|
|||||||
CdmSigningAlgorithm algorithm,
|
CdmSigningAlgorithm algorithm,
|
||||||
const std::string& signature);
|
const std::string& signature);
|
||||||
|
|
||||||
|
virtual CdmResponseType SetDecryptHash(uint32_t frame_number,
|
||||||
|
const std::string& hash);
|
||||||
|
|
||||||
|
virtual CdmResponseType GetDecryptHashError(std::string* hash_error_string);
|
||||||
|
|
||||||
virtual metrics::SessionMetrics* GetMetrics() { return metrics_; }
|
virtual metrics::SessionMetrics* GetMetrics() { return metrics_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -152,6 +152,14 @@ class CryptoSession {
|
|||||||
virtual bool GetResourceRatingTier(uint32_t* tier);
|
virtual bool GetResourceRatingTier(uint32_t* tier);
|
||||||
virtual bool GetBuildInformation(std::string* info);
|
virtual bool GetBuildInformation(std::string* info);
|
||||||
|
|
||||||
|
virtual uint32_t IsDecryptHashSupported();
|
||||||
|
|
||||||
|
virtual CdmResponseType SetDecryptHash(uint32_t frame_number,
|
||||||
|
const std::string& hash);
|
||||||
|
|
||||||
|
virtual CdmResponseType GetDecryptHashError(std::string* error_string);
|
||||||
|
|
||||||
|
|
||||||
virtual CdmResponseType GenericEncrypt(const std::string& in_buffer,
|
virtual CdmResponseType GenericEncrypt(const std::string& in_buffer,
|
||||||
const std::string& key_id,
|
const std::string& key_id,
|
||||||
const std::string& iv,
|
const std::string& iv,
|
||||||
|
|||||||
@@ -82,6 +82,8 @@ static const std::string QUERY_KEY_WVCDM_VERSION = "WidevineCdmVersion";
|
|||||||
static const std::string QUERY_KEY_RESOURCE_RATING_TIER = "ResourceRatingTier";
|
static const std::string QUERY_KEY_RESOURCE_RATING_TIER = "ResourceRatingTier";
|
||||||
static const std::string QUERY_KEY_OEMCRYPTO_BUILD_INFORMATION =
|
static const std::string QUERY_KEY_OEMCRYPTO_BUILD_INFORMATION =
|
||||||
"OemCryptoBuildInformation";
|
"OemCryptoBuildInformation";
|
||||||
|
static const std::string QUERY_KEY_DECRYPT_HASH_SUPPORT =
|
||||||
|
"DecryptHashSupport";
|
||||||
|
|
||||||
static const std::string QUERY_VALUE_TRUE = "True";
|
static const std::string QUERY_VALUE_TRUE = "True";
|
||||||
static const std::string QUERY_VALUE_FALSE = "False";
|
static const std::string QUERY_VALUE_FALSE = "False";
|
||||||
|
|||||||
@@ -338,6 +338,10 @@ enum CdmResponseType {
|
|||||||
DEVICE_CANNOT_REPROVISION = 293,
|
DEVICE_CANNOT_REPROVISION = 293,
|
||||||
SESSION_NOT_FOUND_19 = 294,
|
SESSION_NOT_FOUND_19 = 294,
|
||||||
KEY_SIZE_ERROR_2 = 295,
|
KEY_SIZE_ERROR_2 = 295,
|
||||||
|
SET_DECRYPT_HASH_ERROR = 296,
|
||||||
|
GET_DECRYPT_HASH_ERROR = 297,
|
||||||
|
SESSION_NOT_FOUND_20 = 298,
|
||||||
|
INVALID_DECRYPT_HASH_FORMAT = 299,
|
||||||
// Don't forget to add new values to ../test/test_printers.cpp.
|
// Don't forget to add new values to ../test/test_printers.cpp.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -676,6 +676,8 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
|
|||||||
LOGW("CdmEngine::QueryStatus: GetBuildInformation failed");
|
LOGW("CdmEngine::QueryStatus: GetBuildInformation failed");
|
||||||
return UNKNOWN_ERROR;
|
return UNKNOWN_ERROR;
|
||||||
}
|
}
|
||||||
|
} else if (query_token == QUERY_KEY_DECRYPT_HASH_SUPPORT) {
|
||||||
|
*query_response = std::to_string(crypto_session->IsDecryptHashSupported());
|
||||||
} else {
|
} else {
|
||||||
LOGW("CdmEngine::QueryStatus: Unknown status requested, token = %s",
|
LOGW("CdmEngine::QueryStatus: Unknown status requested, token = %s",
|
||||||
query_token.c_str());
|
query_token.c_str());
|
||||||
@@ -1643,6 +1645,86 @@ CdmResponseType CdmEngine::GenericVerify(
|
|||||||
return session->GenericVerify(message, key_id, algorithm, signature);
|
return session->GenericVerify(message, key_id, algorithm, signature);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CdmResponseType CdmEngine::ParseDecryptHashString(
|
||||||
|
const std::string& hash_string,
|
||||||
|
CdmSessionId* session_id,
|
||||||
|
uint32_t* frame_number,
|
||||||
|
std::string* hash) {
|
||||||
|
if (session_id == nullptr) {
|
||||||
|
LOGE("CdmEngine::ParseDecryptHashString: |session_id| was not provided");
|
||||||
|
return PARAMETER_NULL;
|
||||||
|
}
|
||||||
|
if (frame_number == nullptr) {
|
||||||
|
LOGE("CdmEngine::ParseDecryptHashString: |frame_number| was not provided");
|
||||||
|
return PARAMETER_NULL;
|
||||||
|
}
|
||||||
|
if (hash == nullptr) {
|
||||||
|
LOGE("CdmEngine::ParseDecryptHashString: |hash| was not provided");
|
||||||
|
return PARAMETER_NULL;
|
||||||
|
}
|
||||||
|
std::stringstream ss;
|
||||||
|
std::string token;
|
||||||
|
std::vector<std::string> tokens;
|
||||||
|
ss.str(hash_string);
|
||||||
|
while (getline(ss, token, ',')) {
|
||||||
|
tokens.push_back(token);
|
||||||
|
}
|
||||||
|
if (tokens.size() != 3) {
|
||||||
|
LOGE("CdmEngine::ParseDecryptHashString: |hash_string| has invalid format, "
|
||||||
|
"unexpected number of tokens: %d (%s)",
|
||||||
|
tokens.size(), hash_string.c_str());
|
||||||
|
return INVALID_DECRYPT_HASH_FORMAT;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < tokens.size(); ++i) {
|
||||||
|
if (tokens[i].empty()) {
|
||||||
|
LOGE("CdmEngine::ParseDecryptHashString: |hash_string| has invalid "
|
||||||
|
"format, token %d of length 0: %s", i, hash_string.c_str());
|
||||||
|
return INVALID_DECRYPT_HASH_FORMAT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*session_id = tokens[0];
|
||||||
|
std::istringstream iss(tokens[1]);
|
||||||
|
if (!(iss >> *frame_number)) {
|
||||||
|
LOGE("CdmEngine::ParseDecryptHashString: error while trying to convert "
|
||||||
|
"frame number to a numeric format: %s", hash_string.c_str());
|
||||||
|
return INVALID_DECRYPT_HASH_FORMAT;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> hash_vec = wvcdm::Base64Decode(tokens[2]);
|
||||||
|
if (hash_vec.empty()) {
|
||||||
|
LOGE("CdmEngine::ParseDecryptHashString: malformed hash: %s",
|
||||||
|
hash_string.c_str());
|
||||||
|
return INVALID_DECRYPT_HASH_FORMAT;
|
||||||
|
}
|
||||||
|
hash->assign(hash_vec.begin(), hash_vec.end());
|
||||||
|
return NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
CdmResponseType CdmEngine::SetDecryptHash(
|
||||||
|
const CdmSessionId& session_id,
|
||||||
|
uint32_t frame_number,
|
||||||
|
const std::string& hash) {
|
||||||
|
LOGI("CdmEngine::SetDecryptHash");
|
||||||
|
std::shared_ptr<CdmSession> session;
|
||||||
|
if (!session_map_.FindSession(session_id, &session)) {
|
||||||
|
return SESSION_NOT_FOUND_20;
|
||||||
|
}
|
||||||
|
return session->SetDecryptHash(frame_number, hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
CdmResponseType CdmEngine::GetDecryptHashError(
|
||||||
|
const CdmSessionId& session_id,
|
||||||
|
std::string* error_string) {
|
||||||
|
LOGI("CdmEngine::GetDecryptHashError");
|
||||||
|
std::shared_ptr<CdmSession> session;
|
||||||
|
if (!session_map_.FindSession(session_id, &session)) {
|
||||||
|
return SESSION_NOT_FOUND_20;
|
||||||
|
}
|
||||||
|
return session->GetDecryptHashError(error_string);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(gmorgan) Used? Delete if unused.
|
// TODO(gmorgan) Used? Delete if unused.
|
||||||
bool CdmEngine::IsKeyLoaded(const KeyId& key_id) {
|
bool CdmEngine::IsKeyLoaded(const KeyId& key_id) {
|
||||||
CdmSessionList sessions;
|
CdmSessionList sessions;
|
||||||
|
|||||||
@@ -1068,6 +1068,16 @@ CdmResponseType CdmSession::GenericVerify(const std::string& message,
|
|||||||
return sts;
|
return sts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CdmResponseType CdmSession::SetDecryptHash(uint32_t frame_number,
|
||||||
|
const std::string& hash) {
|
||||||
|
return crypto_session_->SetDecryptHash(frame_number, hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
CdmResponseType CdmSession::GetDecryptHashError(
|
||||||
|
std::string* error_string) {
|
||||||
|
return crypto_session_->GetDecryptHashError(error_string);
|
||||||
|
}
|
||||||
|
|
||||||
bool CdmSession::UpdateUsageInfo() {
|
bool CdmSession::UpdateUsageInfo() {
|
||||||
std::string app_id;
|
std::string app_id;
|
||||||
GetApplicationId(&app_id);
|
GetApplicationId(&app_id);
|
||||||
|
|||||||
@@ -1810,6 +1810,70 @@ bool CryptoSession::GetBuildInformation(std::string* info) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t CryptoSession::IsDecryptHashSupported() {
|
||||||
|
LOGV("IsDecryptHashSupported");
|
||||||
|
if (!initialized_) return false;
|
||||||
|
|
||||||
|
uint32_t secure_decrypt_support =
|
||||||
|
OEMCrypto_SupportsDecryptHash(requested_security_level_);
|
||||||
|
switch (secure_decrypt_support) {
|
||||||
|
case OEMCrypto_Hash_Not_Supported:
|
||||||
|
case OEMCrypto_CRC_Clear_Buffer:
|
||||||
|
case OEMCrypto_Partner_Defined_Hash:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOGW("OEMCrypto_SupportsDecryptHash returned unexpected result: %d",
|
||||||
|
secure_decrypt_support);
|
||||||
|
secure_decrypt_support = OEMCrypto_Hash_Not_Supported;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return secure_decrypt_support;
|
||||||
|
}
|
||||||
|
|
||||||
|
CdmResponseType CryptoSession::SetDecryptHash(
|
||||||
|
uint32_t frame_number,
|
||||||
|
const std::string& hash) {
|
||||||
|
LOGV("SetDecryptHash");
|
||||||
|
OEMCryptoResult sts = OEMCrypto_SetDecryptHash(
|
||||||
|
oec_session_id_, frame_number,
|
||||||
|
reinterpret_cast<const uint8_t*>(hash.data()), hash.size());
|
||||||
|
if (OEMCrypto_SUCCESS != sts) {
|
||||||
|
LOGE("SetSecureDecryptHash: failed with error %d", sts);
|
||||||
|
return SET_DECRYPT_HASH_ERROR;
|
||||||
|
}
|
||||||
|
return NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
CdmResponseType CryptoSession::GetDecryptHashError(std::string* error_string) {
|
||||||
|
LOGV("GetDecryptHashError");
|
||||||
|
if (error_string == nullptr) {
|
||||||
|
LOGE("CryptoSession::GetDecryptHashError: |error_string| not provided");
|
||||||
|
return PARAMETER_NULL;
|
||||||
|
}
|
||||||
|
error_string->clear();
|
||||||
|
|
||||||
|
uint32_t failed_frame_number;
|
||||||
|
OEMCryptoResult sts = OEMCrypto_GetHashErrorCode(
|
||||||
|
oec_session_id_, &failed_frame_number);
|
||||||
|
error_string->assign(std::to_string(sts));
|
||||||
|
|
||||||
|
switch (sts) {
|
||||||
|
case OEMCrypto_SUCCESS:
|
||||||
|
case OEMCrypto_ERROR_BAD_HASH:
|
||||||
|
case OEMCrypto_ERROR_SESSION_LOST_STATE:
|
||||||
|
case OEMCrypto_ERROR_SYSTEM_INVALIDATED:
|
||||||
|
error_string->assign(std::to_string(sts));
|
||||||
|
error_string->append(",");
|
||||||
|
error_string->append(std::to_string(failed_frame_number));
|
||||||
|
return NO_ERROR;
|
||||||
|
case OEMCrypto_ERROR_UNKNOWN_FAILURE:
|
||||||
|
case OEMCrypto_ERROR_NOT_IMPLEMENTED:
|
||||||
|
default:
|
||||||
|
LOGE("GetDecryptHashError: failed with error %d", sts);
|
||||||
|
return GET_DECRYPT_HASH_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
CdmResponseType CryptoSession::GenericEncrypt(const std::string& in_buffer,
|
CdmResponseType CryptoSession::GenericEncrypt(const std::string& in_buffer,
|
||||||
const std::string& key_id,
|
const std::string& key_id,
|
||||||
const std::string& iv,
|
const std::string& iv,
|
||||||
|
|||||||
@@ -40,6 +40,8 @@ const int kHttpOk = 200;
|
|||||||
|
|
||||||
const std::string kCencMimeType = "video/mp4";
|
const std::string kCencMimeType = "video/mp4";
|
||||||
const std::string kWebmMimeType = "video/webm";
|
const std::string kWebmMimeType = "video/webm";
|
||||||
|
const std::string kEmptyString;
|
||||||
|
const std::string kComma = ",";
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
@@ -389,4 +391,116 @@ TEST_F(WvCdmEngineTest, LicenseRenewal) {
|
|||||||
config_.client_auth());
|
config_.client_auth());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(WvCdmEngineTest, ParseDecryptHashStringTest) {
|
||||||
|
|
||||||
|
CdmSessionId session_id;
|
||||||
|
uint32_t frame_number;
|
||||||
|
std::string hash;
|
||||||
|
|
||||||
|
const std::string test_session_id = "sample session id";
|
||||||
|
uint32_t test_frame_number = 5;
|
||||||
|
const std::string test_frame_number_string =
|
||||||
|
std::to_string(test_frame_number);
|
||||||
|
const std::string test_invalid_hash = "an invalid hash";
|
||||||
|
std::vector<uint8_t> binary_hash{ 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38 };
|
||||||
|
const std::string test_valid_decoded_hash(binary_hash.begin(),
|
||||||
|
binary_hash.end());
|
||||||
|
const std::string test_valid_hash = Base64Encode(binary_hash);
|
||||||
|
const std::string test_invalid_hash_string = "sample hash string";
|
||||||
|
const std::string test_valid_hash_string = test_session_id + kComma +
|
||||||
|
test_frame_number_string + kComma + test_valid_hash;
|
||||||
|
|
||||||
|
// invalid parameters
|
||||||
|
EXPECT_EQ(PARAMETER_NULL,
|
||||||
|
CdmEngine::ParseDecryptHashString(test_valid_hash_string, nullptr,
|
||||||
|
&frame_number, &hash));
|
||||||
|
EXPECT_EQ(PARAMETER_NULL,
|
||||||
|
CdmEngine::ParseDecryptHashString(test_valid_hash_string,
|
||||||
|
&session_id, nullptr, &hash));
|
||||||
|
EXPECT_EQ(PARAMETER_NULL,
|
||||||
|
CdmEngine::ParseDecryptHashString(test_valid_hash_string,
|
||||||
|
&session_id, &frame_number,
|
||||||
|
nullptr));
|
||||||
|
|
||||||
|
// Too few or too many parameters
|
||||||
|
EXPECT_EQ(INVALID_DECRYPT_HASH_FORMAT,
|
||||||
|
CdmEngine::ParseDecryptHashString(kEmptyString, &session_id,
|
||||||
|
&frame_number, &hash));
|
||||||
|
std::string hash_data = test_session_id;
|
||||||
|
EXPECT_EQ(INVALID_DECRYPT_HASH_FORMAT,
|
||||||
|
CdmEngine::ParseDecryptHashString(hash_data, &session_id,
|
||||||
|
&frame_number, &hash));
|
||||||
|
hash_data += kComma;
|
||||||
|
EXPECT_EQ(INVALID_DECRYPT_HASH_FORMAT,
|
||||||
|
CdmEngine::ParseDecryptHashString(hash_data, &session_id,
|
||||||
|
&frame_number, &hash));
|
||||||
|
hash_data += test_frame_number_string;
|
||||||
|
EXPECT_EQ(INVALID_DECRYPT_HASH_FORMAT,
|
||||||
|
CdmEngine::ParseDecryptHashString(hash_data, &session_id,
|
||||||
|
&frame_number, &hash));
|
||||||
|
hash_data += kComma;
|
||||||
|
EXPECT_EQ(INVALID_DECRYPT_HASH_FORMAT,
|
||||||
|
CdmEngine::ParseDecryptHashString(hash_data, &session_id,
|
||||||
|
&frame_number, &hash));
|
||||||
|
hash_data += test_valid_hash + kComma + test_valid_hash;
|
||||||
|
EXPECT_EQ(INVALID_DECRYPT_HASH_FORMAT,
|
||||||
|
CdmEngine::ParseDecryptHashString(hash_data, &session_id,
|
||||||
|
&frame_number, &hash));
|
||||||
|
|
||||||
|
// No parameters
|
||||||
|
hash_data = kComma;
|
||||||
|
EXPECT_EQ(INVALID_DECRYPT_HASH_FORMAT,
|
||||||
|
CdmEngine::ParseDecryptHashString(hash_data, &session_id,
|
||||||
|
&frame_number, &hash));
|
||||||
|
hash_data = kComma + kComma;
|
||||||
|
EXPECT_EQ(INVALID_DECRYPT_HASH_FORMAT,
|
||||||
|
CdmEngine::ParseDecryptHashString(hash_data, &session_id,
|
||||||
|
&frame_number, &hash));
|
||||||
|
hash_data = kComma + kComma + kComma;
|
||||||
|
EXPECT_EQ(INVALID_DECRYPT_HASH_FORMAT,
|
||||||
|
CdmEngine::ParseDecryptHashString(hash_data, &session_id,
|
||||||
|
&frame_number, &hash));
|
||||||
|
|
||||||
|
// Missing parameters
|
||||||
|
hash_data = kComma + test_frame_number_string + kComma + test_valid_hash;
|
||||||
|
EXPECT_EQ(INVALID_DECRYPT_HASH_FORMAT,
|
||||||
|
CdmEngine::ParseDecryptHashString(hash_data, &session_id,
|
||||||
|
&frame_number, &hash));
|
||||||
|
hash_data = test_session_id + kComma + kComma + test_valid_hash;
|
||||||
|
EXPECT_EQ(INVALID_DECRYPT_HASH_FORMAT,
|
||||||
|
CdmEngine::ParseDecryptHashString(hash_data, &session_id,
|
||||||
|
&frame_number, &hash));
|
||||||
|
hash_data = test_session_id + kComma + test_frame_number_string + kComma;
|
||||||
|
EXPECT_EQ(INVALID_DECRYPT_HASH_FORMAT,
|
||||||
|
CdmEngine::ParseDecryptHashString(hash_data, &session_id,
|
||||||
|
&frame_number, &hash));
|
||||||
|
hash_data = test_session_id + kComma + test_frame_number_string + kComma +
|
||||||
|
kComma;
|
||||||
|
EXPECT_EQ(INVALID_DECRYPT_HASH_FORMAT,
|
||||||
|
CdmEngine::ParseDecryptHashString(hash_data, &session_id,
|
||||||
|
&frame_number, &hash));
|
||||||
|
|
||||||
|
// Invalid parameters (frame number, hash)
|
||||||
|
hash_data = test_session_id + kComma + test_session_id + kComma +
|
||||||
|
test_valid_hash;
|
||||||
|
EXPECT_EQ(INVALID_DECRYPT_HASH_FORMAT,
|
||||||
|
CdmEngine::ParseDecryptHashString(hash_data, &session_id,
|
||||||
|
&frame_number, &hash));
|
||||||
|
hash_data = test_session_id + kComma + test_frame_number_string + kComma +
|
||||||
|
test_invalid_hash;
|
||||||
|
EXPECT_EQ(INVALID_DECRYPT_HASH_FORMAT,
|
||||||
|
CdmEngine::ParseDecryptHashString(hash_data, &session_id,
|
||||||
|
&frame_number, &hash));
|
||||||
|
|
||||||
|
// valid data
|
||||||
|
EXPECT_EQ(NO_ERROR,
|
||||||
|
CdmEngine::ParseDecryptHashString(test_valid_hash_string,
|
||||||
|
&session_id, &frame_number,
|
||||||
|
&hash));
|
||||||
|
EXPECT_EQ(test_session_id, session_id);
|
||||||
|
EXPECT_EQ(test_frame_number, frame_number);
|
||||||
|
EXPECT_EQ(test_valid_decoded_hash, hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace wvcdm
|
} // namespace wvcdm
|
||||||
|
|||||||
@@ -775,6 +775,18 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
|
|||||||
case DEVICE_CANNOT_REPROVISION:
|
case DEVICE_CANNOT_REPROVISION:
|
||||||
*os << "DEVICE_CANNOT_REPROVISION";
|
*os << "DEVICE_CANNOT_REPROVISION";
|
||||||
break;
|
break;
|
||||||
|
case SET_DECRYPT_HASH_ERROR:
|
||||||
|
*os << "SET_DECRYPT_HASH_ERROR";
|
||||||
|
break;
|
||||||
|
case GET_DECRYPT_HASH_ERROR:
|
||||||
|
*os << "GET_DECRYPT_HASH_ERROR";
|
||||||
|
break;
|
||||||
|
case SESSION_NOT_FOUND_20:
|
||||||
|
*os << "SESSION_NOT_FOUND_20";
|
||||||
|
break;
|
||||||
|
case INVALID_DECRYPT_HASH_FORMAT:
|
||||||
|
*os << "INVALID_DECRYPT_HASH_FORMAT";
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
*os << "Unknown CdmResponseType";
|
*os << "Unknown CdmResponseType";
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -139,6 +139,11 @@ class WvContentDecryptionModule : public android::RefBase, public TimerHandler {
|
|||||||
// Closes the CdmEngine and sessions associated with the given CdmIdentifier.
|
// Closes the CdmEngine and sessions associated with the given CdmIdentifier.
|
||||||
virtual CdmResponseType CloseCdm(const CdmIdentifier& identifier);
|
virtual CdmResponseType CloseCdm(const CdmIdentifier& identifier);
|
||||||
|
|
||||||
|
virtual CdmResponseType SetDecryptHash(const std::string& hash_data,
|
||||||
|
CdmSessionId* session_id);
|
||||||
|
virtual CdmResponseType GetDecryptHashError(const CdmSessionId& session_id,
|
||||||
|
std::string* hash_error_string);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct CdmInfo {
|
struct CdmInfo {
|
||||||
CdmInfo();
|
CdmInfo();
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
#include "metrics.pb.h"
|
#include "metrics.pb.h"
|
||||||
#include "properties.h"
|
#include "properties.h"
|
||||||
#include "service_certificate.h"
|
#include "service_certificate.h"
|
||||||
|
#include "string_conversions.h"
|
||||||
#include "wv_cdm_constants.h"
|
#include "wv_cdm_constants.h"
|
||||||
#include "wv_cdm_event_listener.h"
|
#include "wv_cdm_event_listener.h"
|
||||||
|
|
||||||
@@ -456,6 +457,44 @@ CdmResponseType WvContentDecryptionModule::CloseCdm(
|
|||||||
return NO_ERROR;
|
return NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CdmResponseType WvContentDecryptionModule::SetDecryptHash(
|
||||||
|
const std::string& hash_data, CdmSessionId* id) {
|
||||||
|
if (id == nullptr) {
|
||||||
|
LOGE("WVContentDecryptionModule::SetDecryptHash: |id| was not provided");
|
||||||
|
return PARAMETER_NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t frame_number;
|
||||||
|
std::string hash;
|
||||||
|
CdmResponseType res =
|
||||||
|
CdmEngine::ParseDecryptHashString(hash_data, id, &frame_number, &hash);
|
||||||
|
|
||||||
|
if (res != NO_ERROR) return res;
|
||||||
|
|
||||||
|
CdmEngine* cdm_engine = GetCdmForSessionId(*id);
|
||||||
|
|
||||||
|
if (!cdm_engine) {
|
||||||
|
LOGE("WVContentDecryptionModule::SetDecryptHash: Unable to find CdmEngine");
|
||||||
|
return SESSION_NOT_FOUND_20;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = cdm_engine->SetDecryptHash(*id, frame_number, hash);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
CdmResponseType WvContentDecryptionModule::GetDecryptHashError(
|
||||||
|
const CdmSessionId& session_id,
|
||||||
|
std::string* hash_error_string) {
|
||||||
|
CdmEngine* cdm_engine = GetCdmForSessionId(session_id);
|
||||||
|
|
||||||
|
if (!cdm_engine) {
|
||||||
|
LOGE("WVContentDecryptionModule::GetDecryptHashError: Unable to find "
|
||||||
|
"CdmEngine");
|
||||||
|
return SESSION_NOT_FOUND_20;
|
||||||
|
}
|
||||||
|
return cdm_engine->GetDecryptHashError(session_id, hash_error_string);
|
||||||
|
}
|
||||||
|
|
||||||
void WvContentDecryptionModule::EnablePolicyTimer() {
|
void WvContentDecryptionModule::EnablePolicyTimer() {
|
||||||
std::unique_lock<std::mutex> auto_lock(policy_timer_lock_);
|
std::unique_lock<std::mutex> auto_lock(policy_timer_lock_);
|
||||||
if (!policy_timer_.IsRunning())
|
if (!policy_timer_.IsRunning())
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ const wvcdm::CdmIdentifier kAlternateCdmIdentifier1 = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const std::string kEmptyServiceCertificate;
|
const std::string kEmptyServiceCertificate;
|
||||||
|
const std::string kComma = ",";
|
||||||
|
|
||||||
// Protobuf generated classes
|
// Protobuf generated classes
|
||||||
using video_widevine::LicenseIdentification;
|
using video_widevine::LicenseIdentification;
|
||||||
@@ -5104,4 +5105,34 @@ TEST_F(WvCdmRequestLicenseTest, CloseCdmReleaseResourcesTest) {
|
|||||||
EXPECT_TRUE(decryptor_.IsOpenSession(alternate_session_id));
|
EXPECT_TRUE(decryptor_.IsOpenSession(alternate_session_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Enable when OEMCrypto v15 has been deployed. Currently setting a decrypt
|
||||||
|
// hash returns OEMCrypto_ERROR_NOT_IMPLEMENTED
|
||||||
|
TEST_F(WvCdmRequestLicenseTest, DISABLED_DecryptPathTest) {
|
||||||
|
Provision(kDefaultCdmIdentifier, kLevelDefault);
|
||||||
|
|
||||||
|
// Retrieve a streaming license
|
||||||
|
EXPECT_EQ(NO_ERROR, decryptor_.OpenSession(config_.key_system(), NULL,
|
||||||
|
kDefaultCdmIdentifier, NULL,
|
||||||
|
&session_id_));
|
||||||
|
|
||||||
|
CdmSessionId invalid_session_id = session_id_ + "more";
|
||||||
|
std::string frame_number = std::to_string(5);
|
||||||
|
std::vector<uint8_t> binary_hash{ 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38 };
|
||||||
|
const std::string hash = Base64Encode(binary_hash);
|
||||||
|
|
||||||
|
// Invalid session id
|
||||||
|
std::string hash_data =
|
||||||
|
invalid_session_id + kComma + std::to_string(5) + kComma + hash;
|
||||||
|
|
||||||
|
CdmSessionId session_id;
|
||||||
|
EXPECT_NE(NO_ERROR, decryptor_.SetDecryptHash(hash_data, &session_id));
|
||||||
|
|
||||||
|
// Valid hash data
|
||||||
|
hash_data = session_id_ + kComma + std::to_string(5) + kComma + hash;
|
||||||
|
EXPECT_EQ(NO_ERROR, decryptor_.SetDecryptHash(hash_data, &session_id));
|
||||||
|
EXPECT_EQ(session_id_, session_id);
|
||||||
|
|
||||||
|
decryptor_.CloseSession(session_id_);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace wvcdm
|
} // namespace wvcdm
|
||||||
|
|||||||
@@ -270,10 +270,13 @@ enum {
|
|||||||
kGetProvisioningError = ERROR_DRM_VENDOR_MIN + 280,
|
kGetProvisioningError = ERROR_DRM_VENDOR_MIN + 280,
|
||||||
kDeviceCannotReprovision = ERROR_DRM_VENDOR_MIN + 281,
|
kDeviceCannotReprovision = ERROR_DRM_VENDOR_MIN + 281,
|
||||||
kKeySizeError2 = ERROR_DRM_VENDOR_MIN + 282,
|
kKeySizeError2 = ERROR_DRM_VENDOR_MIN + 282,
|
||||||
|
kSetDecryptHashError = ERROR_DRM_VENDOR_MIN + 283,
|
||||||
|
kGetDecryptHashError = ERROR_DRM_VENDOR_MIN + 284,
|
||||||
|
kInvalidDecryptHashFormat = ERROR_DRM_VENDOR_MIN + 285,
|
||||||
|
|
||||||
// This should always follow the last error code.
|
// This should always follow the last error code.
|
||||||
// The offset value should be updated each time a new error code is added.
|
// The offset value should be updated each time a new error code is added.
|
||||||
kErrorWVDrmMaxErrorUsed = ERROR_DRM_VENDOR_MIN + 282,
|
kErrorWVDrmMaxErrorUsed = ERROR_DRM_VENDOR_MIN + 285,
|
||||||
|
|
||||||
// Used by crypto test mode
|
// Used by crypto test mode
|
||||||
kErrorTestMode = ERROR_DRM_VENDOR_MAX,
|
kErrorTestMode = ERROR_DRM_VENDOR_MAX,
|
||||||
|
|||||||
@@ -234,6 +234,7 @@ static android::status_t mapCdmResponseType(wvcdm::CdmResponseType res) {
|
|||||||
case wvcdm::SESSION_NOT_FOUND_17:
|
case wvcdm::SESSION_NOT_FOUND_17:
|
||||||
case wvcdm::SESSION_NOT_FOUND_18:
|
case wvcdm::SESSION_NOT_FOUND_18:
|
||||||
case wvcdm::SESSION_NOT_FOUND_19:
|
case wvcdm::SESSION_NOT_FOUND_19:
|
||||||
|
case wvcdm::SESSION_NOT_FOUND_20:
|
||||||
return android::ERROR_DRM_SESSION_NOT_OPENED;
|
return android::ERROR_DRM_SESSION_NOT_OPENED;
|
||||||
case wvcdm::SESSION_KEYS_NOT_FOUND:
|
case wvcdm::SESSION_KEYS_NOT_FOUND:
|
||||||
return kSessionKeysNotFound;
|
return kSessionKeysNotFound;
|
||||||
@@ -524,6 +525,12 @@ static android::status_t mapCdmResponseType(wvcdm::CdmResponseType res) {
|
|||||||
return kGetProvisioningError;
|
return kGetProvisioningError;
|
||||||
case wvcdm::DEVICE_CANNOT_REPROVISION:
|
case wvcdm::DEVICE_CANNOT_REPROVISION:
|
||||||
return kDeviceCannotReprovision;
|
return kDeviceCannotReprovision;
|
||||||
|
case wvcdm::SET_DECRYPT_HASH_ERROR:
|
||||||
|
return kSetDecryptHashError;
|
||||||
|
case wvcdm::GET_DECRYPT_HASH_ERROR:
|
||||||
|
return kGetDecryptHashError;
|
||||||
|
case wvcdm::INVALID_DECRYPT_HASH_FORMAT:
|
||||||
|
return kInvalidDecryptHashFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return here instead of as a default case so that the compiler will warn
|
// Return here instead of as a default case so that the compiler will warn
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ static Status mapCdmResponseType(wvcdm::CdmResponseType res) {
|
|||||||
case wvcdm::SESSION_NOT_FOUND_17:
|
case wvcdm::SESSION_NOT_FOUND_17:
|
||||||
case wvcdm::SESSION_NOT_FOUND_18:
|
case wvcdm::SESSION_NOT_FOUND_18:
|
||||||
case wvcdm::SESSION_NOT_FOUND_19:
|
case wvcdm::SESSION_NOT_FOUND_19:
|
||||||
|
case wvcdm::SESSION_NOT_FOUND_20:
|
||||||
return Status::ERROR_DRM_SESSION_NOT_OPENED;
|
return Status::ERROR_DRM_SESSION_NOT_OPENED;
|
||||||
|
|
||||||
case wvcdm::DECRYPT_ERROR:
|
case wvcdm::DECRYPT_ERROR:
|
||||||
@@ -305,6 +306,9 @@ static Status mapCdmResponseType(wvcdm::CdmResponseType res) {
|
|||||||
case wvcdm::LOAD_ENTITLED_CONTENT_KEYS_ERROR:
|
case wvcdm::LOAD_ENTITLED_CONTENT_KEYS_ERROR:
|
||||||
case wvcdm::GET_PROVISIONING_METHOD_ERROR:
|
case wvcdm::GET_PROVISIONING_METHOD_ERROR:
|
||||||
case wvcdm::DEVICE_CANNOT_REPROVISION:
|
case wvcdm::DEVICE_CANNOT_REPROVISION:
|
||||||
|
case wvcdm::SET_DECRYPT_HASH_ERROR:
|
||||||
|
case wvcdm::GET_DECRYPT_HASH_ERROR:
|
||||||
|
case wvcdm::INVALID_DECRYPT_HASH_FORMAT:
|
||||||
ALOGW("Returns UNKNOWN error for legacy status: %d", res);
|
ALOGW("Returns UNKNOWN error for legacy status: %d", res);
|
||||||
return Status::ERROR_DRM_UNKNOWN;
|
return Status::ERROR_DRM_UNKNOWN;
|
||||||
|
|
||||||
|
|||||||
@@ -263,6 +263,8 @@ class WVDrmPlugin : public android::DrmPlugin,
|
|||||||
|
|
||||||
std::string mProvisioningServiceCertificate;
|
std::string mProvisioningServiceCertificate;
|
||||||
|
|
||||||
|
CdmSessionId mDecryptHashSessionId;
|
||||||
|
|
||||||
status_t queryProperty(const std::string& property,
|
status_t queryProperty(const std::string& property,
|
||||||
std::string& stringValue) const;
|
std::string& stringValue) const;
|
||||||
|
|
||||||
|
|||||||
@@ -411,6 +411,8 @@ struct WVDrmPlugin : public IDrmPlugin, IDrmPluginListener,
|
|||||||
|
|
||||||
std::string mProvisioningServiceCertificate;
|
std::string mProvisioningServiceCertificate;
|
||||||
|
|
||||||
|
wvcdm::CdmSessionId mDecryptHashSessionId;
|
||||||
|
|
||||||
Status queryProperty(const std::string& property,
|
Status queryProperty(const std::string& property,
|
||||||
std::string& stringValue) const;
|
std::string& stringValue) const;
|
||||||
|
|
||||||
|
|||||||
@@ -528,6 +528,14 @@ status_t WVDrmPlugin::getPropertyString(const String8& name,
|
|||||||
return queryProperty(QUERY_KEY_RESOURCE_RATING_TIER, value);
|
return queryProperty(QUERY_KEY_RESOURCE_RATING_TIER, value);
|
||||||
} else if (name == "oemCryptoBuildInformation") {
|
} else if (name == "oemCryptoBuildInformation") {
|
||||||
return queryProperty(QUERY_KEY_OEMCRYPTO_BUILD_INFORMATION, value);
|
return queryProperty(QUERY_KEY_OEMCRYPTO_BUILD_INFORMATION, value);
|
||||||
|
} else if (name == "decryptHashSupport") {
|
||||||
|
return queryProperty(QUERY_KEY_DECRYPT_HASH_SUPPORT, value);
|
||||||
|
} else if (name == "decryptHashError") {
|
||||||
|
std::string hash_error_string;
|
||||||
|
CdmResponseType res =
|
||||||
|
mCDM->GetDecryptHashError(mDecryptHashSessionId, &hash_error_string);
|
||||||
|
value = hash_error_string.c_str();
|
||||||
|
return mapCdmResponseType(res);
|
||||||
} else {
|
} else {
|
||||||
ALOGE("App requested unknown string property %s", name.string());
|
ALOGE("App requested unknown string property %s", name.string());
|
||||||
return android::ERROR_DRM_CANNOT_HANDLE;
|
return android::ERROR_DRM_CANNOT_HANDLE;
|
||||||
@@ -630,6 +638,16 @@ status_t WVDrmPlugin::setPropertyString(const String8& name,
|
|||||||
} else {
|
} else {
|
||||||
mCdmIdentifier.origin = value.string();
|
mCdmIdentifier.origin = value.string();
|
||||||
}
|
}
|
||||||
|
} else if (name == "decryptHash") {
|
||||||
|
CdmSessionId sessionId;
|
||||||
|
CdmResponseType res =
|
||||||
|
mCDM->SetDecryptHash(value.string(), &sessionId);
|
||||||
|
|
||||||
|
if (wvcdm::NO_ERROR == res) mDecryptHashSessionId = sessionId;
|
||||||
|
|
||||||
|
return mapCdmResponseType(res);
|
||||||
|
} else if (name == "decryptHashSessionId") {
|
||||||
|
mDecryptHashSessionId = value.string();
|
||||||
} else {
|
} else {
|
||||||
ALOGE("App set unknown string property %s", name.string());
|
ALOGE("App set unknown string property %s", name.string());
|
||||||
return android::ERROR_DRM_CANNOT_HANDLE;
|
return android::ERROR_DRM_CANNOT_HANDLE;
|
||||||
|
|||||||
@@ -1025,6 +1025,11 @@ Return<void> WVDrmPlugin::getPropertyString(const hidl_string& propertyName,
|
|||||||
status = queryProperty(wvcdm::QUERY_KEY_RESOURCE_RATING_TIER, value);
|
status = queryProperty(wvcdm::QUERY_KEY_RESOURCE_RATING_TIER, value);
|
||||||
} else if (name == "oemCryptoBuildInformation") {
|
} else if (name == "oemCryptoBuildInformation") {
|
||||||
status = queryProperty(wvcdm::QUERY_KEY_OEMCRYPTO_BUILD_INFORMATION, value);
|
status = queryProperty(wvcdm::QUERY_KEY_OEMCRYPTO_BUILD_INFORMATION, value);
|
||||||
|
} else if (name == "decryptHashSupport") {
|
||||||
|
status = queryProperty(wvcdm::QUERY_KEY_DECRYPT_HASH_SUPPORT, value);
|
||||||
|
} else if (name == "decryptHashError") {
|
||||||
|
status = mapCdmResponseType(
|
||||||
|
mCDM->GetDecryptHashError(mDecryptHashSessionId, &value));
|
||||||
} else {
|
} else {
|
||||||
ALOGE("App requested unknown string property %s", name.c_str());
|
ALOGE("App requested unknown string property %s", name.c_str());
|
||||||
status = Status::ERROR_DRM_CANNOT_HANDLE;
|
status = Status::ERROR_DRM_CANNOT_HANDLE;
|
||||||
@@ -1167,6 +1172,16 @@ Return<Status> WVDrmPlugin::setPropertyString(const hidl_string& propertyName,
|
|||||||
return Status::BAD_VALUE;
|
return Status::BAD_VALUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (name == "decryptHash") {
|
||||||
|
wvcdm::CdmSessionId sessionId;
|
||||||
|
CdmResponseType res =
|
||||||
|
mCDM->SetDecryptHash(_value.c_str(), &sessionId);
|
||||||
|
|
||||||
|
if (wvcdm::NO_ERROR == res) mDecryptHashSessionId = sessionId;
|
||||||
|
|
||||||
|
return mapCdmResponseType(res);
|
||||||
|
} else if (name == "decryptHashSessionId") {
|
||||||
|
mDecryptHashSessionId = _value.c_str();
|
||||||
} else {
|
} else {
|
||||||
ALOGE("App set unknown string property %s", name.c_str());
|
ALOGE("App set unknown string property %s", name.c_str());
|
||||||
return Status::ERROR_DRM_CANNOT_HANDLE;
|
return Status::ERROR_DRM_CANNOT_HANDLE;
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ using wvcdm::kLicenseTypeRelease;
|
|||||||
using wvcdm::kLicenseTypeStreaming;
|
using wvcdm::kLicenseTypeStreaming;
|
||||||
using wvcdm::kSecurityLevelL1;
|
using wvcdm::kSecurityLevelL1;
|
||||||
using wvcdm::kSecurityLevelL3;
|
using wvcdm::kSecurityLevelL3;
|
||||||
|
using wvcdm::Base64Encode;
|
||||||
using wvcdm::CdmAppParameterMap;
|
using wvcdm::CdmAppParameterMap;
|
||||||
using wvcdm::CdmCertificateType;
|
using wvcdm::CdmCertificateType;
|
||||||
using wvcdm::CdmClientPropertySet;
|
using wvcdm::CdmClientPropertySet;
|
||||||
@@ -100,6 +101,7 @@ using wvcdm::KEY_SET_ID_PREFIX;
|
|||||||
using wvcdm::NEVER_EXPIRES;
|
using wvcdm::NEVER_EXPIRES;
|
||||||
using wvcdm::QUERY_KEY_CURRENT_SRM_VERSION;
|
using wvcdm::QUERY_KEY_CURRENT_SRM_VERSION;
|
||||||
using wvcdm::QUERY_KEY_DEVICE_ID;
|
using wvcdm::QUERY_KEY_DEVICE_ID;
|
||||||
|
using wvcdm::QUERY_KEY_DECRYPT_HASH_SUPPORT;
|
||||||
using wvcdm::QUERY_KEY_MAX_NUMBER_OF_SESSIONS;
|
using wvcdm::QUERY_KEY_MAX_NUMBER_OF_SESSIONS;
|
||||||
using wvcdm::QUERY_KEY_NUMBER_OF_OPEN_SESSIONS;
|
using wvcdm::QUERY_KEY_NUMBER_OF_OPEN_SESSIONS;
|
||||||
using wvcdm::QUERY_KEY_OEMCRYPTO_API_VERSION;
|
using wvcdm::QUERY_KEY_OEMCRYPTO_API_VERSION;
|
||||||
@@ -208,6 +210,8 @@ class MockCDM : public WvContentDecryptionModule {
|
|||||||
|
|
||||||
MOCK_METHOD2(GetMetrics, CdmResponseType(const CdmIdentifier&,
|
MOCK_METHOD2(GetMetrics, CdmResponseType(const CdmIdentifier&,
|
||||||
drm_metrics::WvCdmMetrics*));
|
drm_metrics::WvCdmMetrics*));
|
||||||
|
|
||||||
|
MOCK_METHOD2(GetDecryptHashError, CdmResponseType(const CdmSessionId&, std::string*));
|
||||||
};
|
};
|
||||||
|
|
||||||
class MockCrypto : public WVGenericCryptoInterface {
|
class MockCrypto : public WVGenericCryptoInterface {
|
||||||
@@ -344,7 +348,7 @@ TEST_F(WVDrmPluginTest, OpensSessions_1_1) {
|
|||||||
StrictMock<MockCrypto> crypto;
|
StrictMock<MockCrypto> crypto;
|
||||||
std::string appPackageName;
|
std::string appPackageName;
|
||||||
|
|
||||||
const CdmClientPropertySet* propertySet = NULL;
|
const CdmClientPropertySet* propertySet = nullptr;
|
||||||
|
|
||||||
// Provide expected mock behavior
|
// Provide expected mock behavior
|
||||||
EXPECT_CALL(*cdm, QueryStatus(_, QUERY_KEY_SECURITY_LEVEL, _))
|
EXPECT_CALL(*cdm, QueryStatus(_, QUERY_KEY_SECURITY_LEVEL, _))
|
||||||
@@ -655,7 +659,7 @@ TEST_F(WVDrmPluginTest, HandlesPrivacyCertCaseOfAddKey) {
|
|||||||
sp<StrictMock<MockDrmPluginListener> > listener =
|
sp<StrictMock<MockDrmPluginListener> > listener =
|
||||||
new StrictMock<MockDrmPluginListener>();
|
new StrictMock<MockDrmPluginListener>();
|
||||||
|
|
||||||
const CdmClientPropertySet* propertySet = NULL;
|
const CdmClientPropertySet* propertySet = nullptr;
|
||||||
|
|
||||||
// Provide expected behavior in response to OpenSession and store the
|
// Provide expected behavior in response to OpenSession and store the
|
||||||
// property set
|
// property set
|
||||||
@@ -1138,6 +1142,10 @@ TEST_F(WVDrmPluginTest, ReturnsExpectedPropertyValues) {
|
|||||||
static const std::string cdmVersion = "Infinity Minus 1";
|
static const std::string cdmVersion = "Infinity Minus 1";
|
||||||
static const std::string resourceRatingTier = "1";
|
static const std::string resourceRatingTier = "1";
|
||||||
static const std::string oemCryptoBuildInformation = "Mostly Harmless";
|
static const std::string oemCryptoBuildInformation = "Mostly Harmless";
|
||||||
|
static const std::string oemCryptoHashNotSupported = "0";
|
||||||
|
static const std::string oemCryptoCrcClearBuffer = "1";
|
||||||
|
static const std::string oemCryptoPartnerDefinedHash = "2";
|
||||||
|
static const std::string decryptHashErrorBadHashAndFrameNumber = "53, 1";
|
||||||
drm_metrics::WvCdmMetrics expected_metrics;
|
drm_metrics::WvCdmMetrics expected_metrics;
|
||||||
std::string serialized_metrics = wvcdm::a2bs_hex(kSerializedMetricsHex);
|
std::string serialized_metrics = wvcdm::a2bs_hex(kSerializedMetricsHex);
|
||||||
ASSERT_TRUE(expected_metrics.ParseFromString(serialized_metrics));
|
ASSERT_TRUE(expected_metrics.ParseFromString(serialized_metrics));
|
||||||
@@ -1188,10 +1196,18 @@ TEST_F(WVDrmPluginTest, ReturnsExpectedPropertyValues) {
|
|||||||
.WillOnce(DoAll(SetArgPointee<2>(resourceRatingTier),
|
.WillOnce(DoAll(SetArgPointee<2>(resourceRatingTier),
|
||||||
testing::Return(wvcdm::NO_ERROR)));
|
testing::Return(wvcdm::NO_ERROR)));
|
||||||
|
|
||||||
|
EXPECT_CALL(*cdm, QueryStatus(_, QUERY_KEY_DECRYPT_HASH_SUPPORT, _))
|
||||||
|
.WillOnce(DoAll(SetArgPointee<2>("1"),
|
||||||
|
testing::Return(wvcdm::NO_ERROR)));
|
||||||
|
|
||||||
EXPECT_CALL(*cdm, QueryStatus(_, QUERY_KEY_OEMCRYPTO_BUILD_INFORMATION, _))
|
EXPECT_CALL(*cdm, QueryStatus(_, QUERY_KEY_OEMCRYPTO_BUILD_INFORMATION, _))
|
||||||
.WillOnce(DoAll(SetArgPointee<2>(oemCryptoBuildInformation),
|
.WillOnce(DoAll(SetArgPointee<2>(oemCryptoBuildInformation),
|
||||||
testing::Return(wvcdm::NO_ERROR)));
|
testing::Return(wvcdm::NO_ERROR)));
|
||||||
|
|
||||||
|
EXPECT_CALL(*cdm, GetDecryptHashError(_, _))
|
||||||
|
.WillOnce(DoAll(SetArgPointee<1>(decryptHashErrorBadHashAndFrameNumber),
|
||||||
|
testing::Return(wvcdm::NO_ERROR)));
|
||||||
|
|
||||||
EXPECT_CALL(*cdm, GetMetrics(_, _))
|
EXPECT_CALL(*cdm, GetMetrics(_, _))
|
||||||
.WillOnce(DoAll(SetArgPointee<1>(expected_metrics),
|
.WillOnce(DoAll(SetArgPointee<1>(expected_metrics),
|
||||||
testing::Return(wvcdm::NO_ERROR)));
|
testing::Return(wvcdm::NO_ERROR)));
|
||||||
@@ -1311,6 +1327,24 @@ TEST_F(WVDrmPluginTest, ReturnsExpectedPropertyValues) {
|
|||||||
EXPECT_STREQ(oemCryptoBuildInformation.c_str(), stringResult.c_str());
|
EXPECT_STREQ(oemCryptoBuildInformation.c_str(), stringResult.c_str());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << oemCryptoHashNotSupported << " " << oemCryptoCrcClearBuffer << " "
|
||||||
|
<< oemCryptoPartnerDefinedHash;
|
||||||
|
std::string validResults = ss.str();
|
||||||
|
plugin.getPropertyString(
|
||||||
|
hidl_string("decryptHashSupport"),
|
||||||
|
[&](Status status, hidl_string stringResult) {
|
||||||
|
ASSERT_EQ(Status::OK, status);
|
||||||
|
EXPECT_EQ(oemCryptoCrcClearBuffer, stringResult);
|
||||||
|
});
|
||||||
|
|
||||||
|
plugin.getPropertyString(
|
||||||
|
hidl_string("decryptHashError"),
|
||||||
|
[&](Status status, hidl_string stringResult) {
|
||||||
|
ASSERT_EQ(Status::OK, status);
|
||||||
|
EXPECT_EQ(decryptHashErrorBadHashAndFrameNumber, stringResult);
|
||||||
|
});
|
||||||
|
|
||||||
// This call occurs before any open session or other call. This means
|
// This call occurs before any open session or other call. This means
|
||||||
// that the cdm identifer is not yet sealed, and metrics return empty
|
// that the cdm identifer is not yet sealed, and metrics return empty
|
||||||
// metrics data.
|
// metrics data.
|
||||||
@@ -2065,7 +2099,7 @@ TEST_F(WVDrmPluginTest, ProvidesExpectedDefaultPropertiesToCdm) {
|
|||||||
StrictMock<MockCrypto> crypto;
|
StrictMock<MockCrypto> crypto;
|
||||||
std::string appPackageName;
|
std::string appPackageName;
|
||||||
|
|
||||||
const CdmClientPropertySet* propertySet = NULL;
|
const CdmClientPropertySet* propertySet = nullptr;
|
||||||
|
|
||||||
// Provide expected mock behavior
|
// Provide expected mock behavior
|
||||||
{
|
{
|
||||||
@@ -2104,7 +2138,7 @@ TEST_F(WVDrmPluginTest, CanSetAppId) {
|
|||||||
StrictMock<MockCrypto> crypto;
|
StrictMock<MockCrypto> crypto;
|
||||||
std::string appPackageName;
|
std::string appPackageName;
|
||||||
|
|
||||||
const CdmClientPropertySet* propertySet = NULL;
|
const CdmClientPropertySet* propertySet = nullptr;
|
||||||
|
|
||||||
// Provide expected mock behavior
|
// Provide expected mock behavior
|
||||||
{
|
{
|
||||||
@@ -2205,7 +2239,7 @@ TEST_F(WVDrmPluginTest, CanSetSecurityLevel) {
|
|||||||
StrictMock<MockCrypto> crypto;
|
StrictMock<MockCrypto> crypto;
|
||||||
std::string appPackageName;
|
std::string appPackageName;
|
||||||
|
|
||||||
const CdmClientPropertySet* propertySet = NULL;
|
const CdmClientPropertySet* propertySet = nullptr;
|
||||||
|
|
||||||
EXPECT_CALL(*cdm, QueryStatus(_, QUERY_KEY_SECURITY_LEVEL, _))
|
EXPECT_CALL(*cdm, QueryStatus(_, QUERY_KEY_SECURITY_LEVEL, _))
|
||||||
.WillOnce(DoAll(SetArgPointee<2>(QUERY_VALUE_SECURITY_LEVEL_L3),
|
.WillOnce(DoAll(SetArgPointee<2>(QUERY_VALUE_SECURITY_LEVEL_L3),
|
||||||
@@ -2335,7 +2369,7 @@ TEST_F(WVDrmPluginTest, CanSetPrivacyMode) {
|
|||||||
StrictMock<MockCrypto> crypto;
|
StrictMock<MockCrypto> crypto;
|
||||||
std::string appPackageName;
|
std::string appPackageName;
|
||||||
|
|
||||||
const CdmClientPropertySet* propertySet = NULL;
|
const CdmClientPropertySet* propertySet = nullptr;
|
||||||
|
|
||||||
// Provide expected mock behavior
|
// Provide expected mock behavior
|
||||||
{
|
{
|
||||||
@@ -2386,7 +2420,7 @@ TEST_F(WVDrmPluginTest, CanSetServiceCertificate) {
|
|||||||
StrictMock<MockCrypto> crypto;
|
StrictMock<MockCrypto> crypto;
|
||||||
std::string appPackageName;
|
std::string appPackageName;
|
||||||
|
|
||||||
const CdmClientPropertySet* propertySet = NULL;
|
const CdmClientPropertySet* propertySet = nullptr;
|
||||||
|
|
||||||
static const size_t kPrivacyCertSize = 256;
|
static const size_t kPrivacyCertSize = 256;
|
||||||
uint8_t privacyCertRaw[kPrivacyCertSize];
|
uint8_t privacyCertRaw[kPrivacyCertSize];
|
||||||
@@ -2459,7 +2493,7 @@ TEST_F(WVDrmPluginTest, CanSetSessionSharing) {
|
|||||||
StrictMock<MockCrypto> crypto;
|
StrictMock<MockCrypto> crypto;
|
||||||
std::string appPackageName;
|
std::string appPackageName;
|
||||||
|
|
||||||
const CdmClientPropertySet* propertySet = NULL;
|
const CdmClientPropertySet* propertySet = nullptr;
|
||||||
|
|
||||||
// Provide expected mock behavior
|
// Provide expected mock behavior
|
||||||
{
|
{
|
||||||
@@ -2534,7 +2568,7 @@ TEST_F(WVDrmPluginTest, AllowsStoringOfSessionSharingId) {
|
|||||||
StrictMock<MockCrypto> crypto;
|
StrictMock<MockCrypto> crypto;
|
||||||
std::string appPackageName;
|
std::string appPackageName;
|
||||||
|
|
||||||
CdmClientPropertySet* propertySet = NULL;
|
CdmClientPropertySet* propertySet = nullptr;
|
||||||
|
|
||||||
uint32_t sharingId;
|
uint32_t sharingId;
|
||||||
FILE* fp = fopen("/dev/urandom", "r");
|
FILE* fp = fopen("/dev/urandom", "r");
|
||||||
@@ -2570,6 +2604,126 @@ TEST_F(WVDrmPluginTest, AllowsStoringOfSessionSharingId) {
|
|||||||
EXPECT_EQ(sharingId, propertySet->session_sharing_id());
|
EXPECT_EQ(sharingId, propertySet->session_sharing_id());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(WVDrmPluginTest, CanSetDecryptHashProperties) {
|
||||||
|
android::sp<StrictMock<MockCDM>> cdm = new StrictMock<MockCDM>();
|
||||||
|
StrictMock<MockCrypto> crypto;
|
||||||
|
std::string appPackageName;
|
||||||
|
|
||||||
|
// Provide expected mock behavior
|
||||||
|
{
|
||||||
|
// Provide expected behavior in response to OpenSession and store the
|
||||||
|
// property set
|
||||||
|
EXPECT_CALL(*cdm, OpenSession(_, _, _, _, _))
|
||||||
|
.WillRepeatedly(DoAll(SetArgPointee<4>(cdmSessionId),
|
||||||
|
testing::Return(wvcdm::NO_ERROR)));
|
||||||
|
|
||||||
|
// Provide expected behavior when plugin requests session control info
|
||||||
|
EXPECT_CALL(*cdm, QueryOemCryptoSessionId(cdmSessionId, _))
|
||||||
|
.WillRepeatedly(Invoke(setSessionIdOnMap<4>));
|
||||||
|
|
||||||
|
EXPECT_CALL(*cdm, CloseSession(_))
|
||||||
|
.Times(AtLeast(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
WVDrmPlugin plugin(cdm.get(), appPackageName, &crypto, false);
|
||||||
|
|
||||||
|
hidl_vec<uint8_t> hSessionId;
|
||||||
|
hSessionId.setToExternal(sessionIdRaw, kSessionIdSize);
|
||||||
|
plugin.openSession([&](Status status, hidl_vec<uint8_t> hSessionId) {
|
||||||
|
ASSERT_EQ(Status::OK, status);
|
||||||
|
sessionId.clear();
|
||||||
|
sessionId.assign(hSessionId.data(), hSessionId.data() + hSessionId.size());
|
||||||
|
});
|
||||||
|
|
||||||
|
// CDM expects the string property value to be in the following format:
|
||||||
|
// "<sessionId>,<frameNumber>,<base64encodedHash>"
|
||||||
|
static const std::string frameNumber = ",1";
|
||||||
|
uint32_t hash = 0xbeef; // crc32 hash
|
||||||
|
std::vector<uint8_t> hashVector(
|
||||||
|
reinterpret_cast<uint8_t*>(&hash),
|
||||||
|
reinterpret_cast<uint8_t*>(&hash) + sizeof(uint32_t));
|
||||||
|
std::string base64EncodedHash = Base64Encode(hashVector);
|
||||||
|
std::string computedHash(sessionId.begin(), sessionId.end());
|
||||||
|
computedHash.append(frameNumber.c_str());
|
||||||
|
computedHash.append(base64EncodedHash.c_str());
|
||||||
|
Status status = plugin.setPropertyString(hidl_string("decryptHash"),
|
||||||
|
hidl_string(computedHash));
|
||||||
|
ASSERT_NE(Status::OK, status);
|
||||||
|
|
||||||
|
status = plugin.closeSession(toHidlVec(sessionId));
|
||||||
|
ASSERT_EQ(Status::OK, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(WVDrmPluginTest, DoesNotSetDecryptHashProperties) {
|
||||||
|
android::sp<StrictMock<MockCDM>> cdm = new StrictMock<MockCDM>();
|
||||||
|
StrictMock<MockCrypto> crypto;
|
||||||
|
std::string appPackageName;
|
||||||
|
|
||||||
|
// Provide expected mock behavior
|
||||||
|
{
|
||||||
|
// Provide expected behavior in response to OpenSession and store the
|
||||||
|
// property set
|
||||||
|
EXPECT_CALL(*cdm, OpenSession(_, _, _, _, _))
|
||||||
|
.WillRepeatedly(DoAll(SetArgPointee<4>(cdmSessionId),
|
||||||
|
testing::Return(wvcdm::NO_ERROR)));
|
||||||
|
|
||||||
|
// Provide expected behavior when plugin requests session control info
|
||||||
|
EXPECT_CALL(*cdm, QueryOemCryptoSessionId(cdmSessionId, _))
|
||||||
|
.WillRepeatedly(Invoke(setSessionIdOnMap<4>));
|
||||||
|
|
||||||
|
EXPECT_CALL(*cdm, CloseSession(_))
|
||||||
|
.Times(AtLeast(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
WVDrmPlugin plugin(cdm.get(), appPackageName, &crypto, false);
|
||||||
|
|
||||||
|
hidl_vec<uint8_t> hSessionId;
|
||||||
|
hSessionId.setToExternal(sessionIdRaw, kSessionIdSize);
|
||||||
|
plugin.openSession([&](Status status, hidl_vec<uint8_t> hSessionId) {
|
||||||
|
ASSERT_EQ(Status::OK, status);
|
||||||
|
sessionId.clear();
|
||||||
|
sessionId.assign(hSessionId.data(), hSessionId.data() + hSessionId.size());
|
||||||
|
});
|
||||||
|
|
||||||
|
// CDM expects the string property value to be in the following format:
|
||||||
|
// "<sessionId>,<frameNumber>,<base64encodedHash>"
|
||||||
|
static const std::string frameNumber = ",1";
|
||||||
|
static const std::string hash = ",AZaz0+,/";
|
||||||
|
std::string value(sessionId.begin(), sessionId.end());
|
||||||
|
value.append(frameNumber.c_str());
|
||||||
|
|
||||||
|
// Tests for missing token handling
|
||||||
|
Status status = plugin.setPropertyString(hidl_string("decryptHash"),
|
||||||
|
hidl_string(value));
|
||||||
|
EXPECT_NE(Status::OK, status);
|
||||||
|
|
||||||
|
// Tests for empty token
|
||||||
|
value.append(",");
|
||||||
|
status = plugin.setPropertyString(hidl_string("decryptHash"),
|
||||||
|
hidl_string(value));
|
||||||
|
EXPECT_NE(Status::OK, status);
|
||||||
|
|
||||||
|
// Tests for invalid sessionId
|
||||||
|
value.clear();
|
||||||
|
value.append("bad session id");
|
||||||
|
value.append(",1");
|
||||||
|
value.append(hash.c_str());
|
||||||
|
status = plugin.setPropertyString(hidl_string("decryptHash"),
|
||||||
|
hidl_string(value));
|
||||||
|
EXPECT_NE(Status::OK, status);
|
||||||
|
|
||||||
|
// Tests for malformed Base64encode hash, with a ","
|
||||||
|
std::string computedHash(sessionId.begin(), sessionId.end());
|
||||||
|
computedHash.append(frameNumber.c_str());
|
||||||
|
computedHash.append(hash.c_str());
|
||||||
|
status = plugin.setPropertyString(hidl_string("decryptHash"),
|
||||||
|
hidl_string(computedHash));
|
||||||
|
EXPECT_NE(Status::OK, status);
|
||||||
|
|
||||||
|
status = plugin.closeSession(toHidlVec(sessionId));
|
||||||
|
ASSERT_EQ(Status::OK, status);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
} // namespace V1_2
|
} // namespace V1_2
|
||||||
} // namespace drm
|
} // namespace drm
|
||||||
|
|||||||
Reference in New Issue
Block a user