Group license support
This commit is contained in:
@@ -12,19 +12,11 @@
|
||||
|
||||
#include "cas_status.h"
|
||||
#include "cas_util.h"
|
||||
#include "crypto_key.h"
|
||||
#include "device_files.pb.h"
|
||||
#include "license_protocol.pb.h"
|
||||
#include "mock_crypto_session.h"
|
||||
#include "string_conversions.h"
|
||||
|
||||
// Prototype for ExtractEntitlementKeys. This prototype is added here to allow
|
||||
// this method to be unit tested without being added to CasLicense header.
|
||||
namespace wvcas {
|
||||
std::vector<wvcas::CryptoKey> ExtractEntitlementKeys(
|
||||
const video_widevine::License& license);
|
||||
} // namespace wvcas
|
||||
|
||||
using ::testing::_;
|
||||
using ::testing::AllOf;
|
||||
using ::testing::DoAll;
|
||||
@@ -211,41 +203,6 @@ std::string CreateLicenseFileData() {
|
||||
return hashed_file.SerializeAsString();
|
||||
}
|
||||
|
||||
TEST(CasLicenseUtilityTest, ExtractEntitlementKeys) {
|
||||
video_widevine::License license;
|
||||
|
||||
auto* key = license.add_key();
|
||||
key->set_type(video_widevine::License::KeyContainer::ENTITLEMENT);
|
||||
key->set_id(kKeyIDVideo);
|
||||
key->set_iv(kKeyVideoIV);
|
||||
key->mutable_key_control()->set_key_control_block(kKeyControlVideo);
|
||||
key->mutable_key_control()->set_iv(kKeyControlIVVideo);
|
||||
key->set_track_label(kTrackTypeVideo);
|
||||
|
||||
key = license.add_key();
|
||||
key->set_type(video_widevine::License::KeyContainer::ENTITLEMENT);
|
||||
key->set_id(kKeyIDAudio);
|
||||
key->set_iv(kKeyAudioIV);
|
||||
key->mutable_key_control()->set_key_control_block(kKeyControlAudio);
|
||||
key->mutable_key_control()->set_iv(kKeyControlIVAudio);
|
||||
key->set_track_label(kTrackTypeAudio);
|
||||
|
||||
std::vector<wvcas::CryptoKey> keys = wvcas::ExtractEntitlementKeys(license);
|
||||
ASSERT_EQ(2, keys.size());
|
||||
|
||||
EXPECT_EQ(kKeyIDVideo, keys[0].key_id());
|
||||
EXPECT_EQ(kKeyVideoIV, keys[0].key_data_iv());
|
||||
EXPECT_EQ(kKeyControlVideo, keys[0].key_control());
|
||||
EXPECT_EQ(kKeyControlIVVideo, keys[0].key_control_iv());
|
||||
EXPECT_EQ(kTrackTypeVideo, keys[0].track_label());
|
||||
|
||||
EXPECT_EQ(kKeyIDAudio, keys[1].key_id());
|
||||
EXPECT_EQ(kKeyAudioIV, keys[1].key_data_iv());
|
||||
EXPECT_EQ(kKeyControlAudio, keys[1].key_control());
|
||||
EXPECT_EQ(kKeyControlIVAudio, keys[1].key_control_iv());
|
||||
EXPECT_EQ(kTrackTypeAudio, keys[1].track_label());
|
||||
}
|
||||
|
||||
TEST_F(CasLicenseTest, GenerateDeviceProvisioningRequest) {
|
||||
strict_mock_ = std::make_shared<StrictMockCryptoSession>();
|
||||
TestCasLicense cas_license;
|
||||
@@ -409,24 +366,31 @@ TEST_F(CasLicenseTest, HandleEntitlementResponse) {
|
||||
EXPECT_CALL(*cas_license.policy_engine_, SetLicense(_));
|
||||
EXPECT_CALL(*cas_license.policy_engine_, CanPersist())
|
||||
.WillOnce(Return(false));
|
||||
status = cas_license.HandleEntitlementResponse(entitlement_response, nullptr);
|
||||
status = cas_license.HandleEntitlementResponse(entitlement_response,
|
||||
/*content_id_filter=*/nullptr,
|
||||
/*device_file=*/nullptr);
|
||||
EXPECT_EQ(wvcas::CasStatusCode::kNoError, status.status_code());
|
||||
// Not a group license.
|
||||
EXPECT_TRUE(cas_license.GetGroupId().empty());
|
||||
EXPECT_TRUE(cas_license.GetContentIdList().empty());
|
||||
EXPECT_FALSE(cas_license.IsGroupLicense());
|
||||
EXPECT_FALSE(cas_license.IsMultiContentLicense());
|
||||
|
||||
// Valid with device file.
|
||||
EXPECT_CALL(*cas_license.policy_engine_, SetLicense(_));
|
||||
EXPECT_CALL(*cas_license.policy_engine_, CanPersist())
|
||||
.WillOnce(Return(false));
|
||||
std::string device_file;
|
||||
status =
|
||||
cas_license.HandleEntitlementResponse(entitlement_response, &device_file);
|
||||
status = cas_license.HandleEntitlementResponse(
|
||||
entitlement_response, /*content_id_filter=*/nullptr, &device_file);
|
||||
EXPECT_TRUE(device_file.empty());
|
||||
EXPECT_EQ(wvcas::CasStatusCode::kNoError, status.status_code());
|
||||
|
||||
// Valid with device file and can_persist = true.
|
||||
EXPECT_CALL(*cas_license.policy_engine_, SetLicense(_));
|
||||
EXPECT_CALL(*cas_license.policy_engine_, CanPersist()).WillOnce(Return(true));
|
||||
status =
|
||||
cas_license.HandleEntitlementResponse(entitlement_response, &device_file);
|
||||
status = cas_license.HandleEntitlementResponse(
|
||||
entitlement_response, /*content_id_filter=*/nullptr, &device_file);
|
||||
EXPECT_FALSE(device_file.empty());
|
||||
EXPECT_EQ(wvcas::CasStatusCode::kNoError, status.status_code());
|
||||
|
||||
@@ -632,6 +596,79 @@ TEST_F(CasLicenseTest, RestoreLicense) {
|
||||
EXPECT_CALL(*cas_license.policy_engine_, SetLicense(_));
|
||||
EXPECT_CALL(*cas_license.policy_engine_, UpdateLicense(_));
|
||||
EXPECT_EQ(wvcas::CasStatusCode::kNoError,
|
||||
cas_license.HandleStoredLicense(wrapped_rsa_key_, license_file_data)
|
||||
cas_license
|
||||
.HandleStoredLicense(wrapped_rsa_key_, license_file_data,
|
||||
/*content_id_filter=*/nullptr)
|
||||
.status_code());
|
||||
}
|
||||
|
||||
TEST_F(CasLicenseTest, HandleMultiContentEntitlementResponse) {
|
||||
strict_mock_ = std::make_shared<StrictMockCryptoSession>();
|
||||
TestCasLicense cas_license;
|
||||
EXPECT_CALL(*cas_license.policy_engine_, initialize(_, _));
|
||||
EXPECT_EQ(wvcas::CasStatusCode::kNoError,
|
||||
cas_license.initialize(strict_mock_, nullptr).status_code());
|
||||
EXPECT_CALL(*strict_mock_, GetOEMPublicCertificate(_, _))
|
||||
.WillOnce(Return(wvcas::CasStatusCode::kCryptoSessionError));
|
||||
EXPECT_CALL(*strict_mock_, APIVersion(_))
|
||||
.WillOnce(Return(wvcas::CasStatusCode::kNoError));
|
||||
EXPECT_CALL(*strict_mock_, GenerateNonce(_))
|
||||
.WillOnce(Return(wvcas::CasStatusCode::kNoError));
|
||||
EXPECT_CALL(*strict_mock_,
|
||||
PrepareAndSignLicenseRequest(_, NotNull(), NotNull()))
|
||||
.WillOnce(DoAll(SetArgPointee<2>(kExpectedSignature),
|
||||
Return(wvcas::CasStatusCode::kNoError)));
|
||||
EXPECT_CALL(*strict_mock_, LoadDeviceRSAKey(_, _));
|
||||
|
||||
std::string serialized_entitlement_request;
|
||||
wvcas::CasStatus status = cas_license.GenerateEntitlementRequest(
|
||||
kInitializationData, device_certificate_, wrapped_rsa_key_,
|
||||
wvcas::LicenseType::kStreaming, &serialized_entitlement_request);
|
||||
EXPECT_EQ(wvcas::CasStatusCode::kNoError, status.status_code());
|
||||
|
||||
// Create multi content entitlement response.
|
||||
video_widevine::LicenseCategorySpec license_category_spec;
|
||||
license_category_spec.set_license_category(
|
||||
video_widevine::LicenseCategorySpec::MULTI_CONTENT_LICENSE);
|
||||
license_category_spec.set_group_id("group_id");
|
||||
video_widevine::License license;
|
||||
*license.mutable_license_category_spec() = license_category_spec;
|
||||
video_widevine::License::KeyContainer::KeyCategorySpec key_category_spec;
|
||||
key_category_spec.set_content_id("content_id_1");
|
||||
auto* key = license.add_key();
|
||||
key->set_type(video_widevine::License_KeyContainer::ENTITLEMENT);
|
||||
*key->mutable_key_category_spec() = key_category_spec;
|
||||
key = license.add_key();
|
||||
key->set_type(video_widevine::License_KeyContainer::ENTITLEMENT);
|
||||
*key->mutable_key_category_spec() = key_category_spec;
|
||||
key = license.add_key();
|
||||
key->set_type(video_widevine::License_KeyContainer::ENTITLEMENT);
|
||||
key_category_spec.set_content_id("content_id_2");
|
||||
*key->mutable_key_category_spec() = key_category_spec;
|
||||
|
||||
video_widevine::SignedMessage signed_message;
|
||||
license.SerializeToString(signed_message.mutable_msg());
|
||||
signed_message.set_type(video_widevine::SignedMessage::CAS_LICENSE);
|
||||
signed_message.set_signature(kExpectedSignature);
|
||||
signed_message.set_session_key(kExpectedSignature);
|
||||
signed_message.set_oemcrypto_core_message(kCoreMessage);
|
||||
std::string entitlement_response = signed_message.SerializeAsString();
|
||||
|
||||
EXPECT_CALL(*strict_mock_, DeriveKeysFromSessionKey(_, _, _, _, _, _))
|
||||
.WillRepeatedly(Return(wvcas::CasStatusCode::kNoError));
|
||||
EXPECT_CALL(*strict_mock_, LoadLicense(_, _, _))
|
||||
.WillRepeatedly(Return(wvcas::CasStatusCode::kNoError));
|
||||
EXPECT_CALL(*cas_license.policy_engine_, SetLicense(_));
|
||||
EXPECT_CALL(*cas_license.policy_engine_, CanPersist()).WillOnce(Return(true));
|
||||
|
||||
status = cas_license.HandleEntitlementResponse(entitlement_response,
|
||||
/*content_id_filter=*/nullptr,
|
||||
/*device_file=*/nullptr);
|
||||
EXPECT_EQ(status.status_code(), wvcas::CasStatusCode::kNoError);
|
||||
// Not a group license.
|
||||
EXPECT_EQ(cas_license.GetGroupId(), "group_id");
|
||||
EXPECT_THAT(cas_license.GetContentIdList(),
|
||||
testing::ElementsAre("content_id_1", "content_id_2"));
|
||||
EXPECT_FALSE(cas_license.IsGroupLicense());
|
||||
EXPECT_TRUE(cas_license.IsMultiContentLicense());
|
||||
}
|
||||
@@ -49,12 +49,6 @@ static const std::string kOddWrappedKeyIv("odd_wrapped_content_key_iv");
|
||||
static const std::string kEvenContentIv("even_content_iv");
|
||||
static const std::string kOddContentIv("odd_content_iv");
|
||||
|
||||
// Defined in cas_license.cpp.
|
||||
namespace wvcas {
|
||||
extern std::vector<CryptoKey> ExtractKeyControlKeys(
|
||||
const video_widevine::License& license);
|
||||
} // namespace wvcas
|
||||
|
||||
// TODO(jfore): Add validation of arg->buffer based on type. Type is assumed to
|
||||
// be clear.
|
||||
MATCHER_P2(IsValidOutputBuffer, type, dest, "") {
|
||||
@@ -271,12 +265,6 @@ class MockedOEMCrypto : public wvcas::OEMCryptoInterface {
|
||||
const uint8_t* content_key_id,
|
||||
size_t content_key_id_length,
|
||||
OEMCryptoCipherMode cipher_mode));
|
||||
MOCK_METHOD7(OEMCrypto_RefreshKeys,
|
||||
OEMCryptoResult(OEMCrypto_SESSION session,
|
||||
const uint8_t* message, size_t message_length,
|
||||
const uint8_t* signature,
|
||||
size_t signature_length, size_t num_keys,
|
||||
const OEMCrypto_KeyRefreshObject* key_array));
|
||||
MOCK_METHOD2(OEMCrypto_GetDeviceID,
|
||||
OEMCryptoResult(uint8_t* deviceID, size_t* idLength));
|
||||
MOCK_METHOD2(OEMCrypto_CreateEntitledKeySession,
|
||||
@@ -937,56 +925,35 @@ TEST_F(CryptoSessionTest, SelectKeys) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(CryptoSessionTest, RefreshKeys) {
|
||||
TestCryptoSession<StrictMock<MockedOEMCrypto> > crypto_session(
|
||||
strict_oemcrypto_interface_);
|
||||
|
||||
EXPECT_CALL(strict_oemcrypto_interface_, OEMCrypto_Initialize());
|
||||
EXPECT_CALL(strict_oemcrypto_interface_, OEMCrypto_Terminate());
|
||||
EXPECT_CALL(strict_oemcrypto_interface_, OEMCrypto_OpenSession(_))
|
||||
TEST_F(CryptoSessionTest, LoadRenewal) {
|
||||
TestCryptoSession<NiceMock<MockedOEMCrypto> > crypto_session(
|
||||
nice_oemcrypto_interface_);
|
||||
EXPECT_CALL(nice_oemcrypto_interface_, OEMCrypto_OpenSession(_))
|
||||
.WillOnce(
|
||||
DoAll(SetArgPointee<0>(kOemcSessionId), Return(OEMCrypto_SUCCESS)));
|
||||
|
||||
ASSERT_EQ(wvcas::CasStatusCode::kNoError,
|
||||
EXPECT_EQ(wvcas::CasStatusCode::kNoError,
|
||||
crypto_session.initialize().status_code());
|
||||
|
||||
video_widevine::License license;
|
||||
|
||||
// Empty key array - no keys.
|
||||
ASSERT_EQ(wvcas::CasStatusCode::kNoError,
|
||||
crypto_session
|
||||
.RefreshKeys(license.SerializeAsString(), "signature",
|
||||
std::vector<wvcas::CryptoKey>())
|
||||
.status_code());
|
||||
|
||||
auto* key_1 = license.add_key();
|
||||
key_1->set_type(video_widevine::License_KeyContainer::KEY_CONTROL);
|
||||
auto* key_2 = license.add_key();
|
||||
key_2->set_type(video_widevine::License_KeyContainer::KEY_CONTROL);
|
||||
auto* key_3 = license.add_key();
|
||||
key_3->set_type(video_widevine::License_KeyContainer::KEY_CONTROL);
|
||||
|
||||
std::vector<wvcas::CryptoKey> key_array =
|
||||
wvcas::ExtractKeyControlKeys(license);
|
||||
|
||||
EXPECT_CALL(strict_oemcrypto_interface_,
|
||||
OEMCrypto_RefreshKeys(_, _, _, _, _, 3, _))
|
||||
const std::string signed_message("signed_message");
|
||||
const std::string core_message("core_message");
|
||||
const std::string signature("signature");
|
||||
EXPECT_CALL(
|
||||
nice_oemcrypto_interface_,
|
||||
OEMCrypto_LoadRenewal(kOemcSessionId, NotNull(),
|
||||
signed_message.size() + core_message.size(),
|
||||
core_message.size(), NotNull(), signature.size()))
|
||||
.WillOnce(Return(OEMCrypto_ERROR_SIGNATURE_FAILURE))
|
||||
.WillOnce(Return(OEMCrypto_SUCCESS));
|
||||
|
||||
// OEMCrypto error.
|
||||
ASSERT_EQ(
|
||||
wvcas::CasStatusCode::kCryptoSessionError,
|
||||
crypto_session
|
||||
.RefreshKeys(license.SerializeAsString(), "signature", key_array)
|
||||
.status_code());
|
||||
ASSERT_EQ(wvcas::CasStatusCode::kCryptoSessionError,
|
||||
crypto_session.LoadRenewal(signed_message, core_message, signature)
|
||||
.status_code());
|
||||
|
||||
// Valid.
|
||||
ASSERT_EQ(
|
||||
wvcas::CasStatusCode::kNoError,
|
||||
crypto_session
|
||||
.RefreshKeys(license.SerializeAsString(), "signature", key_array)
|
||||
.status_code());
|
||||
ASSERT_EQ(wvcas::CasStatusCode::kNoError,
|
||||
crypto_session.LoadRenewal(signed_message, core_message, signature)
|
||||
.status_code());
|
||||
}
|
||||
|
||||
TEST_F(CryptoSessionTest, ReadUniqueId) {
|
||||
|
||||
@@ -55,7 +55,7 @@ class EcmParserV2Test : public testing::Test {
|
||||
void BuildEcm(bool with_rotation, bool content_iv_flag);
|
||||
|
||||
std::vector<uint8_t> ecm_data_;
|
||||
std::unique_ptr<const wvcas::EcmParserV2> parser_;
|
||||
std::unique_ptr<wvcas::EcmParserV2> parser_;
|
||||
};
|
||||
|
||||
size_t EcmParserV2Test::ContentKeyIVSize(bool content_iv_flag) {
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
namespace wvcas {
|
||||
namespace {
|
||||
|
||||
using video_widevine::EcmGroupKeyData;
|
||||
using video_widevine::EcmMetaData;
|
||||
using video_widevine::EcmPayload;
|
||||
using video_widevine::SignedEcmPayload;
|
||||
@@ -84,7 +85,7 @@ TEST(EcmParserV3Test, CreateWithEvenKeySuccess) {
|
||||
signed_ecm_payload.set_serialized_payload(ecm_payload.SerializeAsString());
|
||||
std::vector<uint8_t> ecm = GenerateEcm(signed_ecm_payload);
|
||||
|
||||
std::unique_ptr<const EcmParserV3> parser = EcmParserV3::Create(ecm);
|
||||
std::unique_ptr<EcmParserV3> parser = EcmParserV3::Create(ecm);
|
||||
|
||||
ASSERT_TRUE(parser != nullptr);
|
||||
EXPECT_EQ(parser->version(), kEcmVersion);
|
||||
@@ -121,7 +122,7 @@ TEST(EcmParserV3Test, CreateWithEvenOddKeysSuccess) {
|
||||
signed_ecm_payload.set_serialized_payload(ecm_payload.SerializeAsString());
|
||||
std::vector<uint8_t> ecm = GenerateEcm(signed_ecm_payload);
|
||||
|
||||
std::unique_ptr<const EcmParserV3> parser = EcmParserV3::Create(ecm);
|
||||
std::unique_ptr<EcmParserV3> parser = EcmParserV3::Create(ecm);
|
||||
|
||||
ASSERT_TRUE(parser != nullptr);
|
||||
EXPECT_TRUE(parser->rotation_enabled());
|
||||
@@ -155,7 +156,7 @@ TEST(EcmParserV3Test, CreateWithOmittedOddKeyFieldsSuccess) {
|
||||
signed_ecm_payload.set_serialized_payload(ecm_payload.SerializeAsString());
|
||||
std::vector<uint8_t> ecm = GenerateEcm(signed_ecm_payload);
|
||||
|
||||
std::unique_ptr<const EcmParserV3> parser = EcmParserV3::Create(ecm);
|
||||
std::unique_ptr<EcmParserV3> parser = EcmParserV3::Create(ecm);
|
||||
|
||||
ASSERT_TRUE(parser != nullptr);
|
||||
EXPECT_TRUE(parser->rotation_enabled());
|
||||
@@ -187,7 +188,7 @@ TEST(EcmParserV3Test, AgeRestrictionSuccess) {
|
||||
signed_ecm_payload.set_serialized_payload(ecm_payload.SerializeAsString());
|
||||
std::vector<uint8_t> ecm = GenerateEcm(signed_ecm_payload);
|
||||
|
||||
std::unique_ptr<const EcmParserV3> parser = EcmParserV3::Create(ecm);
|
||||
std::unique_ptr<EcmParserV3> parser = EcmParserV3::Create(ecm);
|
||||
|
||||
ASSERT_TRUE(parser != nullptr);
|
||||
EXPECT_EQ(parser->age_restriction(), expected_age_restriction);
|
||||
@@ -206,7 +207,7 @@ TEST_P(EcmParserV3AgeRestrictionTest, ExpectedAgeRestriction) {
|
||||
signed_ecm_payload.set_serialized_payload(ecm_payload.SerializeAsString());
|
||||
std::vector<uint8_t> ecm = GenerateEcm(signed_ecm_payload);
|
||||
|
||||
std::unique_ptr<const EcmParserV3> parser = EcmParserV3::Create(ecm);
|
||||
std::unique_ptr<EcmParserV3> parser = EcmParserV3::Create(ecm);
|
||||
|
||||
ASSERT_TRUE(parser != nullptr);
|
||||
EXPECT_EQ(parser->age_restriction(), expected_age_restriction);
|
||||
@@ -229,7 +230,7 @@ TEST_P(EcmParserV3CipherModeTest, ExpectedCipherMode) {
|
||||
signed_ecm_payload.set_serialized_payload(ecm_payload.SerializeAsString());
|
||||
std::vector<uint8_t> ecm = GenerateEcm(signed_ecm_payload);
|
||||
|
||||
std::unique_ptr<const EcmParserV3> parser = EcmParserV3::Create(ecm);
|
||||
std::unique_ptr<EcmParserV3> parser = EcmParserV3::Create(ecm);
|
||||
|
||||
ASSERT_TRUE(parser != nullptr);
|
||||
EXPECT_EQ(parser->crypto_mode(), expected);
|
||||
@@ -252,7 +253,7 @@ TEST(EcmParserV3Test, FingerprintingSuccess) {
|
||||
signed_ecm_payload.set_serialized_payload(ecm_payload.SerializeAsString());
|
||||
std::vector<uint8_t> ecm = GenerateEcm(signed_ecm_payload);
|
||||
|
||||
std::unique_ptr<const EcmParserV3> parser = EcmParserV3::Create(ecm);
|
||||
std::unique_ptr<EcmParserV3> parser = EcmParserV3::Create(ecm);
|
||||
|
||||
ASSERT_TRUE(parser != nullptr);
|
||||
EXPECT_TRUE(parser->has_fingerprinting());
|
||||
@@ -267,7 +268,7 @@ TEST(EcmParserV3Test, ServiceBlockingSuccess) {
|
||||
signed_ecm_payload.set_serialized_payload(ecm_payload.SerializeAsString());
|
||||
std::vector<uint8_t> ecm = GenerateEcm(signed_ecm_payload);
|
||||
|
||||
std::unique_ptr<const EcmParserV3> parser = EcmParserV3::Create(ecm);
|
||||
std::unique_ptr<EcmParserV3> parser = EcmParserV3::Create(ecm);
|
||||
|
||||
ASSERT_TRUE(parser != nullptr);
|
||||
EXPECT_TRUE(parser->has_service_blocking());
|
||||
@@ -281,11 +282,126 @@ TEST(EcmParserV3Test, SignatureSuccess) {
|
||||
signed_ecm_payload.set_signature(expected_signature);
|
||||
std::vector<uint8_t> ecm = GenerateEcm(signed_ecm_payload);
|
||||
|
||||
std::unique_ptr<const EcmParserV3> parser = EcmParserV3::Create(ecm);
|
||||
std::unique_ptr<EcmParserV3> parser = EcmParserV3::Create(ecm);
|
||||
|
||||
ASSERT_TRUE(parser != nullptr);
|
||||
EXPECT_EQ(parser->signature(), expected_signature);
|
||||
}
|
||||
|
||||
TEST(EcmParserV3Test, SetGroupIdSuccess) {
|
||||
const std::string group_id = "group_id";
|
||||
const std::string group_id2 = "another_group";
|
||||
EcmPayload ecm_payload;
|
||||
EcmGroupKeyData* group_key_data = ecm_payload.add_group_key_data();
|
||||
group_key_data->set_group_id(group_id);
|
||||
group_key_data = ecm_payload.add_group_key_data();
|
||||
group_key_data->set_group_id(group_id2);
|
||||
SignedEcmPayload signed_ecm_payload;
|
||||
signed_ecm_payload.set_serialized_payload(ecm_payload.SerializeAsString());
|
||||
std::vector<uint8_t> ecm = GenerateEcm(signed_ecm_payload);
|
||||
|
||||
std::unique_ptr<EcmParserV3> parser = EcmParserV3::Create(ecm);
|
||||
|
||||
ASSERT_TRUE(parser != nullptr);
|
||||
EXPECT_TRUE(parser->set_group_id(group_id));
|
||||
EXPECT_TRUE(parser->set_group_id(group_id2));
|
||||
}
|
||||
|
||||
TEST(EcmParserV3Test, SetUnknownGroupIdFail) {
|
||||
EcmPayload ecm_payload;
|
||||
EcmGroupKeyData* group_key_data = ecm_payload.add_group_key_data();
|
||||
group_key_data->set_group_id("group_id");
|
||||
SignedEcmPayload signed_ecm_payload;
|
||||
signed_ecm_payload.set_serialized_payload(ecm_payload.SerializeAsString());
|
||||
std::vector<uint8_t> ecm = GenerateEcm(signed_ecm_payload);
|
||||
|
||||
std::unique_ptr<EcmParserV3> parser = EcmParserV3::Create(ecm);
|
||||
|
||||
ASSERT_TRUE(parser != nullptr);
|
||||
EXPECT_FALSE(parser->set_group_id("unknown"));
|
||||
}
|
||||
|
||||
TEST(EcmParserV3Test, ParserWithGroupIdSuccess) {
|
||||
const std::string group_id = "group_id";
|
||||
EcmPayload ecm_payload;
|
||||
EcmGroupKeyData* group_key_data = ecm_payload.add_group_key_data();
|
||||
group_key_data->set_group_id(group_id);
|
||||
group_key_data->mutable_even_key_data()->set_entitlement_key_id(
|
||||
kEntitlementId);
|
||||
group_key_data->mutable_even_key_data()->set_wrapped_key_data(
|
||||
kWrappedContentKey);
|
||||
group_key_data->mutable_even_key_data()->set_content_iv(kContentIv);
|
||||
group_key_data->mutable_even_key_data()->set_wrapped_key_iv(kWrappedKeyIv);
|
||||
ecm_payload.mutable_even_key_data()->set_entitlement_key_id(kEntitlementId2);
|
||||
ecm_payload.mutable_even_key_data()->set_wrapped_key_data(
|
||||
kWrappedContentKey2);
|
||||
ecm_payload.mutable_even_key_data()->set_content_iv(kContentIv2);
|
||||
ecm_payload.mutable_even_key_data()->set_wrapped_key_iv(kWrappedKeyIv2);
|
||||
|
||||
SignedEcmPayload signed_ecm_payload;
|
||||
signed_ecm_payload.set_serialized_payload(ecm_payload.SerializeAsString());
|
||||
std::vector<uint8_t> ecm = GenerateEcm(signed_ecm_payload);
|
||||
|
||||
std::unique_ptr<EcmParserV3> parser = EcmParserV3::Create(ecm);
|
||||
|
||||
ASSERT_TRUE(parser != nullptr);
|
||||
// If group Id is not set, the normal keys will be returned.
|
||||
std::vector<uint8_t> result =
|
||||
parser->entitlement_key_id(KeySlotId::kEvenKeySlot);
|
||||
EXPECT_EQ(std::string(result.begin(), result.end()), kEntitlementId2);
|
||||
result = parser->wrapped_key_data(KeySlotId::kEvenKeySlot);
|
||||
EXPECT_EQ(std::string(result.begin(), result.end()), kWrappedContentKey2);
|
||||
result = parser->content_iv(KeySlotId::kEvenKeySlot);
|
||||
EXPECT_EQ(std::string(result.begin(), result.end()), kContentIv2);
|
||||
result = parser->wrapped_key_iv(KeySlotId::kEvenKeySlot);
|
||||
EXPECT_EQ(std::string(result.begin(), result.end()), kWrappedKeyIv2);
|
||||
|
||||
// Now set the group id.
|
||||
EXPECT_TRUE(parser->set_group_id(group_id));
|
||||
result = parser->entitlement_key_id(KeySlotId::kEvenKeySlot);
|
||||
EXPECT_EQ(std::string(result.begin(), result.end()), kEntitlementId);
|
||||
result = parser->wrapped_key_data(KeySlotId::kEvenKeySlot);
|
||||
EXPECT_EQ(std::string(result.begin(), result.end()), kWrappedContentKey);
|
||||
result = parser->content_iv(KeySlotId::kEvenKeySlot);
|
||||
EXPECT_EQ(std::string(result.begin(), result.end()), kContentIv);
|
||||
result = parser->wrapped_key_iv(KeySlotId::kEvenKeySlot);
|
||||
EXPECT_EQ(std::string(result.begin(), result.end()), kWrappedKeyIv);
|
||||
}
|
||||
|
||||
TEST(EcmParserV3Test, ParserGroupKeysWithOmittedFieldsSuccess) {
|
||||
const std::string group_id = "group_id";
|
||||
EcmPayload ecm_payload;
|
||||
EcmGroupKeyData* group_key_data = ecm_payload.add_group_key_data();
|
||||
group_key_data->set_group_id(group_id);
|
||||
group_key_data->mutable_even_key_data()->set_entitlement_key_id(
|
||||
kEntitlementId);
|
||||
group_key_data->mutable_even_key_data()->set_wrapped_key_data(
|
||||
kWrappedContentKey);
|
||||
// Content IV and wrapped key iv is omitted in |group_key_data|/
|
||||
ecm_payload.mutable_even_key_data()->set_entitlement_key_id(kEntitlementId2);
|
||||
ecm_payload.mutable_even_key_data()->set_wrapped_key_data(
|
||||
kWrappedContentKey2);
|
||||
ecm_payload.mutable_even_key_data()->set_content_iv(kContentIv2);
|
||||
ecm_payload.mutable_even_key_data()->set_wrapped_key_iv(kWrappedKeyIv2);
|
||||
SignedEcmPayload signed_ecm_payload;
|
||||
signed_ecm_payload.set_serialized_payload(ecm_payload.SerializeAsString());
|
||||
std::vector<uint8_t> ecm = GenerateEcm(signed_ecm_payload);
|
||||
|
||||
std::unique_ptr<EcmParserV3> parser = EcmParserV3::Create(ecm);
|
||||
ASSERT_TRUE(parser != nullptr);
|
||||
EXPECT_TRUE(parser->set_group_id(group_id));
|
||||
|
||||
std::vector<uint8_t> result =
|
||||
parser->entitlement_key_id(KeySlotId::kEvenKeySlot);
|
||||
EXPECT_EQ(std::string(result.begin(), result.end()), kEntitlementId);
|
||||
result = parser->wrapped_key_data(KeySlotId::kEvenKeySlot);
|
||||
EXPECT_EQ(std::string(result.begin(), result.end()), kWrappedContentKey);
|
||||
// Content IV and wrapped key iv are from normal non-group key.
|
||||
result = parser->content_iv(KeySlotId::kEvenKeySlot);
|
||||
EXPECT_EQ(std::string(result.begin(), result.end()), kContentIv2);
|
||||
result = parser->wrapped_key_iv(KeySlotId::kEvenKeySlot);
|
||||
EXPECT_EQ(std::string(result.begin(), result.end()), kWrappedKeyIv2);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace wvcas
|
||||
|
||||
@@ -55,6 +55,11 @@ TEST(IntegrationTests, TestCasPluginEventPassing) {
|
||||
EXPECT_EQ(kIntegrationTestPassed, RunNamedTest("TestCasPluginEventPassing"));
|
||||
}
|
||||
|
||||
TEST(IntegrationTests, TestSessionFailWithoutProvisioning) {
|
||||
EXPECT_EQ(kIntegrationTestPassed,
|
||||
RunNamedTest("TestSessionFailWithoutProvisioning"));
|
||||
}
|
||||
|
||||
TEST(IntegrationTests, TestUniqueIdQuery) {
|
||||
EXPECT_EQ(kIntegrationTestPassed, RunNamedTest("TestUniqueIdQuery"));
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
using ::testing::_;
|
||||
using ::testing::DoAll;
|
||||
using ::testing::NiceMock;
|
||||
using ::testing::NotNull;
|
||||
using ::testing::Return;
|
||||
using ::testing::SetArgPointee;
|
||||
using ::testing::StrictMock;
|
||||
@@ -54,20 +55,27 @@ class MockLicense : public wvcas::CasLicense {
|
||||
const std::string& wrapped_rsa_key,
|
||||
wvcas::LicenseType license_type,
|
||||
std::string* signed_license_request));
|
||||
MOCK_METHOD2(HandleStoredLicense,
|
||||
MOCK_METHOD3(HandleStoredLicense,
|
||||
wvcas::CasStatus(const std::string& wrapped_rsa_key,
|
||||
const std::string& license_file));
|
||||
const std::string& license_file,
|
||||
const std::string* content_id_filter));
|
||||
MOCK_METHOD2(GenerateEntitlementRenewalRequest,
|
||||
wvcas::CasStatus(const std::string& device_certificate,
|
||||
std::string* signed_renewal_request));
|
||||
MOCK_METHOD2(HandleEntitlementRenewalResponse,
|
||||
wvcas::CasStatus(const std::string& renewal_response,
|
||||
std::string* device_file));
|
||||
MOCK_METHOD2(HandleEntitlementResponse,
|
||||
MOCK_METHOD3(HandleEntitlementResponse,
|
||||
wvcas::CasStatus(const std::string& entitlement_response,
|
||||
const std::string* content_id_filter,
|
||||
std::string* device_file));
|
||||
MOCK_METHOD0(BeginDecryption, void());
|
||||
MOCK_METHOD0(UpdateLicenseForLicenseRemove, void());
|
||||
MOCK_METHOD(std::string, GetGroupId, (), (const, override));
|
||||
MOCK_METHOD(std::vector<std::string>, GetContentIdList, (),
|
||||
(const, override));
|
||||
MOCK_METHOD(bool, IsMultiContentLicense, (), (const, override));
|
||||
MOCK_METHOD(bool, IsGroupLicense, (), (const, override));
|
||||
};
|
||||
typedef StrictMock<MockLicense> StrictMockLicense;
|
||||
|
||||
@@ -136,8 +144,10 @@ class MockWidevineSession : public wvcas::WidevineCasSession {
|
||||
public:
|
||||
MockWidevineSession() {}
|
||||
~MockWidevineSession() override {}
|
||||
MOCK_METHOD2(processEcm, wvcas::CasStatus(const wvcas::CasEcm& ecm,
|
||||
uint8_t parental_control_age));
|
||||
MOCK_METHOD(wvcas::CasStatus, processEcm,
|
||||
(const wvcas::CasEcm& ecm, uint8_t parental_control_age,
|
||||
const std::string& license_group_id),
|
||||
(override));
|
||||
MOCK_METHOD2(HandleProcessEcm,
|
||||
wvcas::CasStatus(const wvcas::WvCasSessionId& sessionId,
|
||||
const wvcas::CasEcm& ecm));
|
||||
@@ -238,6 +248,7 @@ TEST_F(WidevineCasTest, generateEntitlementRequest) {
|
||||
.WillOnce(Return(wvcas::CasStatus::OkStatus()));
|
||||
EXPECT_EQ(wvcas::CasStatusCode::kNoError,
|
||||
cas_api.initialize(nullptr).status_code());
|
||||
EXPECT_CALL(*cas_api.license_, IsGroupLicense).WillRepeatedly(Return(false));
|
||||
|
||||
// Invalid parameter.
|
||||
std::string request, init_data, license_id;
|
||||
@@ -267,7 +278,7 @@ TEST_F(WidevineCasTest, generateEntitlementRequest) {
|
||||
.WillRepeatedly(Return(mock_file));
|
||||
EXPECT_CALL(*mock_file, Read(_, _)).WillRepeatedly(Return(mock_filesize));
|
||||
|
||||
EXPECT_CALL(*cas_api.license_, HandleStoredLicense(_, _))
|
||||
EXPECT_CALL(*cas_api.license_, HandleStoredLicense)
|
||||
.WillRepeatedly(Return(wvcas::CasStatus(
|
||||
wvcas::CasStatusCode::kCasLicenseError, "forced failure")));
|
||||
EXPECT_EQ(wvcas::CasStatusCode::kCasLicenseError,
|
||||
@@ -281,7 +292,7 @@ TEST_F(WidevineCasTest, generateEntitlementRequest) {
|
||||
EXPECT_CALL(*mock_file, Read(_, _)).WillRepeatedly(Return(mock_filesize));
|
||||
// For expired license file, remove it successfully
|
||||
// and return CasLicenseError.
|
||||
EXPECT_CALL(*cas_api.license_, HandleStoredLicense(_, _))
|
||||
EXPECT_CALL(*cas_api.license_, HandleStoredLicense)
|
||||
.WillRepeatedly(Return(wvcas::CasStatus::OkStatus()));
|
||||
EXPECT_CALL(*cas_api.license_, IsExpired()).WillRepeatedly(Return(true));
|
||||
EXPECT_CALL(*cas_api.file_system_, Remove(_)).WillRepeatedly(Return(true));
|
||||
@@ -442,6 +453,7 @@ TEST_P(WidevineCasTest, ECMProcessing) {
|
||||
EXPECT_CALL(*cas_api.license_, IsExpired()).WillRepeatedly(Return(false));
|
||||
EXPECT_EQ(wvcas::CasStatusCode::kNoError,
|
||||
cas_api.initialize(nullptr).status_code());
|
||||
EXPECT_CALL(*cas_api.license_, IsGroupLicense).WillRepeatedly(Return(false));
|
||||
|
||||
wvcas::WvCasSessionId video_sid;
|
||||
wvcas::WvCasSessionId audio_sid;
|
||||
@@ -487,10 +499,10 @@ TEST_P(WidevineCasTest, ECMProcessing) {
|
||||
int expected_begin_decryption_calls = expected_process_ecm_calls * 2;
|
||||
|
||||
EXPECT_CALL(*reinterpret_cast<MockWidevineSession*>(video_session.get()),
|
||||
processEcm(video_ecm, 0))
|
||||
processEcm(video_ecm, 0, ""))
|
||||
.Times(expected_process_ecm_calls);
|
||||
EXPECT_CALL(*reinterpret_cast<MockWidevineSession*>(audio_session.get()),
|
||||
processEcm(audio_ecm, 0))
|
||||
processEcm(audio_ecm, 0, ""))
|
||||
.Times(expected_process_ecm_calls);
|
||||
EXPECT_CALL(*cas_api.license_, BeginDecryption())
|
||||
.Times(expected_begin_decryption_calls);
|
||||
@@ -507,7 +519,7 @@ TEST_P(WidevineCasTest, ECMProcessing) {
|
||||
.WillOnce(Return(file_handle));
|
||||
|
||||
EXPECT_CALL(*file_handle, Read(_, _)).WillOnce(Return(file_data.size()));
|
||||
EXPECT_CALL(*cas_api.license_, HandleStoredLicense(_, _));
|
||||
EXPECT_CALL(*cas_api.license_, HandleStoredLicense);
|
||||
EXPECT_EQ(
|
||||
wvcas::CasStatusCode::kNoError,
|
||||
cas_api.generateEntitlementRequest("init_data", &request, license_id)
|
||||
@@ -520,12 +532,26 @@ TEST_P(WidevineCasTest, ECMProcessing) {
|
||||
license_id);
|
||||
} else {
|
||||
// Empty response.
|
||||
std::string init_data;
|
||||
EXPECT_CALL(*cas_api.license_, HandleEntitlementResponse(_, _))
|
||||
.WillOnce(Return(wvcas::CasStatusCode::kNoError));
|
||||
// Initialize CaMediaId.
|
||||
EXPECT_CALL(*cas_api.file_system_, Exists(_)).WillOnce(Return(false));
|
||||
EXPECT_CALL(*cas_api.license_, GenerateEntitlementRequest(_, _, _, _, _))
|
||||
.WillRepeatedly(Return(wvcas::CasStatus::OkStatus()));
|
||||
std::string request, init_data, license_id;
|
||||
EXPECT_EQ(
|
||||
wvcas::CasStatusCode::kNoError,
|
||||
cas_api.handleEntitlementResponse("response", init_data).status_code());
|
||||
cas_api.generateEntitlementRequest(init_data, &request, license_id)
|
||||
.status_code());
|
||||
|
||||
EXPECT_CALL(*cas_api.license_, HandleEntitlementResponse)
|
||||
.WillOnce(Return(wvcas::CasStatusCode::kNoError));
|
||||
std::string multi_content_license_info;
|
||||
std::string group_license_info;
|
||||
EXPECT_EQ(wvcas::CasStatusCode::kNoError,
|
||||
cas_api
|
||||
.handleEntitlementResponse("response", init_data,
|
||||
multi_content_license_info,
|
||||
group_license_info)
|
||||
.status_code());
|
||||
}
|
||||
|
||||
EXPECT_EQ(wvcas::CasStatusCode::kNoError,
|
||||
@@ -589,17 +615,217 @@ TEST_F(WidevineCasTest, RemoveLicense) {
|
||||
|
||||
EXPECT_EQ(wvcas::CasStatusCode::kNoError,
|
||||
cas_api.RemoveLicense(mocked_file_name).status_code());
|
||||
}
|
||||
|
||||
// Happy case: remove the in used license file
|
||||
std::string hash;
|
||||
std::string kBasePathPrefix = "/data/vendor/mediacas/IDM/widevine/";
|
||||
hash.resize(SHA256_DIGEST_LENGTH);
|
||||
const auto* input =
|
||||
reinterpret_cast<const unsigned char*>(mocked_file_name.data());
|
||||
auto* output = reinterpret_cast<unsigned char*>(&hash[0]);
|
||||
SHA256(input, mocked_file_name.size(), output);
|
||||
std::string full_filename = GenerateTestLicenseFileName(mocked_file_name);
|
||||
TEST_F(WidevineCasTest, RemoveLicenseInUse) {
|
||||
TestWidevineCas cas_api;
|
||||
EXPECT_CALL(*cas_api.crypto_session_, initialize())
|
||||
.WillOnce(Return(wvcas::CasStatus::OkStatus()));
|
||||
EXPECT_EQ(cas_api.initialize(nullptr).status_code(),
|
||||
wvcas::CasStatusCode::kNoError);
|
||||
EXPECT_CALL(*cas_api.license_, IsExpired()).WillRepeatedly(Return(false));
|
||||
|
||||
// Initialize media_id_.
|
||||
EXPECT_CALL(*cas_api.license_, GenerateEntitlementRequest)
|
||||
.WillOnce(Return(wvcas::CasStatus::OkStatus()));
|
||||
std::string request, init_data, license_id;
|
||||
EXPECT_EQ(cas_api.generateEntitlementRequest(init_data, &request, license_id)
|
||||
.status_code(),
|
||||
wvcas::CasStatusCode::kNoError);
|
||||
// Install the license
|
||||
EXPECT_CALL(*cas_api.license_, HandleEntitlementResponse(_, _, NotNull()))
|
||||
.WillOnce(DoAll(SetArgPointee<2>("device_file"),
|
||||
Return(wvcas::CasStatus::OkStatus())));
|
||||
EXPECT_CALL(*cas_api.license_, IsMultiContentLicense)
|
||||
.WillRepeatedly(Return(false));
|
||||
EXPECT_CALL(*cas_api.license_, IsGroupLicense).WillRepeatedly(Return(false));
|
||||
EXPECT_CALL(*cas_api.license_, GetGroupId).WillRepeatedly(Return(""));
|
||||
std::string multi_content_license_info;
|
||||
std::string group_license_info;
|
||||
EXPECT_EQ(cas_api
|
||||
.handleEntitlementResponse("response", license_id,
|
||||
multi_content_license_info,
|
||||
group_license_info)
|
||||
.status_code(),
|
||||
wvcas::CasStatusCode::kNoError);
|
||||
EXPECT_FALSE(license_id.empty());
|
||||
|
||||
MockFile mock_file;
|
||||
EXPECT_CALL(*cas_api.file_system_, Exists(_)).WillRepeatedly(Return(true));
|
||||
EXPECT_CALL(*cas_api.file_system_, Remove(_)).WillRepeatedly(Return(true));
|
||||
EXPECT_CALL(*cas_api.license_, UpdateLicenseForLicenseRemove()).Times(1);
|
||||
EXPECT_EQ(cas_api.RemoveLicense(license_id + ".lic").status_code(),
|
||||
wvcas::CasStatusCode::kNoError);
|
||||
}
|
||||
|
||||
TEST_F(WidevineCasTest, handleMultiContentEntitlementResponse) {
|
||||
TestWidevineCas cas_api;
|
||||
EXPECT_CALL(*cas_api.crypto_session_, initialize())
|
||||
.WillOnce(Return(wvcas::CasStatus::OkStatus()));
|
||||
EXPECT_EQ(cas_api.initialize(nullptr).status_code(),
|
||||
wvcas::CasStatusCode::kNoError);
|
||||
EXPECT_CALL(*cas_api.license_, IsExpired()).WillRepeatedly(Return(false));
|
||||
|
||||
// Initialize media_id_.
|
||||
EXPECT_CALL(*cas_api.license_, GenerateEntitlementRequest)
|
||||
.WillOnce(Return(wvcas::CasStatus::OkStatus()));
|
||||
std::string request, init_data, license_id;
|
||||
EXPECT_EQ(cas_api.generateEntitlementRequest(init_data, &request, license_id)
|
||||
.status_code(),
|
||||
wvcas::CasStatusCode::kNoError);
|
||||
|
||||
std::string license_group_id = "license_group_id";
|
||||
std::vector<std::string> content_list = {"content1", "content2"};
|
||||
EXPECT_CALL(*cas_api.license_, HandleEntitlementResponse(_, _, NotNull()))
|
||||
.WillOnce(DoAll(SetArgPointee<2>("device_file"),
|
||||
Return(wvcas::CasStatus::OkStatus())));
|
||||
EXPECT_CALL(*cas_api.license_, IsMultiContentLicense)
|
||||
.WillRepeatedly(Return(true));
|
||||
EXPECT_CALL(*cas_api.license_, IsGroupLicense).WillRepeatedly(Return(false));
|
||||
EXPECT_CALL(*cas_api.license_, GetGroupId)
|
||||
.WillRepeatedly(Return(license_group_id));
|
||||
EXPECT_CALL(*cas_api.license_, GetContentIdList)
|
||||
.WillRepeatedly(Return(content_list));
|
||||
|
||||
std::string multi_content_license_info;
|
||||
std::string group_license_info;
|
||||
EXPECT_EQ(cas_api
|
||||
.handleEntitlementResponse("response", license_id,
|
||||
multi_content_license_info,
|
||||
group_license_info)
|
||||
.status_code(),
|
||||
wvcas::CasStatusCode::kNoError);
|
||||
|
||||
std::string expected_license_id =
|
||||
GenerateTestLicenseFileName(license_group_id);
|
||||
expected_license_id = expected_license_id.substr(
|
||||
0, expected_license_id.size() - strlen(kLicenseFileNameSuffix));
|
||||
EXPECT_EQ(license_id, expected_license_id);
|
||||
|
||||
std::string expected_info;
|
||||
expected_info.push_back(0);
|
||||
expected_info.push_back(0);
|
||||
expected_info.push_back(license_id.size());
|
||||
expected_info.append(license_id);
|
||||
expected_info.push_back(1);
|
||||
expected_info.push_back(0);
|
||||
expected_info.push_back(content_list[0].size());
|
||||
expected_info.append(content_list[0]);
|
||||
expected_info.push_back(1);
|
||||
expected_info.push_back(0);
|
||||
expected_info.push_back(content_list[1].size());
|
||||
expected_info.append(content_list[1]);
|
||||
EXPECT_EQ(multi_content_license_info, expected_info);
|
||||
EXPECT_TRUE(group_license_info.empty());
|
||||
}
|
||||
|
||||
TEST_F(WidevineCasTest, handleGroupEntitlementResponse) {
|
||||
TestWidevineCas cas_api;
|
||||
EXPECT_CALL(*cas_api.crypto_session_, initialize())
|
||||
.WillOnce(Return(wvcas::CasStatus::OkStatus()));
|
||||
EXPECT_EQ(cas_api.initialize(nullptr).status_code(),
|
||||
wvcas::CasStatusCode::kNoError);
|
||||
EXPECT_CALL(*cas_api.license_, IsExpired()).WillRepeatedly(Return(false));
|
||||
|
||||
// Initialize media_id_.
|
||||
EXPECT_CALL(*cas_api.license_, GenerateEntitlementRequest)
|
||||
.WillOnce(Return(wvcas::CasStatus::OkStatus()));
|
||||
std::string request, init_data, license_id;
|
||||
EXPECT_EQ(cas_api.generateEntitlementRequest(init_data, &request, license_id)
|
||||
.status_code(),
|
||||
wvcas::CasStatusCode::kNoError);
|
||||
|
||||
std::string license_group_id = "license_group_id";
|
||||
std::vector<std::string> content_list = {};
|
||||
EXPECT_CALL(*cas_api.license_, HandleEntitlementResponse(_, _, NotNull()))
|
||||
.WillOnce(DoAll(SetArgPointee<2>("device_file"),
|
||||
Return(wvcas::CasStatus::OkStatus())));
|
||||
EXPECT_CALL(*cas_api.license_, IsMultiContentLicense)
|
||||
.WillRepeatedly(Return(false));
|
||||
EXPECT_CALL(*cas_api.license_, IsGroupLicense).WillRepeatedly(Return(true));
|
||||
EXPECT_CALL(*cas_api.license_, GetGroupId)
|
||||
.WillRepeatedly(Return(license_group_id));
|
||||
EXPECT_CALL(*cas_api.license_, GetContentIdList)
|
||||
.WillRepeatedly(Return(content_list));
|
||||
|
||||
std::string multi_content_license_info;
|
||||
std::string group_license_info;
|
||||
EXPECT_EQ(cas_api
|
||||
.handleEntitlementResponse("response", license_id,
|
||||
multi_content_license_info,
|
||||
group_license_info)
|
||||
.status_code(),
|
||||
wvcas::CasStatusCode::kNoError);
|
||||
|
||||
std::string expected_license_id =
|
||||
GenerateTestLicenseFileName(license_group_id);
|
||||
expected_license_id = expected_license_id.substr(
|
||||
0, expected_license_id.size() - strlen(kLicenseFileNameSuffix));
|
||||
EXPECT_EQ(license_id, expected_license_id);
|
||||
|
||||
std::string expected_info;
|
||||
expected_info.push_back(0);
|
||||
expected_info.push_back(0);
|
||||
expected_info.push_back(license_id.size());
|
||||
expected_info.append(license_id);
|
||||
expected_info.push_back(1);
|
||||
expected_info.push_back(0);
|
||||
expected_info.push_back(license_group_id.size());
|
||||
expected_info.append(license_group_id);
|
||||
EXPECT_EQ(group_license_info, expected_info);
|
||||
EXPECT_TRUE(multi_content_license_info.empty());
|
||||
}
|
||||
|
||||
TEST_F(WidevineCasTest, ECMProcessingWithGroupId) {
|
||||
TestWidevineCas cas_api;
|
||||
EXPECT_CALL(*cas_api.crypto_session_, initialize())
|
||||
.WillOnce(Return(wvcas::CasStatus::OkStatus()));
|
||||
EXPECT_EQ(cas_api.initialize(nullptr).status_code(),
|
||||
wvcas::CasStatusCode::kNoError);
|
||||
EXPECT_CALL(*cas_api.license_, IsExpired()).WillRepeatedly(Return(false));
|
||||
|
||||
// Initialize media_id_.
|
||||
EXPECT_CALL(*cas_api.license_, GenerateEntitlementRequest)
|
||||
.WillOnce(Return(wvcas::CasStatus::OkStatus()));
|
||||
std::string request, init_data, license_id;
|
||||
EXPECT_EQ(cas_api.generateEntitlementRequest(init_data, &request, license_id)
|
||||
.status_code(),
|
||||
wvcas::CasStatusCode::kNoError);
|
||||
// Install license
|
||||
const std::string license_group_id = "license_group_id";
|
||||
EXPECT_CALL(*cas_api.license_, HandleEntitlementResponse)
|
||||
.WillOnce(Return(wvcas::CasStatus::OkStatus()));
|
||||
EXPECT_CALL(*cas_api.license_, IsMultiContentLicense)
|
||||
.WillRepeatedly(Return(false));
|
||||
EXPECT_CALL(*cas_api.license_, IsGroupLicense).WillRepeatedly(Return(true));
|
||||
EXPECT_CALL(*cas_api.license_, GetGroupId)
|
||||
.WillRepeatedly(Return(license_group_id));
|
||||
std::string multi_content_license_info;
|
||||
std::string group_license_info;
|
||||
EXPECT_EQ(cas_api
|
||||
.handleEntitlementResponse("response", license_id,
|
||||
multi_content_license_info,
|
||||
group_license_info)
|
||||
.status_code(),
|
||||
wvcas::CasStatusCode::kNoError);
|
||||
// Init a session
|
||||
wvcas::WvCasSessionId sid;
|
||||
EXPECT_CALL(*(cas_api.crypto_session_), CreateEntitledKeySession(_))
|
||||
.WillOnce(
|
||||
DoAll(SetArgPointee<0>(10), Return(wvcas::CasStatus::OkStatus())));
|
||||
EXPECT_EQ(wvcas::CasStatusCode::kNoError,
|
||||
cas_api.RemoveLicense(full_filename).status_code());
|
||||
cas_api.openSession(&sid).status_code());
|
||||
wvcas::CasSessionPtr session =
|
||||
wvcas::WidevineCasSessionMap::instance().GetSession(sid);
|
||||
ASSERT_TRUE(session != nullptr);
|
||||
const wvcas::CasEcm ecm = {1, 2, 3};
|
||||
// It is expected that process ecm with group_id
|
||||
EXPECT_CALL(*reinterpret_cast<MockWidevineSession*>(session.get()),
|
||||
processEcm(ecm, 0, license_group_id))
|
||||
.Times(1);
|
||||
EXPECT_CALL(*cas_api.license_, BeginDecryption());
|
||||
|
||||
EXPECT_EQ(cas_api.processEcm(sid, ecm).status_code(),
|
||||
wvcas::CasStatusCode::kNoError);
|
||||
EXPECT_CALL(*(cas_api.crypto_session_), RemoveEntitledKeySession(sid));
|
||||
}
|
||||
@@ -40,6 +40,7 @@ static const char kOddWrappedKeyIv[] = "odd_wrapped_content_key_iv";
|
||||
static const char kEvenContentIv[] = "even_content_iv";
|
||||
static const char kOddContentIv[] = "odd_content_iv";
|
||||
static const OEMCrypto_SESSION kEntitledKeySessionId = 0x1111;
|
||||
constexpr char kEmptyGroupId[] = "";
|
||||
|
||||
MATCHER(IsValidKeyEvenSlotData, "") {
|
||||
if (nullptr == arg) {
|
||||
@@ -125,6 +126,7 @@ class MockEcmParser : public wvcas::EcmParser {
|
||||
std::vector<uint8_t>(wvcas::KeySlotId id));
|
||||
MOCK_CONST_METHOD1(wrapped_key_iv, std::vector<uint8_t>(wvcas::KeySlotId id));
|
||||
MOCK_CONST_METHOD1(content_iv, std::vector<uint8_t>(wvcas::KeySlotId id));
|
||||
MOCK_METHOD(bool, set_group_id, (const std::string& group_id), (override));
|
||||
MOCK_CONST_METHOD0(has_fingerprinting, bool());
|
||||
MOCK_CONST_METHOD0(fingerprinting, video_widevine::Fingerprinting());
|
||||
MOCK_CONST_METHOD0(has_service_blocking, bool());
|
||||
@@ -145,7 +147,7 @@ class TestCasSession : public wvcas::WidevineCasSession {
|
||||
TestCasSession() {}
|
||||
virtual ~TestCasSession() {}
|
||||
|
||||
std::unique_ptr<const wvcas::EcmParser> getEcmParser(
|
||||
std::unique_ptr<wvcas::EcmParser> getEcmParser(
|
||||
const wvcas::CasEcm& ecm) const override;
|
||||
|
||||
std::vector<uint8_t> entitlement_key_id(wvcas::KeySlotId id) const {
|
||||
@@ -222,7 +224,7 @@ class TestCasSession : public wvcas::WidevineCasSession {
|
||||
video_widevine::ServiceBlocking service_blocking_;
|
||||
};
|
||||
|
||||
std::unique_ptr<const wvcas::EcmParser> TestCasSession::getEcmParser(
|
||||
std::unique_ptr<wvcas::EcmParser> TestCasSession::getEcmParser(
|
||||
const wvcas::CasEcm& ecm) const {
|
||||
std::unique_ptr<NiceMock<MockEcmParser>> mock_ecm_parser(
|
||||
new NiceMock<MockEcmParser>);
|
||||
@@ -241,6 +243,7 @@ std::unique_ptr<const wvcas::EcmParser> TestCasSession::getEcmParser(
|
||||
.WillByDefault(Invoke(this, &TestCasSession::wrapped_key_iv));
|
||||
ON_CALL(*mock_ecm_parser, content_iv(_))
|
||||
.WillByDefault(Invoke(this, &TestCasSession::content_iv));
|
||||
ON_CALL(*mock_ecm_parser, set_group_id(_)).WillByDefault(Return(true));
|
||||
ON_CALL(*mock_ecm_parser, has_fingerprinting())
|
||||
.WillByDefault(Return(fingerprinting_.has_control()));
|
||||
ON_CALL(*mock_ecm_parser, fingerprinting())
|
||||
@@ -249,7 +252,7 @@ std::unique_ptr<const wvcas::EcmParser> TestCasSession::getEcmParser(
|
||||
.WillByDefault(Return(service_blocking_.device_groups_size() > 0));
|
||||
ON_CALL(*mock_ecm_parser, service_blocking())
|
||||
.WillByDefault(Return(service_blocking_));
|
||||
return std::unique_ptr<const wvcas::EcmParser>(mock_ecm_parser.release());
|
||||
return std::unique_ptr<wvcas::EcmParser>(mock_ecm_parser.release());
|
||||
}
|
||||
|
||||
TEST_F(CasSessionTest, processEcm) {
|
||||
@@ -269,7 +272,7 @@ TEST_F(CasSessionTest, processEcm) {
|
||||
wvcas::CasEcm ecm(184);
|
||||
EXPECT_CALL(*mock, LoadCasECMKeys(session_id, IsValidKeyEvenSlotData(),
|
||||
IsValidKeyOddSlotData()));
|
||||
session.processEcm(ecm, 0);
|
||||
session.processEcm(ecm, 0, kEmptyGroupId);
|
||||
EXPECT_CALL(*mock, RemoveEntitledKeySession(session_id));
|
||||
}
|
||||
|
||||
@@ -293,25 +296,25 @@ TEST_F(CasSessionTest, parentalControl) {
|
||||
// Different Ecm to make sure processEcm() processes this ecm.
|
||||
std::generate(ecm.begin(), ecm.end(), std::rand);
|
||||
ASSERT_EQ(wvcas::CasStatusCode::kNoError,
|
||||
session.processEcm(ecm, 0).status_code());
|
||||
session.processEcm(ecm, 0, kEmptyGroupId).status_code());
|
||||
std::generate(ecm.begin(), ecm.end(), std::rand);
|
||||
ASSERT_EQ(wvcas::CasStatusCode::kNoError,
|
||||
session.processEcm(ecm, 13).status_code());
|
||||
session.processEcm(ecm, 13, kEmptyGroupId).status_code());
|
||||
|
||||
// Parental control age must >= 10 (if non-zero).
|
||||
session.set_age_restriction(10);
|
||||
std::generate(ecm.begin(), ecm.end(), std::rand);
|
||||
ASSERT_EQ(wvcas::CasStatusCode::kNoError,
|
||||
session.processEcm(ecm, 0).status_code());
|
||||
session.processEcm(ecm, 0, kEmptyGroupId).status_code());
|
||||
std::generate(ecm.begin(), ecm.end(), std::rand);
|
||||
ASSERT_EQ(wvcas::CasStatusCode::kNoError,
|
||||
session.processEcm(ecm, 10).status_code());
|
||||
session.processEcm(ecm, 10, kEmptyGroupId).status_code());
|
||||
std::generate(ecm.begin(), ecm.end(), std::rand);
|
||||
ASSERT_EQ(wvcas::CasStatusCode::kNoError,
|
||||
session.processEcm(ecm, 13).status_code());
|
||||
session.processEcm(ecm, 13, kEmptyGroupId).status_code());
|
||||
std::generate(ecm.begin(), ecm.end(), std::rand);
|
||||
ASSERT_EQ(wvcas::CasStatusCode::kAccessDeniedByParentalControl,
|
||||
session.processEcm(ecm, 3).status_code());
|
||||
session.processEcm(ecm, 3, kEmptyGroupId).status_code());
|
||||
EXPECT_CALL(*mock, RemoveEntitledKeySession(session_id));
|
||||
}
|
||||
|
||||
@@ -354,7 +357,7 @@ TEST_F(CasSessionTest, FingerprintingSuccess) {
|
||||
OnSessionFingerprintingUpdated(session_id, expected_message))
|
||||
.Times(1);
|
||||
|
||||
session.processEcm(wvcas::CasEcm(184, '0'), 0);
|
||||
session.processEcm(wvcas::CasEcm(184, '0'), 0, kEmptyGroupId);
|
||||
}
|
||||
|
||||
TEST_F(CasSessionTest, RepeatedFingerprintingNoEventSuccess) {
|
||||
@@ -368,10 +371,10 @@ TEST_F(CasSessionTest, RepeatedFingerprintingNoEventSuccess) {
|
||||
session.set_fingerprinting_control("control");
|
||||
EXPECT_CALL(mock_listener, OnSessionFingerprintingUpdated).Times(1);
|
||||
|
||||
session.processEcm(wvcas::CasEcm(184, '0'), 0);
|
||||
session.processEcm(wvcas::CasEcm(184, '0'), 0, kEmptyGroupId);
|
||||
// Same fingerprinting will not trigger event.
|
||||
EXPECT_CALL(mock_listener, OnSessionFingerprintingUpdated).Times(0);
|
||||
session.processEcm(wvcas::CasEcm(184, '1'), 0);
|
||||
session.processEcm(wvcas::CasEcm(184, '1'), 0, kEmptyGroupId);
|
||||
}
|
||||
|
||||
TEST_F(CasSessionTest, DifferentFingerprintingTriggerEventSuccess) {
|
||||
@@ -385,15 +388,15 @@ TEST_F(CasSessionTest, DifferentFingerprintingTriggerEventSuccess) {
|
||||
session.set_fingerprinting_control("control");
|
||||
EXPECT_CALL(mock_listener, OnSessionFingerprintingUpdated).Times(1);
|
||||
|
||||
session.processEcm(wvcas::CasEcm(184, '0'), 0);
|
||||
session.processEcm(wvcas::CasEcm(184, '0'), 0, kEmptyGroupId);
|
||||
// Different fingerprinting will trigger event.
|
||||
session.set_fingerprinting_control("control2");
|
||||
EXPECT_CALL(mock_listener, OnSessionFingerprintingUpdated).Times(1);
|
||||
session.processEcm(wvcas::CasEcm(184, '1'), 0);
|
||||
session.processEcm(wvcas::CasEcm(184, '1'), 0, kEmptyGroupId);
|
||||
// Different fingerprinting (including empty) will trigger event.
|
||||
session.set_fingerprinting_control("");
|
||||
EXPECT_CALL(mock_listener, OnSessionFingerprintingUpdated).Times(1);
|
||||
session.processEcm(wvcas::CasEcm(184, '2'), 0);
|
||||
session.processEcm(wvcas::CasEcm(184, '2'), 0, kEmptyGroupId);
|
||||
}
|
||||
|
||||
TEST_F(CasSessionTest, ServiceBlockingSuccess) {
|
||||
@@ -412,7 +415,7 @@ TEST_F(CasSessionTest, ServiceBlockingSuccess) {
|
||||
OnSessionServiceBlockingUpdated(session_id, expected_message))
|
||||
.Times(1);
|
||||
|
||||
session.processEcm(wvcas::CasEcm(184, '0'), 0);
|
||||
session.processEcm(wvcas::CasEcm(184, '0'), 0, kEmptyGroupId);
|
||||
}
|
||||
|
||||
TEST_F(CasSessionTest, RepeatedServiceBlockingNoEventSuccess) {
|
||||
@@ -426,9 +429,9 @@ TEST_F(CasSessionTest, RepeatedServiceBlockingNoEventSuccess) {
|
||||
session.set_service_blocking_groups({"Group1", "g2"});
|
||||
EXPECT_CALL(mock_listener, OnSessionServiceBlockingUpdated).Times(1);
|
||||
|
||||
session.processEcm(wvcas::CasEcm(184, '0'), 0);
|
||||
session.processEcm(wvcas::CasEcm(184, '0'), 0, kEmptyGroupId);
|
||||
EXPECT_CALL(mock_listener, OnSessionServiceBlockingUpdated).Times(0);
|
||||
session.processEcm(wvcas::CasEcm(184, '1'), 0);
|
||||
session.processEcm(wvcas::CasEcm(184, '1'), 0, kEmptyGroupId);
|
||||
}
|
||||
|
||||
TEST_F(CasSessionTest, DifferentServiceBlockingTriggerEventSuccess) {
|
||||
@@ -442,13 +445,13 @@ TEST_F(CasSessionTest, DifferentServiceBlockingTriggerEventSuccess) {
|
||||
session.set_service_blocking_groups({"Group1", "g2"});
|
||||
EXPECT_CALL(mock_listener, OnSessionServiceBlockingUpdated).Times(1);
|
||||
|
||||
session.processEcm(wvcas::CasEcm(184, '0'), 0);
|
||||
session.processEcm(wvcas::CasEcm(184, '0'), 0, kEmptyGroupId);
|
||||
EXPECT_CALL(mock_listener, OnSessionServiceBlockingUpdated).Times(1);
|
||||
session.set_service_blocking_groups({"Group1"});
|
||||
session.processEcm(wvcas::CasEcm(184, '1'), 0);
|
||||
session.processEcm(wvcas::CasEcm(184, '1'), 0, kEmptyGroupId);
|
||||
EXPECT_CALL(mock_listener, OnSessionServiceBlockingUpdated).Times(1);
|
||||
session.set_service_blocking_groups({});
|
||||
session.processEcm(wvcas::CasEcm(184, '2'), 0);
|
||||
session.processEcm(wvcas::CasEcm(184, '2'), 0, kEmptyGroupId);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
213
tests/src/widevine_media_cas_plugin_test.cpp
Normal file
213
tests/src/widevine_media_cas_plugin_test.cpp
Normal file
@@ -0,0 +1,213 @@
|
||||
// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine Master
|
||||
// License Agreement.
|
||||
|
||||
#include "widevine_media_cas_plugin.h"
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <utils/String8.h>
|
||||
|
||||
#include "cas_events.h"
|
||||
#include "cas_status.h"
|
||||
#include "media/cas/CasAPI.h"
|
||||
#include "media/stagefright/MediaErrors.h"
|
||||
#include "widevine_cas_api.h"
|
||||
|
||||
namespace android {
|
||||
// Minimalist implementation of Android string class to support test.
|
||||
std::map<const String8*, std::unique_ptr<std::string> > string8s;
|
||||
|
||||
String8::String8(const String8& value)
|
||||
: String8(value.c_str(), value.length()) {}
|
||||
|
||||
String8::String8(char const* data, size_t data_length) {
|
||||
auto result =
|
||||
string8s.emplace(this, make_unique<std::string>(data, data_length));
|
||||
mString = result.first->second->data();
|
||||
}
|
||||
|
||||
size_t String8::length() const {
|
||||
auto it = string8s.find(this);
|
||||
return it == string8s.end() ? 0 : it->second->size();
|
||||
}
|
||||
|
||||
String8::~String8() { string8s.erase(this); }
|
||||
|
||||
} // namespace android
|
||||
|
||||
namespace wvcas {
|
||||
namespace {
|
||||
|
||||
using ::testing::_;
|
||||
using ::testing::DoAll;
|
||||
using ::testing::Eq;
|
||||
using ::testing::IsNull;
|
||||
using ::testing::NotNull;
|
||||
using ::testing::Return;
|
||||
using ::testing::SetArgPointee;
|
||||
using ::testing::SetArgReferee;
|
||||
|
||||
class MockWidevineCas : public WidevineCas {
|
||||
public:
|
||||
MockWidevineCas() {}
|
||||
~MockWidevineCas() override {}
|
||||
|
||||
MOCK_METHOD(CasStatus, openSession, (WvCasSessionId * sessionId), (override));
|
||||
MOCK_METHOD(bool, is_provisioned, (), (const, override));
|
||||
MOCK_METHOD(CasStatus, generateEntitlementRequest,
|
||||
(const std::string& init_data, std::string* entitlement_request,
|
||||
std::string& license_id),
|
||||
(override));
|
||||
MOCK_METHOD(CasStatus, RecordLicenseId, (const std::string& license_id),
|
||||
(override));
|
||||
MOCK_METHOD(CasStatus, handleEntitlementResponse,
|
||||
(const std::string& response, std::string& license_id,
|
||||
std::string& multi_content_license_info,
|
||||
std::string& group_license_info),
|
||||
(override));
|
||||
};
|
||||
|
||||
// Override WidevineCasPlugin to set WidevineCas and mock callbacks.
|
||||
class TestWidevineCasPlugin : public WidevineCasPlugin {
|
||||
public:
|
||||
TestWidevineCasPlugin() : WidevineCasPlugin() {}
|
||||
~TestWidevineCasPlugin() override {}
|
||||
|
||||
void SetWidevineCasApi(
|
||||
std::unique_ptr<WidevineCas> widevine_cas_api) override {
|
||||
WidevineCasPlugin::SetWidevineCasApi(std::move(widevine_cas_api));
|
||||
}
|
||||
|
||||
MOCK_METHOD(void, CallBack,
|
||||
(void* appData, int32_t event, int32_t arg, uint8_t* data,
|
||||
size_t size, const CasSessionId* sessionId),
|
||||
(const, override));
|
||||
};
|
||||
|
||||
TEST(WidevineCasPluginTest, openSessionSuccess) {
|
||||
TestWidevineCasPlugin plugin;
|
||||
auto pass_through_cas_api = make_unique<MockWidevineCas>();
|
||||
MockWidevineCas* cas_api = pass_through_cas_api.get();
|
||||
plugin.SetWidevineCasApi(std::move(pass_through_cas_api));
|
||||
const int32_t created_session_id = 0x12345678;
|
||||
const std::vector<uint8_t> expected_android_session_id = {0x78, 0x56, 0x34,
|
||||
0x12};
|
||||
EXPECT_CALL(*cas_api, is_provisioned).WillOnce(Return(true));
|
||||
EXPECT_CALL(*cas_api, openSession(NotNull()))
|
||||
.WillOnce(DoAll(SetArgPointee<0>(created_session_id),
|
||||
Return(CasStatus::OkStatus())));
|
||||
EXPECT_CALL(plugin, CallBack(_, CAS_SESSION_ID, created_session_id, IsNull(),
|
||||
0, IsNull()));
|
||||
std::vector<uint8_t> session_id;
|
||||
|
||||
EXPECT_EQ(plugin.openSession(&session_id), android::OK);
|
||||
|
||||
EXPECT_EQ(session_id, expected_android_session_id);
|
||||
}
|
||||
|
||||
TEST(WidevineCasPluginTest, openSessionWithoutProvisionFail) {
|
||||
TestWidevineCasPlugin plugin;
|
||||
auto pass_through_cas_api = make_unique<MockWidevineCas>();
|
||||
MockWidevineCas* cas_api = pass_through_cas_api.get();
|
||||
plugin.SetWidevineCasApi(std::move(pass_through_cas_api));
|
||||
EXPECT_CALL(*cas_api, is_provisioned).WillOnce(Return(false));
|
||||
EXPECT_CALL(*cas_api, openSession(NotNull())).Times((0));
|
||||
std::vector<uint8_t> session_id;
|
||||
|
||||
EXPECT_EQ(plugin.openSession(&session_id),
|
||||
android::ERROR_CAS_NOT_PROVISIONED);
|
||||
}
|
||||
|
||||
TEST(WidevineCasPluginTest,
|
||||
provisionWithProvisionStringAlreadyProvisionedSuccess) {
|
||||
TestWidevineCasPlugin plugin;
|
||||
auto pass_through_cas_api = make_unique<MockWidevineCas>();
|
||||
MockWidevineCas* cas_api = pass_through_cas_api.get();
|
||||
plugin.SetWidevineCasApi(std::move(pass_through_cas_api));
|
||||
const std::string provision_string = "init_data";
|
||||
EXPECT_CALL(*cas_api, is_provisioned).WillOnce(Return(true));
|
||||
EXPECT_CALL(plugin, CallBack(_, INDIVIDUALIZATION_COMPLETE, _, _, _, _));
|
||||
// Provision string is init data; it triggers license request.
|
||||
EXPECT_CALL(*cas_api,
|
||||
generateEntitlementRequest(Eq(provision_string), NotNull(), _))
|
||||
.WillOnce(DoAll(SetArgPointee<1>("signed_license_request"),
|
||||
Return(CasStatus::OkStatus())));
|
||||
EXPECT_CALL(plugin, CallBack(_, LICENSE_REQUEST, _, _, _, _));
|
||||
|
||||
const android::String8 provision_msg();
|
||||
|
||||
EXPECT_EQ(plugin.provision(android::String8(provision_string.c_str(),
|
||||
provision_string.size())),
|
||||
android::OK);
|
||||
}
|
||||
|
||||
TEST(WidevineCasPluginTest, HandleAssignLicenseIDSuccess) {
|
||||
TestWidevineCasPlugin plugin;
|
||||
auto pass_through_cas_api = make_unique<MockWidevineCas>();
|
||||
MockWidevineCas* cas_api = pass_through_cas_api.get();
|
||||
plugin.SetWidevineCasApi(std::move(pass_through_cas_api));
|
||||
const std::string license_id = "license_id";
|
||||
EXPECT_CALL(*cas_api, RecordLicenseId(license_id))
|
||||
.WillOnce(Return(CasStatus::OkStatus()));
|
||||
EXPECT_CALL(plugin, CallBack(_, LICENSE_ID_ASSIGNED, _, _, _, _)).Times(1);
|
||||
|
||||
EXPECT_EQ(plugin.sendEvent(ASSIGN_LICENSE_ID, /*arg=*/0,
|
||||
{license_id.begin(), license_id.end()}),
|
||||
android::OK);
|
||||
}
|
||||
|
||||
TEST(WidevineCasPluginTest, HandleAssignLicenseIDApiError) {
|
||||
TestWidevineCasPlugin plugin;
|
||||
auto pass_through_cas_api = make_unique<MockWidevineCas>();
|
||||
MockWidevineCas* cas_api = pass_through_cas_api.get();
|
||||
plugin.SetWidevineCasApi(std::move(pass_through_cas_api));
|
||||
const std::string license_id = "license_id";
|
||||
EXPECT_CALL(*cas_api, RecordLicenseId)
|
||||
.WillOnce(Return(CasStatus(CasStatusCode::kInvalidParameter, "invalid")));
|
||||
EXPECT_CALL(plugin, CallBack(_, CAS_ERROR, _, _, _, _)).Times(1);
|
||||
|
||||
EXPECT_NE(plugin.sendEvent(ASSIGN_LICENSE_ID, /*arg=*/0,
|
||||
{license_id.begin(), license_id.end()}),
|
||||
android::OK);
|
||||
}
|
||||
|
||||
TEST(WidevineCasPluginTest, HandleEntitlementResponseSuccess) {
|
||||
TestWidevineCasPlugin plugin;
|
||||
auto pass_through_cas_api = make_unique<MockWidevineCas>();
|
||||
MockWidevineCas* cas_api = pass_through_cas_api.get();
|
||||
plugin.SetWidevineCasApi(std::move(pass_through_cas_api));
|
||||
const std::string license = "license";
|
||||
const std::string license_id = "id";
|
||||
const std::string multi_content_license_info = "info";
|
||||
const std::string group_license_info = "info2";
|
||||
EXPECT_CALL(*cas_api, handleEntitlementResponse(_, _, _, _))
|
||||
.WillOnce(DoAll(SetArgReferee<1>(license_id),
|
||||
SetArgReferee<2>(multi_content_license_info),
|
||||
SetArgReferee<3>(group_license_info),
|
||||
Return(CasStatus::OkStatus())));
|
||||
EXPECT_CALL(plugin,
|
||||
CallBack(_, LICENSE_CAS_READY, _, _, license_id.size(), _))
|
||||
.Times(1);
|
||||
EXPECT_CALL(plugin, CallBack(_, MULTI_CONTENT_LICENSE_INFO, _, _,
|
||||
multi_content_license_info.size(), _))
|
||||
.Times(1);
|
||||
EXPECT_CALL(plugin, CallBack(_, GROUP_LICENSE_INFO, _, _,
|
||||
group_license_info.size(), _))
|
||||
.Times(1);
|
||||
|
||||
EXPECT_EQ(plugin.sendEvent(LICENSE_RESPONSE, /*arg=*/0,
|
||||
{license.begin(), license.end()}),
|
||||
android::OK);
|
||||
}
|
||||
|
||||
TEST(WidevineCasPluginTest, HandleEntitlementResponseEmptyResponseFail) {
|
||||
TestWidevineCasPlugin plugin;
|
||||
EXPECT_CALL(plugin, CallBack(_, CAS_ERROR, _, _, _, _)).Times(1);
|
||||
|
||||
EXPECT_NE(plugin.sendEvent(LICENSE_RESPONSE, /*arg=*/0, /*eventData=*/{}),
|
||||
android::OK);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace wvcas
|
||||
Reference in New Issue
Block a user