Minor change
This commit is contained in:
@@ -874,12 +874,19 @@ Status EcmgClientHandler::BuildEcmDatagram(const EcmgParameters& params,
|
||||
DCHECK(stream_info->ecm);
|
||||
|
||||
// Generate serialized ECM.
|
||||
CryptoMode crypto_mode = stream_info->crypto_mode == CryptoMode::kInvalid
|
||||
? ecmg_config_->crypto_mode
|
||||
: stream_info->crypto_mode;
|
||||
std::vector<EntitledKeyInfo> keys;
|
||||
keys.reserve(ecmg_config_->number_of_content_keys);
|
||||
for (size_t i = 0; i < ecmg_config_->number_of_content_keys; i++) {
|
||||
DCHECK(params.cp_cw_combinations[i].cp == params.cp_number + i);
|
||||
keys.emplace_back();
|
||||
keys[i].key_value = params.cp_cw_combinations[i].cw;
|
||||
// Make content key to 16 bytes if crypto mode is Csa2.
|
||||
if (crypto_mode == CryptoMode::kDvbCsa2 && keys[i].key_value.size() == 8) {
|
||||
keys[i].key_value = keys[i].key_value + keys[i].key_value;
|
||||
}
|
||||
keys[i].key_id = crypto_util::DeriveKeyId(keys[i].key_value);
|
||||
keys[i].content_iv = stream_info->content_ivs.empty()
|
||||
? content_ivs_[i]
|
||||
|
||||
@@ -39,7 +39,9 @@ static constexpr size_t kEcmId = 2;
|
||||
static constexpr size_t kNominalCpDuration = 0x64;
|
||||
static constexpr size_t kCpNumber = 0;
|
||||
static constexpr char kContentKeyEven[] = "0123456701234567";
|
||||
static constexpr char kContentKeyEven8Bytes[] = "01234567";
|
||||
static constexpr char kContentKeyOdd[] = "abcdefghabcdefgh";
|
||||
static constexpr char kContentKeyOdd8Bytes[] = "abcdefgh";
|
||||
static constexpr char kEntitlementKeyIdEven[] = "0123456701234567";
|
||||
static constexpr char kEntitlementKeyValueEven[] =
|
||||
"01234567012345670123456701234567";
|
||||
@@ -48,6 +50,7 @@ static constexpr char kEntitlementKeyValueOdd[] =
|
||||
"abcdefghabcdefghabcdefghabcdefgh";
|
||||
static constexpr size_t kAgeRestriction = 3;
|
||||
static constexpr char kCryptoMode[] = "AesScte";
|
||||
static constexpr char kCryptoModeCsa2[] = "DvbCsa2";
|
||||
static constexpr char kTrackTypesSD[] = "SD";
|
||||
static constexpr char kTrackTypesHD[] = "HD";
|
||||
|
||||
@@ -78,14 +81,16 @@ class EcmgClientHandlerTest : public ::testing::Test {
|
||||
}
|
||||
|
||||
void SetupValidChannel() {
|
||||
handler_->HandleRequest(kTestEcmgChannelSetup, response_, &response_len_);
|
||||
handler_->HandleRequest(kTestEcmgChannelSetupWithPrivateParameters,
|
||||
response_, &response_len_);
|
||||
EXPECT_EQ(sizeof(kTestEcmgChannelStatus), response_len_);
|
||||
EXPECT_EQ(0, memcmp(kTestEcmgChannelStatus, response_, response_len_));
|
||||
}
|
||||
|
||||
void SetupValidChannelStream() {
|
||||
SetupValidChannel();
|
||||
handler_->HandleRequest(kTestEcmgStreamSetup, response_, &response_len_);
|
||||
handler_->HandleRequest(kTestEcmgStreamSetupWithPrivateParameters,
|
||||
response_, &response_len_);
|
||||
EXPECT_EQ(sizeof(kTestEcmgStreamStatus), response_len_);
|
||||
EXPECT_EQ(0, memcmp(kTestEcmgStreamStatus, response_, response_len_));
|
||||
}
|
||||
@@ -320,7 +325,33 @@ TEST_F(EcmgClientHandlerTest, SuccessSequenceInjectedEntitlements) {
|
||||
handler_->HandleRequest(request_, response_, &response_len_);
|
||||
EXPECT_EQ(sizeof(kTestEcmgEcmResponse), response_len_);
|
||||
}
|
||||
//
|
||||
|
||||
TEST_F(EcmgClientHandlerTest, SuccessSequenceCsa2) {
|
||||
BuildChannelSetupRequest(kChannelId, kSuperCasId, kAgeRestriction,
|
||||
kCryptoModeCsa2, request_, &request_len_);
|
||||
handler_->HandleRequest(request_, response_, &response_len_);
|
||||
EXPECT_EQ(sizeof(kTestEcmgChannelStatus), response_len_);
|
||||
EXPECT_EQ(0, memcmp(kTestEcmgChannelStatus, response_, response_len_));
|
||||
|
||||
BuildStreamSetupRequest(
|
||||
kChannelId, kStreamId, kEcmId, kNominalCpDuration, kTrackTypesSD,
|
||||
{absl::StrCat(kEntitlementKeyIdEven, kEntitlementKeyValueEven),
|
||||
absl::StrCat(kEntitlementKeyIdOdd, kEntitlementKeyValueOdd)},
|
||||
{kContentKeyEven, kContentKeyEven}, request_, &request_len_);
|
||||
handler_->HandleRequest(request_, response_, &response_len_);
|
||||
EXPECT_EQ(sizeof(kTestEcmgStreamStatus), response_len_);
|
||||
EXPECT_EQ(0, memcmp(kTestEcmgStreamStatus, response_, response_len_));
|
||||
|
||||
const std::vector<EcmgCpCwCombination> cp_cw_combination = {
|
||||
{kCpNumber, kContentKeyEven8Bytes},
|
||||
{kCpNumber + 1, kContentKeyOdd8Bytes}};
|
||||
BuildCwProvisionRequest(kChannelId, kStreamId, kCpNumber, cp_cw_combination,
|
||||
request_, &request_len_);
|
||||
handler_->HandleRequest(request_, response_, &response_len_);
|
||||
EXPECT_EQ(sizeof(kTestEcmgEcmResponse), response_len_);
|
||||
EXPECT_EQ(0, memcmp(kTestEcmgEcmResponse, response_, 27));
|
||||
}
|
||||
|
||||
TEST_F(EcmgClientHandlerTest, SuccessChannelError) {
|
||||
SetupValidChannel();
|
||||
handler_->HandleRequest(kTestEcmgChannelError, response_, &response_len_);
|
||||
@@ -408,6 +439,18 @@ TEST_F(EcmgClientHandlerTest, WrongParameterInsufficientKey) {
|
||||
response_len_);
|
||||
}
|
||||
|
||||
TEST_F(EcmgClientHandlerTest, WrongContentKeySize) {
|
||||
SetupValidChannelStream();
|
||||
// Content keys should be 16 byes.
|
||||
const std::vector<EcmgCpCwCombination> cp_cw_combination = {
|
||||
{kCpNumber, kContentKeyEven8Bytes},
|
||||
{kCpNumber + 1, kContentKeyOdd8Bytes}};
|
||||
BuildCwProvisionRequest(kChannelId, kStreamId, kCpNumber, cp_cw_combination,
|
||||
request_, &request_len_);
|
||||
handler_->HandleRequest(request_, response_, &response_len_);
|
||||
CheckStreamError(INVALID_VALUE_FOR_DVB_PARAMETER, response_, response_len_);
|
||||
}
|
||||
|
||||
TEST_F(EcmgClientHandlerTest, WrongParameterSuperCasId) {
|
||||
// Setup a channel with an unexpected super cas id (expecting kSuperCasId).
|
||||
BuildChannelSetupRequest(kChannelId, 0, kAgeRestriction, kCryptoMode,
|
||||
|
||||
@@ -60,8 +60,20 @@ Status WvCasEcm::GenerateEcm(const WvCasContentKeyInfo& even_key,
|
||||
LOG(ERROR) << "Failed to get initialize ECM class." << status;
|
||||
return status;
|
||||
}
|
||||
|
||||
EntitledKeyInfo entitled_even_key = ConvertToEntitledKeyInfo(even_key);
|
||||
EntitledKeyInfo entitled_odd_key = ConvertToEntitledKeyInfo(odd_key);
|
||||
// Make content key to 16 bytes if crypto mode is Csa2.
|
||||
if (ecm_param_.crypto_mode == CryptoMode::kDvbCsa2) {
|
||||
if (entitled_even_key.key_value.size() == 8) {
|
||||
entitled_even_key.key_value =
|
||||
entitled_even_key.key_value + entitled_even_key.key_value;
|
||||
}
|
||||
if (entitled_odd_key.key_value.size() == 8) {
|
||||
entitled_odd_key.key_value =
|
||||
entitled_odd_key.key_value + entitled_odd_key.key_value;
|
||||
}
|
||||
}
|
||||
return cas_ecm->GenerateEcm(&entitled_even_key, &entitled_odd_key, track_type,
|
||||
serialized_ecm);
|
||||
}
|
||||
@@ -79,6 +91,12 @@ Status WvCasEcm::GenerateSingleKeyEcm(const WvCasContentKeyInfo& key,
|
||||
}
|
||||
|
||||
EntitledKeyInfo entitled_key = ConvertToEntitledKeyInfo(key);
|
||||
// Make content key to 16 bytes if crypto mode is Csa2.
|
||||
if (ecm_param_.crypto_mode == CryptoMode::kDvbCsa2) {
|
||||
if (entitled_key.key_value.size() == 8) {
|
||||
entitled_key.key_value = entitled_key.key_value + entitled_key.key_value;
|
||||
}
|
||||
}
|
||||
return cas_ecm->GenerateSingleKeyEcm(&entitled_key, track_type,
|
||||
serialized_ecm);
|
||||
}
|
||||
|
||||
@@ -22,11 +22,12 @@ namespace cas {
|
||||
// Information needed to generate content key portion of ECM.
|
||||
// Fields:
|
||||
// |key_id| key ID for the content key, must be 16 bytes.
|
||||
// |key| content key value (aka control word), must be 16 bytes. It will be
|
||||
// wrapped (encrypted) by corresponding entitlement key (decided by track
|
||||
// type), together with |wrapped_key_iv| as the initial vector.
|
||||
// |content_iv| content initial vector, must be 8 or 16 bytes. It is used for
|
||||
// decrypting the content stream.
|
||||
// |key| content key value (aka control word), must be 8 (DvbCsa2) or 16 bytes
|
||||
// (all other crypto modes). It will be wrapped (encrypted) by
|
||||
// corresponding entitlement key (decided by track type), together with
|
||||
// |wrapped_key_iv| as the initial vector.
|
||||
// |content_iv| content initial vector, must be 8 (DvbCsa2) or 16 bytes (all
|
||||
// other crypto modes). It is used for decrypting the content stream.
|
||||
// |wrapped_key_iv| must be 16 bytes. It is used to encrypt |key|.
|
||||
struct WvCasContentKeyInfo {
|
||||
std::string key_id;
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace cas {
|
||||
|
||||
namespace {
|
||||
const char kEvenKey[] = "even_key........"; // 16 bytes
|
||||
const char kEvenKey8Bytes[] = "even_key"; // 8 bytes
|
||||
const char kEvenContentIv8Bytes[] = "even_iv."; // 8 bytes
|
||||
const char kEvenContentIv16Bytes[] = "even_iv.even_iv."; // 16 bytes
|
||||
const char kEvenEntitlementKeyId[] = "even_ent_key_id."; // 16 bytes
|
||||
@@ -34,6 +35,7 @@ const char kEvenEntitlementKey[] =
|
||||
const char kEvenEntitlementWrappedKeyIv[] = "1234567812345678"; // 16 bytes
|
||||
|
||||
const char kOddKey[] = "odd_key........."; // 16 bytes
|
||||
const char kOddKey8Bytes[] = "odd_key."; // 8 bytes
|
||||
const char kOddContentIv8Bytes[] = "odd_iv.."; // 8 bytes
|
||||
const char kOddContentIv16Bytes[] = "odd_iv..odd_iv.."; // 16 bytes
|
||||
const char kOddEntitlementKeyId[] = "odd_ent_key_id.."; // 16 bytes
|
||||
@@ -48,22 +50,24 @@ static constexpr size_t kSingleKeyEcmWith16BytesContentIvSizeBytes =
|
||||
kSingleKeyEcmWith8BytesContentIvSizeBytes + 8;
|
||||
} // namespace
|
||||
|
||||
class WvCasEcmTest
|
||||
: public ::testing::Test,
|
||||
public ::testing::WithParamInterface<::testing::tuple<
|
||||
int /*content_iv_size*/, bool /*key_rotation_enabled*/>> {
|
||||
class WvCasEcmTest : public ::testing::Test,
|
||||
public ::testing::WithParamInterface<::testing::tuple<
|
||||
int /*content_iv_size*/, bool /*key_rotation_enabled*/,
|
||||
std::string /*crypto_mode*/>> {
|
||||
protected:
|
||||
WvCasEcmTest() {
|
||||
content_iv_size_ = std::get<0>(GetParam());
|
||||
key_rotation_ = std::get<1>(GetParam());
|
||||
crypto_mode_ = std::get<2>(GetParam());
|
||||
}
|
||||
|
||||
WvCasEcmParameters CreateEcmInitParameters(bool key_rotation,
|
||||
int content_iv_size) {
|
||||
int content_iv_size,
|
||||
std::string crypto_mode) {
|
||||
WvCasEcmParameters params;
|
||||
params.content_iv_size = content_iv_size == 8 ? kIvSize8 : kIvSize16;
|
||||
params.key_rotation_enabled = key_rotation;
|
||||
params.crypto_mode = CryptoMode::kAesScte;
|
||||
EXPECT_TRUE(StringToCryptoMode(crypto_mode, ¶ms.crypto_mode));
|
||||
params.age_restriction = 0;
|
||||
return params;
|
||||
}
|
||||
@@ -95,7 +99,7 @@ class WvCasEcmTest
|
||||
content_keys.reserve(key_rotation ? 2 : 1);
|
||||
content_keys.emplace_back();
|
||||
WvCasContentKeyInfo* content_key = &content_keys.back();
|
||||
content_key->key = kEvenKey;
|
||||
content_key->key = crypto_mode_ == "DvbCsa2" ? kEvenKey8Bytes : kEvenKey;
|
||||
content_key->key_id = crypto_util::DeriveKeyId(content_key->key);
|
||||
content_key->content_iv =
|
||||
content_iv_size == 8 ? kEvenContentIv8Bytes : kEvenContentIv16Bytes;
|
||||
@@ -103,7 +107,7 @@ class WvCasEcmTest
|
||||
if (key_rotation) {
|
||||
content_keys.emplace_back();
|
||||
WvCasContentKeyInfo* content_key = &content_keys.back();
|
||||
content_key->key = kOddKey;
|
||||
content_key->key = crypto_mode_ == "DvbCsa2" ? kOddKey8Bytes : kOddKey;
|
||||
content_key->key_id = crypto_util::DeriveKeyId(content_key->key);
|
||||
content_key->content_iv =
|
||||
content_iv_size == 8 ? kOddContentIv8Bytes : kOddContentIv16Bytes;
|
||||
@@ -115,11 +119,12 @@ class WvCasEcmTest
|
||||
|
||||
int content_iv_size_;
|
||||
bool key_rotation_;
|
||||
std::string crypto_mode_;
|
||||
};
|
||||
|
||||
TEST_P(WvCasEcmTest, InitializeSuccess) {
|
||||
WvCasEcmParameters params =
|
||||
CreateEcmInitParameters(key_rotation_, content_iv_size_);
|
||||
CreateEcmInitParameters(key_rotation_, content_iv_size_, crypto_mode_);
|
||||
std::vector<EntitlementKeyInfo> entitlements =
|
||||
CreateInjectedEntitlements(key_rotation_);
|
||||
WvCasEcm wv_cas_ecm(params, entitlements);
|
||||
@@ -127,7 +132,7 @@ TEST_P(WvCasEcmTest, InitializeSuccess) {
|
||||
|
||||
TEST_P(WvCasEcmTest, GenerateSingleKeyEcmKeyRotationEnabledError) {
|
||||
WvCasEcmParameters params = CreateEcmInitParameters(
|
||||
/*key_rotation*/ true, content_iv_size_);
|
||||
/*key_rotation*/ true, content_iv_size_, crypto_mode_);
|
||||
std::vector<EntitlementKeyInfo> entitlements =
|
||||
CreateInjectedEntitlements(/*key_rotation*/ true);
|
||||
WvCasEcm wv_cas_ecm(params, entitlements);
|
||||
@@ -144,7 +149,7 @@ TEST_P(WvCasEcmTest, GenerateSingleKeyEcmKeyRotationEnabledError) {
|
||||
|
||||
TEST_P(WvCasEcmTest, GenerateEcmKeyRotationDisabledError) {
|
||||
WvCasEcmParameters params = CreateEcmInitParameters(
|
||||
/*key_rotation*/ false, content_iv_size_);
|
||||
/*key_rotation*/ false, content_iv_size_, crypto_mode_);
|
||||
std::vector<EntitlementKeyInfo> entitlements =
|
||||
CreateInjectedEntitlements(/*key_rotation*/ false);
|
||||
WvCasEcm wv_cas_ecm(params, entitlements);
|
||||
@@ -162,7 +167,7 @@ TEST_P(WvCasEcmTest, GenerateEcmKeyRotationDisabledError) {
|
||||
|
||||
TEST_P(WvCasEcmTest, GenerateEcmSuccess) {
|
||||
WvCasEcmParameters params =
|
||||
CreateEcmInitParameters(key_rotation_, content_iv_size_);
|
||||
CreateEcmInitParameters(key_rotation_, content_iv_size_, crypto_mode_);
|
||||
std::vector<EntitlementKeyInfo> entitlements =
|
||||
CreateInjectedEntitlements(key_rotation_);
|
||||
WvCasEcm wv_cas_ecm(params, entitlements);
|
||||
@@ -193,9 +198,10 @@ TEST_P(WvCasEcmTest, GenerateEcmSuccess) {
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(WvCasEcmTests, WvCasEcmTest,
|
||||
::testing::Combine(::testing::Values(8, 16),
|
||||
::testing::Bool()));
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
WvCasEcmTests, WvCasEcmTest,
|
||||
::testing::Combine(::testing::Values(8, 16), ::testing::Bool(),
|
||||
::testing::Values("AesScte", "DvbCsa2")));
|
||||
|
||||
} // namespace cas
|
||||
} // namespace widevine
|
||||
|
||||
Reference in New Issue
Block a user