Use IV from Widevine key server if available

Fixes #555.

Change-Id: Ic077f97884a0c3d26159cd44791298bdd400068d
This commit is contained in:
KongQun Yang 2019-02-12 16:34:24 -08:00
parent a529d4677d
commit fcfc843a2e
2 changed files with 71 additions and 30 deletions

View File

@ -453,6 +453,7 @@ bool WidevineKeySource::ExtractEncryptionKey(
if (!widevine_classic) {
encryption_key->key_id.assign(track.key_id().begin(),
track.key_id().end());
encryption_key->iv.assign(track.iv().begin(), track.iv().end());
if (generate_widevine_protection_system_) {
if (track.pssh_size() != 1) {

View File

@ -34,6 +34,9 @@ using ::testing::WithParamInterface;
namespace shaka {
namespace media {
namespace {
const bool kClassic = true;
const bool kHasIv = true;
const char kServerUrl[] = "http://www.foo.com/getcontentkey";
const char kContentId[] = "ContentFoo";
const char kPolicy[] = "PolicyFoo";
@ -73,6 +76,9 @@ const char kExpectedSignedMessageFormat[] =
R"({"request":"%s","signature":"%s","signer":"%s"})";
const char kTrackFormat[] = R"({"type":"%s","key_id":"%s","key":"%s",)"
R"("pssh":[{"drm_type":"WIDEVINE","data":"%s"}]})";
const char kTrackFormatWithIv[] =
R"({"type":"%s","key_id":"%s","key":"%s","iv":"%s",)"
R"("pssh":[{"drm_type":"WIDEVINE","data":"%s"}]})";
const char kTrackFormatWithBoxes[] =
R"({"type":"%s","key_id":"%s","key":"%s",)"
R"("pssh":[{"drm_type":"WIDEVINE","data":"%s","boxes":"%s"}]})";
@ -119,6 +125,13 @@ std::string GetMockKey(const std::string& track_type) {
return key;
}
std::string GetMockIv(const std::string& track_type) {
// IV must be 16 characters.
std::string iv = "MockIv" + track_type;
iv.resize(16, '~');
return iv;
}
std::string GetMockPsshData() {
return kRequestPsshData;
}
@ -126,14 +139,14 @@ std::string GetMockPsshData() {
std::string GenerateMockLicenseResponseWithBoxes(const std::string& boxes) {
const std::string kTrackTypes[] = {"SD", "HD", "UHD1", "UHD2", "AUDIO"};
std::string tracks;
for (size_t i = 0; i < 5; ++i) {
for (const std::string& track_type : kTrackTypes) {
if (!tracks.empty())
tracks += ",";
tracks += base::StringPrintf(
kTrackFormatWithBoxes, kTrackTypes[i].c_str(),
Base64Encode(GetMockKeyId(kTrackTypes[i])).c_str(),
Base64Encode(GetMockKey(kTrackTypes[i])).c_str(),
Base64Encode(GetMockPsshData()).c_str(), boxes.c_str());
tracks += base::StringPrintf(kTrackFormatWithBoxes, track_type.c_str(),
Base64Encode(GetMockKeyId(track_type)).c_str(),
Base64Encode(GetMockKey(track_type)).c_str(),
Base64Encode(GetMockPsshData()).c_str(),
boxes.c_str());
}
return base::StringPrintf(kLicenseResponseFormat, "OK", tracks.c_str());
}
@ -141,14 +154,28 @@ std::string GenerateMockLicenseResponseWithBoxes(const std::string& boxes) {
std::string GenerateMockLicenseResponse() {
const std::string kTrackTypes[] = {"SD", "HD", "UHD1", "UHD2", "AUDIO"};
std::string tracks;
for (size_t i = 0; i < 5; ++i) {
for (const std::string& track_type : kTrackTypes) {
if (!tracks.empty())
tracks += ",";
tracks +=
base::StringPrintf(kTrackFormat, kTrackTypes[i].c_str(),
Base64Encode(GetMockKeyId(kTrackTypes[i])).c_str(),
Base64Encode(GetMockKey(kTrackTypes[i])).c_str(),
Base64Encode(GetMockPsshData()).c_str());
tracks += base::StringPrintf(kTrackFormat, track_type.c_str(),
Base64Encode(GetMockKeyId(track_type)).c_str(),
Base64Encode(GetMockKey(track_type)).c_str(),
Base64Encode(GetMockPsshData()).c_str());
}
return base::StringPrintf(kLicenseResponseFormat, "OK", tracks.c_str());
}
std::string GenerateMockLicenseResponseWithIv() {
const std::string kTrackTypes[] = {"SD", "HD", "UHD1", "UHD2", "AUDIO"};
std::string tracks;
for (const std::string& track_type : kTrackTypes) {
if (!tracks.empty())
tracks += ",";
tracks += base::StringPrintf(kTrackFormatWithIv, track_type.c_str(),
Base64Encode(GetMockKeyId(track_type)).c_str(),
Base64Encode(GetMockKey(track_type)).c_str(),
Base64Encode(GetMockIv(track_type)).c_str(),
Base64Encode(GetMockPsshData()).c_str());
}
return base::StringPrintf(kLicenseResponseFormat, "OK", tracks.c_str());
}
@ -156,13 +183,11 @@ std::string GenerateMockLicenseResponse() {
std::string GenerateMockClassicLicenseResponse() {
const std::string kTrackTypes[] = {"SD", "HD", "UHD1", "UHD2", "AUDIO"};
std::string tracks;
for (size_t i = 0; i < 5; ++i) {
for (const std::string& track_type : kTrackTypes) {
if (!tracks.empty())
tracks += ",";
tracks += base::StringPrintf(
kClassicTrackFormat,
kTrackTypes[i].c_str(),
Base64Encode(GetMockKey(kTrackTypes[i])).c_str());
tracks += base::StringPrintf(kClassicTrackFormat, track_type.c_str(),
Base64Encode(GetMockKey(track_type)).c_str());
}
return base::StringPrintf(kLicenseResponseFormat, "OK", tracks.c_str());
}
@ -237,7 +262,7 @@ class WidevineKeySourceTest : public Test {
widevine_key_source_->set_key_fetcher(std::move(mock_key_fetcher_));
}
void VerifyKeys(bool classic) {
void VerifyKeys(bool classic, bool has_iv) {
EncryptionKey encryption_key;
const std::string kStreamLabels[] = {"SD", "HD", "UHD1", "UHD2", "AUDIO"};
for (const std::string& stream_label : kStreamLabels) {
@ -248,6 +273,10 @@ class WidevineKeySourceTest : public Test {
(add_widevine_pssh_ && add_common_pssh_) ? 2 : 1;
ASSERT_EQ(num_key_system_info, encryption_key.key_system_info.size());
EXPECT_EQ(GetMockKeyId(stream_label), ToString(encryption_key.key_id));
if (has_iv)
EXPECT_EQ(GetMockIv(stream_label), ToString(encryption_key.iv));
else
EXPECT_TRUE(encryption_key.iv.empty());
auto key_system_info_iter = encryption_key.key_system_info.begin();
@ -307,7 +336,7 @@ TEST_F(WidevineKeySourceTest, RetryOnHttpTimeout) {
CreateWidevineKeySource();
ASSERT_OK(widevine_key_source_->FetchKeys(content_id_, kPolicy));
VerifyKeys(false);
VerifyKeys(!kClassic, !kHasIv);
}
TEST_F(WidevineKeySourceTest, RetryOnTransientError) {
@ -327,7 +356,7 @@ TEST_F(WidevineKeySourceTest, RetryOnTransientError) {
CreateWidevineKeySource();
ASSERT_OK(widevine_key_source_->FetchKeys(content_id_, kPolicy));
VerifyKeys(false);
VerifyKeys(!kClassic, !kHasIv);
}
TEST_F(WidevineKeySourceTest, NoRetryOnUnknownError) {
@ -344,6 +373,19 @@ TEST_F(WidevineKeySourceTest, NoRetryOnUnknownError) {
widevine_key_source_->FetchKeys(content_id_, kPolicy).error_code());
}
TEST_F(WidevineKeySourceTest, CheckIv) {
std::string mock_response = base::StringPrintf(
kHttpResponseFormat,
Base64Encode(GenerateMockLicenseResponseWithIv()).c_str());
EXPECT_CALL(*mock_key_fetcher_, FetchKeys(_, _, _))
.WillOnce(DoAll(SetArgPointee<2>(mock_response), Return(Status::OK)));
CreateWidevineKeySource();
ASSERT_OK(widevine_key_source_->FetchKeys(content_id_, kPolicy));
VerifyKeys(!kClassic, kHasIv);
}
TEST_F(WidevineKeySourceTest, BoxesInResponse) {
const char kMockBoxes[] = "mock_pssh_boxes";
std::string mock_response = base::StringPrintf(
@ -410,7 +452,7 @@ TEST_P(WidevineKeySourceParameterizedTest, LicenseStatusCencOK) {
CreateWidevineKeySource();
ASSERT_OK(widevine_key_source_->FetchKeys(content_id_, kPolicy));
VerifyKeys(false);
VerifyKeys(!kClassic, !kHasIv);
}
TEST_P(WidevineKeySourceParameterizedTest, LicenseStatusCencMalformedResponse) {
@ -444,7 +486,7 @@ TEST_P(WidevineKeySourceParameterizedTest, LicenseStatusCencWithPsshBoxOK) {
std::vector<uint8_t> pssh_box(std::begin(kRequestPsshBox),
std::end(kRequestPsshBox));
ASSERT_OK(widevine_key_source_->FetchKeys(EmeInitDataType::CENC, pssh_box));
VerifyKeys(false);
VerifyKeys(!kClassic, !kHasIv);
}
TEST_P(WidevineKeySourceParameterizedTest, LicenseStatusCencWithKeyIdsOK) {
@ -467,7 +509,7 @@ TEST_P(WidevineKeySourceParameterizedTest, LicenseStatusCencWithKeyIdsOK) {
std::vector<uint8_t> key_id(std::begin(kRequestKeyId),
std::end(kRequestKeyId));
ASSERT_OK(widevine_key_source_->FetchKeys(EmeInitDataType::WEBM, key_id));
VerifyKeys(false);
VerifyKeys(!kClassic, !kHasIv);
}
TEST_P(WidevineKeySourceParameterizedTest, LicenseStatusClassicOK) {
@ -489,7 +531,7 @@ TEST_P(WidevineKeySourceParameterizedTest, LicenseStatusClassicOK) {
EmeInitDataType::WIDEVINE_CLASSIC,
std::vector<uint8_t>(std::begin(kClassicAssetIdBytes),
std::end(kClassicAssetIdBytes))));
VerifyKeys(true);
VerifyKeys(kClassic, !kHasIv);
}
TEST_P(WidevineKeySourceParameterizedTest, VerifyEntitlementLicenseRequest) {
@ -539,15 +581,13 @@ std::string GenerateMockKeyRotationLicenseResponse(
for (uint32_t index = initial_crypto_period_index;
index < initial_crypto_period_index + crypto_period_count;
++index) {
for (size_t i = 0; i < 5; ++i) {
for (const std::string& track_type : kTrackTypes) {
if (!tracks.empty())
tracks += ",";
tracks += base::StringPrintf(
kCryptoPeriodTrackFormat,
kTrackTypes[i].c_str(),
Base64Encode(GetMockKeyId(kTrackTypes[i])).c_str(),
Base64Encode(GetMockKey(kTrackTypes[i], index)).c_str(),
index);
kCryptoPeriodTrackFormat, track_type.c_str(),
Base64Encode(GetMockKeyId(track_type)).c_str(),
Base64Encode(GetMockKey(track_type, index)).c_str(), index);
}
}
return base::StringPrintf(kLicenseResponseFormat, "OK", tracks.c_str());