Update includes and BUILD
This commit is contained in:
@@ -80,6 +80,12 @@ class CertificateClientCert : public ClientCert {
|
||||
widevine::ClientIdentification::TokenType type() const override {
|
||||
return ClientIdentification::DRM_DEVICE_CERTIFICATE;
|
||||
}
|
||||
const std::string& encrypted_unique_id() const override {
|
||||
return device_cert_.rot_id().encrypted_unique_id();
|
||||
}
|
||||
const std::string& unique_id_hash() const override {
|
||||
return device_cert_.rot_id().unique_id_hash();
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<ClientCertAlgorithm> algorithm_;
|
||||
|
||||
@@ -68,6 +68,8 @@ class ClientCert {
|
||||
virtual bool signed_by_provisioner() const = 0;
|
||||
virtual uint32_t system_id() const = 0;
|
||||
virtual widevine::ClientIdentification::TokenType type() const = 0;
|
||||
virtual const std::string& encrypted_unique_id() const = 0;
|
||||
virtual const std::string& unique_id_hash() const = 0;
|
||||
};
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
@@ -22,29 +22,50 @@ using oemcrypto_core_message::serialize::
|
||||
using oemcrypto_core_message::serialize::CreateCoreRenewalResponse;
|
||||
using widevine::Sha256_Hash;
|
||||
|
||||
// TODO(user): Check the Core*RequestFromMessage and
|
||||
// CreateCore*ResponseFromProto return value when b/148472911 is fixed.
|
||||
namespace widevine {
|
||||
namespace core_message_util {
|
||||
void GetCoreProvisioningResponse(const std::string& provisioning_response,
|
||||
std::string* core_message) {
|
||||
bool GetCoreProvisioningResponse(
|
||||
const std::string& serialized_provisioning_response,
|
||||
const std::string& request_core_message,
|
||||
std::string* response_core_message) {
|
||||
oemcrypto_core_message::ODK_ProvisioningRequest odk_provisioning_request;
|
||||
CoreProvisioningRequestFromMessage(*core_message, &odk_provisioning_request);
|
||||
CreateCoreProvisioningResponseFromProto(
|
||||
provisioning_response, odk_provisioning_request, core_message);
|
||||
CoreProvisioningRequestFromMessage(request_core_message,
|
||||
&odk_provisioning_request);
|
||||
CreateCoreProvisioningResponseFromProto(serialized_provisioning_response,
|
||||
odk_provisioning_request,
|
||||
response_core_message);
|
||||
return true;
|
||||
}
|
||||
|
||||
void GetCoreRenewalOrReleaseLicenseResponse(std::string* core_message) {
|
||||
bool GetCoreRenewalOrReleaseLicenseResponse(
|
||||
const std::string& request_core_message,
|
||||
std::string* response_core_message) {
|
||||
oemcrypto_core_message::ODK_RenewalRequest odk_renewal_request;
|
||||
CoreRenewalRequestFromMessage(*core_message, &odk_renewal_request);
|
||||
CreateCoreRenewalResponse(odk_renewal_request, core_message);
|
||||
CoreRenewalRequestFromMessage(request_core_message, &odk_renewal_request);
|
||||
// TODO(user): This function is going to need to know what the renewal
|
||||
// license is, and extract the renewal duration. This should be the sum of
|
||||
// renewal_delay_seconds + 2 * renewal_recovery_duration_seconds. Or if you
|
||||
// want, we could also create CreateCoreRenewalResponseFromProto -- is that
|
||||
// better?
|
||||
uint64_t renewal_duration_seconds = 3600; // I just made this up for now.
|
||||
CreateCoreRenewalResponse(odk_renewal_request, renewal_duration_seconds,
|
||||
response_core_message);
|
||||
return true;
|
||||
}
|
||||
|
||||
void GetCoreNewLicenseResponse(const std::string& license,
|
||||
std::string* core_message) {
|
||||
bool GetCoreNewLicenseResponse(const std::string& license,
|
||||
const std::string& request_core_message,
|
||||
const bool nonce_required,
|
||||
std::string* response_core_message) {
|
||||
oemcrypto_core_message::ODK_LicenseRequest odk_license_request;
|
||||
CoreLicenseRequestFromMessage(*core_message, &odk_license_request);
|
||||
std::string core_request_sha256 = Sha256_Hash(*core_message);
|
||||
CoreLicenseRequestFromMessage(request_core_message, &odk_license_request);
|
||||
std::string core_request_sha256 = Sha256_Hash(request_core_message);
|
||||
CreateCoreLicenseResponseFromProto(license, odk_license_request,
|
||||
core_request_sha256, core_message);
|
||||
core_request_sha256, nonce_required,
|
||||
response_core_message);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace core_message_util
|
||||
|
||||
@@ -13,20 +13,27 @@
|
||||
|
||||
namespace widevine {
|
||||
namespace core_message_util {
|
||||
// Gets the core message from |provisioning_response|. The output is held in
|
||||
// |core_message|. The provisioning response to be sent will be updated with
|
||||
// this |core_message|.
|
||||
void GetCoreProvisioningResponse(const std::string& provisioning_response,
|
||||
std::string* core_message);
|
||||
// Gets the |response_core_message| by parsing |request_core_message| and
|
||||
// |serialized_provisioning_response|. The output is held in
|
||||
// |response_core_message|.
|
||||
bool GetCoreProvisioningResponse(
|
||||
const std::string& serialized_provisioning_response,
|
||||
const std::string& request_core_message,
|
||||
std::string* response_core_message);
|
||||
|
||||
// Gets the core message for renewal or release license response. The output
|
||||
// is held in |core_message|.
|
||||
void GetCoreRenewalOrReleaseLicenseResponse(std::string* core_message);
|
||||
// Gets the |response_core_message| by parsing |request_core_message| for
|
||||
// release and renewal response. The output is held in |response_core_message|.
|
||||
bool GetCoreRenewalOrReleaseLicenseResponse(
|
||||
const std::string& request_core_message,
|
||||
std::string* response_core_message);
|
||||
|
||||
// Gets the core message from |license|. The output is held in |core_message|.
|
||||
// The license to be sent will be updated with this |core_message|.
|
||||
void GetCoreNewLicenseResponse(const std::string& license,
|
||||
std::string* core_message);
|
||||
// Gets the |response_core_message| by parsing |request_core_message| and
|
||||
// |license| for new license response. The output is held in
|
||||
// |response_core_message|.
|
||||
bool GetCoreNewLicenseResponse(const std::string& license,
|
||||
const std::string& request_core_message,
|
||||
const bool nonce_required,
|
||||
std::string* response_core_message);
|
||||
|
||||
} // namespace core_message_util
|
||||
} // namespace widevine
|
||||
|
||||
@@ -78,6 +78,8 @@ class MockClientCert : public ClientCert {
|
||||
MOCK_CONST_METHOD0(signer_serial_number, std::string &());
|
||||
MOCK_CONST_METHOD0(signer_creation_time_seconds, uint32_t());
|
||||
MOCK_CONST_METHOD0(type, ClientIdentification::TokenType());
|
||||
MOCK_CONST_METHOD0(encrypted_unique_id, const std::string &());
|
||||
MOCK_CONST_METHOD0(unique_id_hash, const std::string &());
|
||||
MOCK_CONST_METHOD0(signed_by_provisioner, bool());
|
||||
MOCK_CONST_METHOD3(VerifySignature, Status(const std::string &message,
|
||||
const std::string &signature,
|
||||
|
||||
@@ -43,6 +43,10 @@ class KeyboxClientCert : public ClientCert {
|
||||
widevine::ClientIdentification::TokenType type() const override {
|
||||
return ClientIdentification::KEYBOX;
|
||||
}
|
||||
const std::string& encrypted_unique_id() const override {
|
||||
return unimplemented_;
|
||||
}
|
||||
const std::string& unique_id_hash() const override { return unimplemented_; }
|
||||
|
||||
// Set the system-wide pre-provisioning keys; argument must be human-readable
|
||||
// hex digits.
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "common/rot_id_generator.h"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "google/protobuf/util/message_differencer.h"
|
||||
#include "testing/gmock.h"
|
||||
@@ -109,9 +110,9 @@ TEST_F(RootOfTrustIdGeneratorTest, GenerateIdSuccess) {
|
||||
|
||||
// Verify hashed unique id matches.
|
||||
std::string unique_id_hash = generator.GenerateUniqueIdHash(kTestUniqueId);
|
||||
EXPECT_TRUE(IsRotIdRevoked(root_of_trust_id.encrypted_unique_id(),
|
||||
kTestSystemId, root_of_trust_id.unique_id_hash(),
|
||||
{unique_id_hash}));
|
||||
EXPECT_TRUE(IsRotIdRevoked<std::vector<std::string>>(
|
||||
root_of_trust_id.encrypted_unique_id(), kTestSystemId,
|
||||
root_of_trust_id.unique_id_hash(), {unique_id_hash}));
|
||||
}
|
||||
|
||||
TEST_F(RootOfTrustIdGeneratorTest, GenerateIdUniqueSuccess) {
|
||||
@@ -156,7 +157,7 @@ TEST_F(RootOfTrustIdGeneratorTest, GenerateIdUniqueSuccess) {
|
||||
|
||||
// Verify hashed unique id matches.
|
||||
std::string unique_id_hash = generator.GenerateUniqueIdHash(kTestUniqueId);
|
||||
EXPECT_TRUE(IsRotIdRevoked(
|
||||
EXPECT_TRUE(IsRotIdRevoked<std::vector<std::string>>(
|
||||
second_root_of_trust_id.encrypted_unique_id(), kTestSystemId,
|
||||
second_root_of_trust_id.unique_id_hash(), {unique_id_hash}));
|
||||
}
|
||||
|
||||
@@ -21,25 +21,6 @@
|
||||
|
||||
namespace widevine {
|
||||
|
||||
bool IsRotIdRevoked(const std::string& encrypted_unique_id, uint32_t system_id,
|
||||
const std::string& rot_id_hash,
|
||||
const std::vector<std::string>& revoked_ids) {
|
||||
// This could conceivably happen for legacy DRM certificates without a ROT id.
|
||||
// No need to match if there's nothing to match against.
|
||||
if (encrypted_unique_id.empty() || rot_id_hash.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto& revoked_id : revoked_ids) {
|
||||
std::string revoked_hash =
|
||||
GenerateRotIdHash(encrypted_unique_id, system_id, revoked_id);
|
||||
if (rot_id_hash == revoked_hash) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string GenerateRotIdHash(const std::string& salt, uint32_t system_id,
|
||||
const std::string& unique_id_hash) {
|
||||
if (salt.empty() || unique_id_hash.empty()) {
|
||||
|
||||
@@ -21,15 +21,6 @@
|
||||
|
||||
namespace widevine {
|
||||
|
||||
// Helper function that compares the |rot_id_hash| to a hash of each of the
|
||||
// |revoked_ids|. The |revoked_ids| are the unique id hash (aka inner hash)
|
||||
// values as defined in the spec at go/wv-kb-id. The |encrypted_unique_id| and
|
||||
// |system_id| are used to compute the hash of each of the |revoked_ids|.
|
||||
// Returns true if any of the revoked_ids match.
|
||||
bool IsRotIdRevoked(const std::string& encrypted_unique_id, uint32_t system_id,
|
||||
const std::string& rot_id_hash,
|
||||
const std::vector<std::string>& revoked_ids);
|
||||
|
||||
// Helper function that generates the hash for the ROT id from the
|
||||
// |unique_id_hash|, the |system_id| and the |salt|. |salt| is typically an
|
||||
// encrypted unique id. Since we use an ephemeral eliptic curve key as part of
|
||||
@@ -41,5 +32,28 @@ bool IsRotIdRevoked(const std::string& encrypted_unique_id, uint32_t system_id,
|
||||
std::string GenerateRotIdHash(const std::string& salt, uint32_t system_id,
|
||||
const std::string& unique_id_hash);
|
||||
|
||||
// Helper function that compares the |rot_id_hash| to a hash of each of the
|
||||
// |revoked_ids|. The |revoked_ids| are the unique id hash (aka inner hash)
|
||||
// values as defined in the spec at go/wv-kb-id. The |encrypted_unique_id| and
|
||||
// |system_id| are used to compute the hash of each of the |revoked_ids|.
|
||||
// Returns true if any of the revoked_ids match.
|
||||
template <typename V>
|
||||
bool IsRotIdRevoked(const std::string& encrypted_unique_id, uint32_t system_id,
|
||||
const std::string& rot_id_hash, const V& revoked_ids) {
|
||||
// This could conceivably happen for legacy DRM certificates without a ROT id.
|
||||
// No need to match if there's nothing to match against.
|
||||
if (encrypted_unique_id.empty() || rot_id_hash.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto& revoked_id : revoked_ids) {
|
||||
if (GenerateRotIdHash(encrypted_unique_id, system_id, revoked_id) ==
|
||||
rot_id_hash) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
#endif // COMMON_ROT_ID_UTIL_H_
|
||||
|
||||
@@ -31,28 +31,28 @@ constexpr uint32_t kOtherFakeSystemId = 9876;
|
||||
namespace widevine {
|
||||
|
||||
TEST(RotIdUtilTest, IsRotIdRevokedMatches) {
|
||||
ASSERT_TRUE(IsRotIdRevoked(kFakeEncryptedId, kFakeSystemId,
|
||||
absl::HexStringToBytes(kRotIdHashHex),
|
||||
{"NO MATCH UNIQUE ID HASH 1", kFakeUniqueIdHash}));
|
||||
ASSERT_TRUE(IsRotIdRevoked<std::vector<std::string>>(
|
||||
kFakeEncryptedId, kFakeSystemId, absl::HexStringToBytes(kRotIdHashHex),
|
||||
{"NO MATCH UNIQUE ID HASH 1", kFakeUniqueIdHash}));
|
||||
}
|
||||
|
||||
TEST(RotIdUtilTest, IsRotIdRevokedNoMatchSystemId) {
|
||||
ASSERT_FALSE(
|
||||
IsRotIdRevoked(kFakeEncryptedId, kOtherFakeSystemId,
|
||||
absl::HexStringToBytes(kRotIdHashHex),
|
||||
{"NO MATCH UNIQUE ID HASH 1", kFakeUniqueIdHash}));
|
||||
ASSERT_FALSE(IsRotIdRevoked<std::vector<std::string>>(
|
||||
kFakeEncryptedId, kOtherFakeSystemId,
|
||||
absl::HexStringToBytes(kRotIdHashHex),
|
||||
{"NO MATCH UNIQUE ID HASH 1", kFakeUniqueIdHash}));
|
||||
}
|
||||
|
||||
TEST(RotIdUtilTest, IsRotIdRevokedNoMatch) {
|
||||
ASSERT_FALSE(IsRotIdRevoked(
|
||||
ASSERT_FALSE(IsRotIdRevoked<std::vector<std::string>>(
|
||||
kFakeEncryptedId, kFakeSystemId, kFakeUniqueIdHash,
|
||||
{"NO MATCH UNIQUE ID HASH 1", "NO MATCH UNIQUE ID HASH 2"}));
|
||||
}
|
||||
|
||||
TEST(RotIdUtilTest, IsRotIdRevokedEmptyList) {
|
||||
ASSERT_FALSE(IsRotIdRevoked(kFakeEncryptedId, kFakeSystemId,
|
||||
kFakeUniqueIdHash,
|
||||
{/* Intentionally empty vector */}));
|
||||
ASSERT_FALSE(IsRotIdRevoked<std::vector<std::string>>(
|
||||
kFakeEncryptedId, kFakeSystemId, kFakeUniqueIdHash,
|
||||
{/* Intentionally empty vector */}));
|
||||
}
|
||||
|
||||
// This test really only ensures the stability of the implementation. If the
|
||||
|
||||
@@ -86,10 +86,10 @@ class SecurityProfileList {
|
||||
|
||||
mutable absl::Mutex mutex_;
|
||||
// Widevine security profiles
|
||||
std::vector<SecurityProfile> security_profiles_ GUARDED_BY(mutex_);
|
||||
std::vector<SecurityProfile> security_profiles_ ABSL_GUARDED_BY(mutex_);
|
||||
// Custom security profiles
|
||||
std::map<std::string, SecurityProfile> custom_security_profiles_
|
||||
GUARDED_BY(mutex_);
|
||||
ABSL_GUARDED_BY(mutex_);
|
||||
};
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
@@ -46,7 +46,6 @@ cc_binary(
|
||||
srcs = ["wv_cas_ecm_example.cc"],
|
||||
deps = [
|
||||
"//base",
|
||||
"@abseil_repo//absl/base:core_headers",
|
||||
"//media_cas_packager_sdk/public:wv_cas_ecm",
|
||||
"//media_cas_packager_sdk/public:wv_cas_types",
|
||||
],
|
||||
@@ -57,6 +56,8 @@ cc_binary(
|
||||
srcs = ["wv_cas_key_fetcher_example.cc"],
|
||||
deps = [
|
||||
"//base",
|
||||
"@abseil_repo//absl/flags:flag",
|
||||
"@abseil_repo//absl/flags:parse",
|
||||
"//common:status",
|
||||
"//media_cas_packager_sdk/public:wv_cas_key_fetcher",
|
||||
"//protos/public:media_cas_encryption_cc_proto",
|
||||
@@ -66,9 +67,5 @@ cc_binary(
|
||||
cc_binary(
|
||||
name = "wv_cas_types_example",
|
||||
srcs = ["wv_cas_types_example.cc"],
|
||||
deps = [
|
||||
"//base",
|
||||
"@abseil_repo//absl/base:core_headers",
|
||||
"//media_cas_packager_sdk/public:wv_cas_types",
|
||||
],
|
||||
deps = ["//media_cas_packager_sdk/public:wv_cas_types"],
|
||||
)
|
||||
|
||||
@@ -384,6 +384,30 @@ constexpr char kTestStreamErrorResponse[] = {
|
||||
'\x00', '\x00' // parameter_value: actual value varies.
|
||||
};
|
||||
|
||||
constexpr char kTestEcmgChannelSetupWrongParameterLength[] = {
|
||||
'\x03', // protocol_version
|
||||
'\x00', '\x01', // message_type - Channel_setup
|
||||
'\x00', '\x0e', // message_length
|
||||
'\x00', '\x0e', // parameter_type - ECM_channel_id
|
||||
'\x00', '\x02', // parameter_length
|
||||
'\x00', '\x01', // parameter_value
|
||||
'\x00', '\x01', // parameter_type- SUPER_CAS_ID
|
||||
'\x00', '\x02', // parameter_length -- Should be \x04
|
||||
'\x4a', '\xd4', '\x00', '\x00' // parameter_value
|
||||
};
|
||||
|
||||
constexpr char kTestEcmgChannelSetupWrongMessageLength[] = {
|
||||
'\x03', // protocol_version
|
||||
'\x00', '\x01', // message_type - Channel_setup
|
||||
'\x00', '\xee', // message_length -- Should be \x0e
|
||||
'\x00', '\x0e', // parameter_type - ECM_channel_id
|
||||
'\x00', '\x02', // parameter_length
|
||||
'\x00', '\x01', // parameter_value
|
||||
'\x00', '\x01', // parameter_type- SUPER_CAS_ID
|
||||
'\x00', '\x04', // parameter_length -- Should be \x04
|
||||
'\x4a', '\xd4', '\x00', '\x00' // parameter_value
|
||||
};
|
||||
|
||||
} // namespace cas
|
||||
} // namespace widevine
|
||||
|
||||
|
||||
@@ -91,7 +91,10 @@ const char kTestEmmgDataProvision[] = {
|
||||
'\x00', '\x01', // parameter_value
|
||||
'\x00', '\x05', // parameter_type - datagram
|
||||
'\x00', '\xbc', // parameter_length
|
||||
'\x47', '\x5f', '\xff', '\x10', '\x00', '\x82', '\x70', '\x61', '\x00',
|
||||
'\x47', '\x40', '\x00', '\x10', '\x0a', '\x0d', '\x77', '\x69', '\x64',
|
||||
'\x65', '\x76', '\x69', '\x6e', '\x65', '\x5f', '\x74', '\x65', '\x73',
|
||||
'\x74', '\x12', '\x09', '\x43', '\x61', '\x73', '\x54', '\x73', '\x46',
|
||||
'\x61', '\x6b', '\x65', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
@@ -102,16 +105,13 @@ const char kTestEmmgDataProvision[] = {
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x83', '\x70', '\x61',
|
||||
'\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01',
|
||||
'\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01',
|
||||
'\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01',
|
||||
'\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01',
|
||||
'\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01',
|
||||
'\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01',
|
||||
'\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01',
|
||||
'\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01',
|
||||
'\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01'};
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00'};
|
||||
|
||||
const char kTestEmmgStreamCloseRequest[] = {
|
||||
'\x02', // protocol_version
|
||||
|
||||
@@ -8,11 +8,15 @@
|
||||
|
||||
// Example of how to use the wv_cas_ecm library.
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <cstdint>
|
||||
#include "media_cas_packager_sdk/public/wv_cas_ecm.h"
|
||||
#include "media_cas_packager_sdk/public/wv_cas_types.h"
|
||||
|
||||
@@ -96,11 +100,11 @@ int main(int argc, char** argv) {
|
||||
std::cout << std::endl;
|
||||
}
|
||||
// Write ECM TS Packet to a file.
|
||||
std::ofstream file;
|
||||
file.open(kOutputFile, std::ios_base::binary);
|
||||
assert(file.is_open());
|
||||
file.write(reinterpret_cast<char *>(packet), kTsPacketSize);
|
||||
file.close();
|
||||
std::ofstream file;
|
||||
file.open(kOutputFile, std::ios_base::binary);
|
||||
assert(file.is_open());
|
||||
file.write(reinterpret_cast<char*>(packet), kTsPacketSize);
|
||||
file.close();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -7,36 +7,39 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "gflags/gflags.h"
|
||||
#include "glog/logging.h"
|
||||
#include "absl/flags/flag.h"
|
||||
#include "absl/flags/parse.h"
|
||||
#include "common/status.h"
|
||||
#include "media_cas_packager_sdk/public/wv_cas_key_fetcher.h"
|
||||
#include "protos/public/media_cas_encryption.pb.h"
|
||||
|
||||
DEFINE_string(content_id, "21140844", "Content ID");
|
||||
DEFINE_bool(key_rotation, true, "Whether key rotation is enabled");
|
||||
DEFINE_string(track_type, "SD", "Provider name");
|
||||
ABSL_FLAG(std::string, content_id, "21140844", "Content ID");
|
||||
ABSL_FLAG(bool, key_rotation, true, "Whether key rotation is enabled");
|
||||
ABSL_FLAG(std::string, track_type, "SD", "Provider name");
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||
CHECK(!FLAGS_content_id.empty() && !FLAGS_track_type.empty())
|
||||
absl::ParseCommandLine(argc, argv);
|
||||
CHECK(!absl::GetFlag(FLAGS_content_id).empty() &&
|
||||
!absl::GetFlag(FLAGS_track_type).empty())
|
||||
<< "Flags 'content_id' and 'track_type' are required";
|
||||
// Required flags in key fetcher.
|
||||
CHECK(!FLAGS_license_server.empty() && !FLAGS_signing_provider.empty() &&
|
||||
!FLAGS_signing_key.empty() && !FLAGS_signing_iv.empty())
|
||||
CHECK(!absl::GetFlag(FLAGS_license_server).empty() &&
|
||||
!absl::GetFlag(FLAGS_signing_provider).empty() &&
|
||||
!absl::GetFlag(FLAGS_signing_key).empty() &&
|
||||
!absl::GetFlag(FLAGS_signing_iv).empty())
|
||||
<< "Flags 'license_server', 'signing_provider', 'signing_key' "
|
||||
"and 'signing_iv' are required";
|
||||
|
||||
std::string request_str;
|
||||
widevine::CasEncryptionRequest request;
|
||||
request.set_provider(FLAGS_signing_provider);
|
||||
request.set_content_id(FLAGS_content_id);
|
||||
request.set_key_rotation(FLAGS_key_rotation);
|
||||
request.set_provider(absl::GetFlag(FLAGS_signing_provider));
|
||||
request.set_content_id(absl::GetFlag(FLAGS_content_id));
|
||||
request.set_key_rotation(absl::GetFlag(FLAGS_key_rotation));
|
||||
// Only 1 track in this example.
|
||||
request.add_track_types(FLAGS_track_type);
|
||||
request.add_track_types(absl::GetFlag(FLAGS_track_type));
|
||||
LOG(INFO) << "Request: " << request.ShortDebugString();
|
||||
if (!request.SerializeToString(&request_str)) {
|
||||
LOG(ERROR) << "Failed to serialize request";
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "media_cas_packager_sdk/public/wv_cas_types.h"
|
||||
|
||||
|
||||
@@ -26,13 +26,11 @@ cc_library(
|
||||
hdrs = ["ecm.h"],
|
||||
deps = [
|
||||
"//base",
|
||||
"@abseil_repo//absl/base:core_headers",
|
||||
"@abseil_repo//absl/strings",
|
||||
"//common:aes_cbc_util",
|
||||
"//common:status",
|
||||
"//common:string_util",
|
||||
"//media_cas_packager_sdk/public:wv_cas_types",
|
||||
"//protos/public:media_cas_cc_proto",
|
||||
"//protos/public:media_cas_encryption_cc_proto",
|
||||
],
|
||||
)
|
||||
@@ -44,8 +42,6 @@ cc_test(
|
||||
deps = [
|
||||
":ecm",
|
||||
"//testing:gunit_main",
|
||||
"//common:status",
|
||||
"//media_cas_packager_sdk/public:wv_cas_types",
|
||||
"//protos/public:media_cas_encryption_cc_proto",
|
||||
],
|
||||
)
|
||||
@@ -57,7 +53,6 @@ cc_library(
|
||||
deps = [
|
||||
":ecm",
|
||||
"//base",
|
||||
"@abseil_repo//absl/base:core_headers",
|
||||
"//common:status",
|
||||
],
|
||||
)
|
||||
@@ -91,15 +86,12 @@ cc_library(
|
||||
":simulcrypt_util",
|
||||
":util",
|
||||
"//base",
|
||||
"@abseil_repo//absl/base:core_headers",
|
||||
"@abseil_repo//absl/container:node_hash_map",
|
||||
"@abseil_repo//absl/memory",
|
||||
"@abseil_repo//absl/strings",
|
||||
"@abseil_repo//absl/strings:str_format",
|
||||
"//common:crypto_util",
|
||||
"//common:random_util",
|
||||
"//common:status",
|
||||
"//example:constants",
|
||||
"//media_cas_packager_sdk/public:wv_cas_ecm",
|
||||
"//media_cas_packager_sdk/public:wv_cas_key_fetcher",
|
||||
"//media_cas_packager_sdk/public:wv_cas_types",
|
||||
@@ -132,12 +124,13 @@ cc_library(
|
||||
],
|
||||
deps = [
|
||||
":simulcrypt_util",
|
||||
":ts_packet",
|
||||
":util",
|
||||
"//base",
|
||||
"@abseil_repo//absl/base:core_headers",
|
||||
"@abseil_repo//absl/strings",
|
||||
"@abseil_repo//absl/strings:str_format",
|
||||
"//common:status",
|
||||
"//example:test_emmg_messages",
|
||||
"//protos/public:media_cas_cc_proto",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -182,10 +175,7 @@ cc_library(
|
||||
hdrs = [
|
||||
"mpeg2ts.h",
|
||||
],
|
||||
deps = [
|
||||
"//base",
|
||||
"@abseil_repo//absl/base:core_headers",
|
||||
],
|
||||
deps = ["//base"],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
@@ -198,8 +188,6 @@ cc_library(
|
||||
deps = [
|
||||
":util",
|
||||
"//base",
|
||||
"@abseil_repo//absl/base:core_headers",
|
||||
"//common:status",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -215,7 +203,6 @@ cc_library(
|
||||
deps = [
|
||||
":mpeg2ts",
|
||||
"//base",
|
||||
"@abseil_repo//absl/base:core_headers",
|
||||
"@abseil_repo//absl/strings",
|
||||
"//common:status",
|
||||
"//common:string_util",
|
||||
@@ -227,7 +214,6 @@ cc_test(
|
||||
size = "small",
|
||||
srcs = ["ts_packet_test.cc"],
|
||||
deps = [
|
||||
":mpeg2ts",
|
||||
":ts_packet",
|
||||
"//base",
|
||||
"//testing:gunit_main",
|
||||
@@ -245,7 +231,6 @@ cc_library(
|
||||
":mpeg2ts",
|
||||
":ts_packet",
|
||||
"//base",
|
||||
"@abseil_repo//absl/base:core_headers",
|
||||
"//common:status",
|
||||
],
|
||||
)
|
||||
@@ -258,6 +243,8 @@ cc_test(
|
||||
],
|
||||
deps = [
|
||||
":util",
|
||||
"//base",
|
||||
"//testing:gunit_main",
|
||||
"//common:status",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -8,14 +8,13 @@
|
||||
|
||||
#include "media_cas_packager_sdk/internal/ecm.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <bitset>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "common/aes_cbc_util.h"
|
||||
#include "common/status.h"
|
||||
#include "common/string_util.h"
|
||||
#include "protos/public/media_cas_encryption.pb.h"
|
||||
|
||||
|
||||
@@ -16,9 +16,9 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <cstdint>
|
||||
#include "common/status.h"
|
||||
#include "media_cas_packager_sdk/public/wv_cas_types.h"
|
||||
#include "protos/public/media_cas.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace cas {
|
||||
|
||||
@@ -8,9 +8,6 @@
|
||||
|
||||
#include "media_cas_packager_sdk/internal/ecm_generator.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "testing/gmock.h"
|
||||
#include "testing/gunit.h"
|
||||
#include "absl/memory/memory.h"
|
||||
|
||||
@@ -8,14 +8,8 @@
|
||||
|
||||
#include "media_cas_packager_sdk/internal/ecm.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "testing/gmock.h"
|
||||
#include "testing/gunit.h"
|
||||
#include "common/status.h"
|
||||
#include "media_cas_packager_sdk/public/wv_cas_types.h"
|
||||
#include "protos/public/media_cas_encryption.pb.h"
|
||||
|
||||
using ::testing::Return;
|
||||
|
||||
@@ -8,22 +8,13 @@
|
||||
|
||||
#include "media_cas_packager_sdk/internal/ecmg_client_handler.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "absl/memory/memory.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "common/crypto_util.h"
|
||||
#include "common/random_util.h"
|
||||
#include "common/status.h"
|
||||
#include "example/constants.h"
|
||||
#include "media_cas_packager_sdk/internal/ecmg_constants.h"
|
||||
#include "media_cas_packager_sdk/internal/fixed_key_fetcher.h"
|
||||
#include "media_cas_packager_sdk/internal/mpeg2ts.h"
|
||||
@@ -32,7 +23,6 @@
|
||||
#include "media_cas_packager_sdk/internal/util.h"
|
||||
#include "media_cas_packager_sdk/public/wv_cas_ecm.h"
|
||||
#include "media_cas_packager_sdk/public/wv_cas_key_fetcher.h"
|
||||
#include "media_cas_packager_sdk/public/wv_cas_types.h"
|
||||
|
||||
// CA System ID for Widevine.
|
||||
static constexpr uint16_t kWidevineSystemId = 0x4AD4;
|
||||
@@ -96,7 +86,7 @@ Status ProcessPrivateParameters(const char* const request, uint16_t param_type,
|
||||
break;
|
||||
case AGE_RESTRICTION:
|
||||
if (param_length != AGE_RESTRICTION_SIZE) {
|
||||
return Status(error::INVALID_ARGUMENT,
|
||||
return Status(error::FAILED_PRECONDITION,
|
||||
absl::StrCat("Invalid parameter length ", param_length,
|
||||
" for parameter type ", param_type));
|
||||
}
|
||||
@@ -106,7 +96,7 @@ Status ProcessPrivateParameters(const char* const request, uint16_t param_type,
|
||||
case ENTITLEMENT_ID_KEY_COMBINATION: {
|
||||
if (param_length !=
|
||||
kEntitlementKeyIdSizeBytes + kEntitlementKeyValueSizeBytes) {
|
||||
return Status(error::INVALID_ARGUMENT,
|
||||
return Status(error::FAILED_PRECONDITION,
|
||||
absl::StrCat("Invalid parameter length ", param_length,
|
||||
" for parameter type ", param_type));
|
||||
}
|
||||
@@ -185,7 +175,7 @@ Status HandleParameters(const char* const request, size_t request_length,
|
||||
}
|
||||
case CP_DURATION:
|
||||
if (param_length != CP_DURATION_SIZE) {
|
||||
return Status(error::INVALID_ARGUMENT,
|
||||
return Status(error::FAILED_PRECONDITION,
|
||||
absl::StrCat("Invalid parameter length ", param_length,
|
||||
" for parameter type ", param_type));
|
||||
}
|
||||
@@ -194,7 +184,7 @@ Status HandleParameters(const char* const request, size_t request_length,
|
||||
break;
|
||||
case CP_NUMBER:
|
||||
if (param_length != CP_NUMBER_SIZE) {
|
||||
return Status(error::INVALID_ARGUMENT,
|
||||
return Status(error::FAILED_PRECONDITION,
|
||||
absl::StrCat("Invalid parameter length ", param_length,
|
||||
" for parameter type ", param_type));
|
||||
}
|
||||
@@ -208,7 +198,7 @@ Status HandleParameters(const char* const request, size_t request_length,
|
||||
break;
|
||||
case ECM_CHANNEL_ID:
|
||||
if (param_length != ECM_CHANNEL_ID_SIZE) {
|
||||
return Status(error::INVALID_ARGUMENT,
|
||||
return Status(error::FAILED_PRECONDITION,
|
||||
absl::StrCat("Invalid parameter length ", param_length,
|
||||
" for parameter type ", param_type));
|
||||
}
|
||||
@@ -217,7 +207,7 @@ Status HandleParameters(const char* const request, size_t request_length,
|
||||
break;
|
||||
case ECM_ID:
|
||||
if (param_length != ECM_ID_SIZE) {
|
||||
return Status(error::INVALID_ARGUMENT,
|
||||
return Status(error::FAILED_PRECONDITION,
|
||||
absl::StrCat("Invalid parameter length ", param_length,
|
||||
" for parameter type ", param_type));
|
||||
}
|
||||
@@ -226,7 +216,7 @@ Status HandleParameters(const char* const request, size_t request_length,
|
||||
break;
|
||||
case ECM_STREAM_ID:
|
||||
if (param_length != ECM_STREAM_ID_SIZE) {
|
||||
return Status(error::INVALID_ARGUMENT,
|
||||
return Status(error::FAILED_PRECONDITION,
|
||||
absl::StrCat("Invalid parameter length ", param_length,
|
||||
" for parameter type ", param_type));
|
||||
}
|
||||
@@ -235,7 +225,7 @@ Status HandleParameters(const char* const request, size_t request_length,
|
||||
break;
|
||||
case NOMINAL_CP_DURATION:
|
||||
if (param_length != NOMINAL_CP_DURATION_SIZE) {
|
||||
return Status(error::INVALID_ARGUMENT,
|
||||
return Status(error::FAILED_PRECONDITION,
|
||||
absl::StrCat("Invalid parameter length ", param_length,
|
||||
" for parameter type ", param_type));
|
||||
}
|
||||
@@ -244,7 +234,7 @@ Status HandleParameters(const char* const request, size_t request_length,
|
||||
break;
|
||||
case SUPER_CAS_ID:
|
||||
if (param_length != SUPER_CAS_ID_SIZE) {
|
||||
return Status(error::INVALID_ARGUMENT,
|
||||
return Status(error::FAILED_PRECONDITION,
|
||||
absl::StrCat("Invalid parameter length ", param_length,
|
||||
" for parameter type ", param_type));
|
||||
}
|
||||
@@ -357,10 +347,14 @@ uint16_t StatusToDvbErrorCode(const Status& status) {
|
||||
return 0;
|
||||
}
|
||||
switch (status.error_code()) {
|
||||
case error::FAILED_PRECONDITION:
|
||||
return INCONSISTENT_LENGTH_FOR_DVB_PARAMETER;
|
||||
case error::INVALID_ARGUMENT:
|
||||
return INVALID_VALUE_FOR_DVB_PARAMETER;
|
||||
case error::NOT_FOUND:
|
||||
return MISSING_MANDATORY_DVB_PARAMETER;
|
||||
case error::UNIMPLEMENTED:
|
||||
return UNKNOWN_PARAMETER_TYPE_VALUE;
|
||||
case error::INTERNAL:
|
||||
default:
|
||||
return UNKNOWN_ERROR;
|
||||
@@ -454,22 +448,8 @@ void EcmgClientHandler::HandleRequest(const char* const request, char* response,
|
||||
Status status = HandleParameters(request + offset, request_length, ¶ms);
|
||||
if (!status.ok()) {
|
||||
LOG(ERROR) << status.ToString();
|
||||
switch (status.error_code()) {
|
||||
case error::INVALID_ARGUMENT:
|
||||
// TODO(user): Should use INCONSISTENT_LENGTH_FOR_DVB_PARAMETER in most
|
||||
// cases.
|
||||
BuildChannelError(params.ecm_channel_id,
|
||||
INVALID_VALUE_FOR_DVB_PARAMETER,
|
||||
status.error_message(), response, response_length);
|
||||
break;
|
||||
case error::UNIMPLEMENTED:
|
||||
BuildChannelError(params.ecm_channel_id, UNKNOWN_PARAMETER_TYPE_VALUE,
|
||||
status.error_message(), response, response_length);
|
||||
break;
|
||||
default:
|
||||
BuildChannelError(params.ecm_channel_id, UNKNOWN_ERROR,
|
||||
status.error_message(), response, response_length);
|
||||
}
|
||||
BuildChannelError(params.ecm_channel_id, StatusToDvbErrorCode(status),
|
||||
status.error_message(), response, response_length);
|
||||
return;
|
||||
}
|
||||
switch (request_type) {
|
||||
@@ -535,7 +515,7 @@ void EcmgClientHandler::HandleChannelSetup(const EcmgParameters& params,
|
||||
channel_id_ = params.ecm_channel_id;
|
||||
channel_id_set_ = true;
|
||||
|
||||
Status status = UpdatePrivateParameters(params, false);
|
||||
Status status = UpdateChannelPrivateParameters(params);
|
||||
if (!status.ok()) {
|
||||
LOG(ERROR) << status.ToString();
|
||||
BuildChannelError(params.ecm_channel_id, StatusToDvbErrorCode(status),
|
||||
@@ -595,7 +575,7 @@ void EcmgClientHandler::HandleStreamSetup(const EcmgParameters& params,
|
||||
streams_info_[params.ecm_stream_id] = absl::make_unique<EcmgStreamInfo>();
|
||||
streams_info_[params.ecm_stream_id]->ecm_id = params.ecm_id;
|
||||
|
||||
Status status = UpdatePrivateParameters(params, true);
|
||||
Status status = UpdateStreamPrivateParameters(params);
|
||||
if (!status.ok()) {
|
||||
LOG(ERROR) << status.ToString();
|
||||
BuildStreamError(params.ecm_channel_id, params.ecm_stream_id,
|
||||
@@ -684,7 +664,7 @@ void EcmgClientHandler::HandleCwProvision(const EcmgParameters& params,
|
||||
}
|
||||
|
||||
// Update private parameters based on access_criteria if any.
|
||||
Status status = UpdatePrivateParameters(params, true);
|
||||
Status status = UpdateStreamPrivateParameters(params);
|
||||
if (!status.ok()) {
|
||||
LOG(ERROR) << status.ToString();
|
||||
BuildStreamError(params.ecm_channel_id, params.ecm_stream_id,
|
||||
@@ -721,47 +701,23 @@ void EcmgClientHandler::HandleCwProvision(const EcmgParameters& params,
|
||||
params.cp_number, ecm_datagram, response, response_length);
|
||||
}
|
||||
|
||||
Status EcmgClientHandler::UpdatePrivateParameters(const EcmgParameters& params,
|
||||
bool stream_specific) {
|
||||
EcmgStreamInfo* stream_info =
|
||||
stream_specific ? streams_info_[params.ecm_stream_id].get() : nullptr;
|
||||
|
||||
Status EcmgClientHandler::UpdateCommonPrivateParameters(
|
||||
const EcmgParameters& params) {
|
||||
if (params.age_restriction != 0xff) {
|
||||
if (params.age_restriction > kMaxAllowedAgeRestriction) {
|
||||
return {error::INVALID_ARGUMENT, "Age restriction too large."};
|
||||
}
|
||||
age_restriction_ = params.age_restriction;
|
||||
}
|
||||
|
||||
if (!params.crypto_mode.empty()) {
|
||||
if (!StringToCryptoMode(params.crypto_mode,
|
||||
stream_specific ? &stream_info->crypto_mode
|
||||
: &ecmg_config_->crypto_mode)) {
|
||||
return {error::INVALID_ARGUMENT,
|
||||
absl::StrCat("Unknown crypto mode: ", params.crypto_mode, ".")};
|
||||
}
|
||||
}
|
||||
|
||||
if (!params.track_types.empty()) {
|
||||
track_types_.assign(params.track_types.begin(), params.track_types.end());
|
||||
}
|
||||
|
||||
if (!params.stream_track_type.empty()) {
|
||||
if (stream_specific) {
|
||||
stream_info->track_type = params.stream_track_type;
|
||||
} else {
|
||||
LOG(WARNING) << "Ignoring stream track type received in channel config.";
|
||||
}
|
||||
}
|
||||
|
||||
if (!params.content_id.empty()) {
|
||||
content_id_ = params.content_id;
|
||||
}
|
||||
|
||||
if (!params.content_provider.empty()) {
|
||||
content_provider_ = params.content_provider;
|
||||
}
|
||||
|
||||
if (!params.content_ivs.empty()) {
|
||||
if (params.content_ivs.size() < ecmg_config_->number_of_content_keys) {
|
||||
return {error::INVALID_ARGUMENT,
|
||||
@@ -778,19 +734,54 @@ Status EcmgClientHandler::UpdatePrivateParameters(const EcmgParameters& params,
|
||||
return {error::INVALID_ARGUMENT, "Size of content ivs must match."};
|
||||
}
|
||||
}
|
||||
if (stream_specific) {
|
||||
stream_info->content_ivs.assign(params.content_ivs.begin(),
|
||||
params.content_ivs.end());
|
||||
} else {
|
||||
content_ivs_.assign(params.content_ivs.begin(), params.content_ivs.end());
|
||||
}
|
||||
}
|
||||
|
||||
if (!params.entitlement_comb.empty()) {
|
||||
entitlement_comb_.assign(params.entitlement_comb.begin(),
|
||||
params.entitlement_comb.end());
|
||||
}
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
Status EcmgClientHandler::UpdateChannelPrivateParameters(
|
||||
const EcmgParameters& params) {
|
||||
Status status = UpdateCommonPrivateParameters(params);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
if (!params.crypto_mode.empty()) {
|
||||
if (!StringToCryptoMode(params.crypto_mode, &ecmg_config_->crypto_mode)) {
|
||||
return {error::INVALID_ARGUMENT,
|
||||
absl::StrCat("Unknown crypto mode: ", params.crypto_mode, ".")};
|
||||
}
|
||||
}
|
||||
if (!params.content_ivs.empty()) {
|
||||
content_ivs_.assign(params.content_ivs.begin(), params.content_ivs.end());
|
||||
}
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
Status EcmgClientHandler::UpdateStreamPrivateParameters(
|
||||
const EcmgParameters& params) {
|
||||
DCHECK(streams_info_.contains(params.ecm_stream_id));
|
||||
EcmgStreamInfo* stream_info = streams_info_[params.ecm_stream_id].get();
|
||||
|
||||
Status status = UpdateCommonPrivateParameters(params);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
if (!params.crypto_mode.empty()) {
|
||||
if (!StringToCryptoMode(params.crypto_mode, &stream_info->crypto_mode)) {
|
||||
return {error::INVALID_ARGUMENT,
|
||||
absl::StrCat("Unknown crypto mode: ", params.crypto_mode, ".")};
|
||||
}
|
||||
}
|
||||
if (!params.stream_track_type.empty()) {
|
||||
stream_info->track_type = params.stream_track_type;
|
||||
}
|
||||
if (!params.content_ivs.empty()) {
|
||||
stream_info->content_ivs.assign(params.content_ivs.begin(),
|
||||
params.content_ivs.end());
|
||||
}
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
|
||||
@@ -10,13 +10,8 @@
|
||||
#define MEDIA_CAS_PACKAGER_SDK_INTERNAL_ECMG_CLIENT_HANDLER_H_
|
||||
|
||||
#include <cstddef>
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <cstdint>
|
||||
@@ -116,12 +111,12 @@ class EcmgClientHandler {
|
||||
size_t* response_length);
|
||||
void HandleCwProvision(const EcmgParameters& params, char* response,
|
||||
size_t* response_length);
|
||||
// Update private paprameters using |params|. |stream_specific| indicates if
|
||||
// |params| is for a single stream or the whole channel. If |stream_specific|
|
||||
// is true, |params| will only affect values of this stream. If it is false,
|
||||
// |param| will affect all streams of the channel.
|
||||
Status UpdatePrivateParameters(const EcmgParameters& params,
|
||||
bool stream_specific);
|
||||
|
||||
// Update private parameters using |params|.
|
||||
Status UpdateChannelPrivateParameters(const EcmgParameters& params);
|
||||
Status UpdateStreamPrivateParameters(const EcmgParameters& params);
|
||||
Status UpdateCommonPrivateParameters(const EcmgParameters& params);
|
||||
|
||||
// Check if all required parameters have been set. If so, initialize |ecm_| by
|
||||
// fetching entitlement keys.
|
||||
Status CheckAndInitializeEcm(const EcmgParameters& params);
|
||||
|
||||
@@ -8,9 +8,10 @@
|
||||
|
||||
#include "media_cas_packager_sdk/internal/ecmg_client_handler.h"
|
||||
|
||||
#include <string>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "testing/gmock.h"
|
||||
#include "testing/gunit.h"
|
||||
#include "absl/memory/memory.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
@@ -455,6 +456,81 @@ TEST_F(EcmgClientHandlerTest, WrongParameterChannelId) {
|
||||
CheckStreamError(UNKNOWN_ECM_CHANNEL_ID_VALUE, response_, response_len_);
|
||||
}
|
||||
|
||||
TEST_F(EcmgClientHandlerTest, WrongParameterCryptoMode) {
|
||||
BuildChannelSetupRequest(kChannelId, kSuperCasId, kAgeRestriction,
|
||||
"someCryptoMode", {kTrackTypesHD, kTrackTypesSD},
|
||||
kContentId, kContentProvider, /*entitlements*/ {},
|
||||
request_, &request_len_);
|
||||
handler_->HandleRequest(request_, response_, &response_len_);
|
||||
CheckChannelError(INVALID_VALUE_FOR_DVB_PARAMETER, response_, response_len_);
|
||||
}
|
||||
|
||||
TEST_F(EcmgClientHandlerTest, WrongParameterLengthSuperCasId) {
|
||||
// Setup a channel with an unexpected super cas id length (2 instead of 4).
|
||||
handler_->HandleRequest(kTestEcmgChannelSetupWrongParameterLength, response_,
|
||||
&response_len_);
|
||||
CheckChannelError(INCONSISTENT_LENGTH_FOR_DVB_PARAMETER, response_,
|
||||
response_len_);
|
||||
}
|
||||
|
||||
TEST_F(EcmgClientHandlerTest, NoEntitlementsNoContentIdProvider) {
|
||||
BuildChannelSetupRequest(kChannelId, kSuperCasId, kAgeRestriction,
|
||||
kCryptoMode, {kTrackTypesHD, kTrackTypesSD},
|
||||
/*ContentId*/ "", /*ContentProvider*/ "",
|
||||
/*entitlements*/ {}, 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, {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, kContentKeyEven}, {kCpNumber + 1, kContentKeyOdd}};
|
||||
BuildCwProvisionRequest(kChannelId, kStreamId, kCpNumber, cp_cw_combination,
|
||||
request_, &request_len_);
|
||||
handler_->HandleRequest(request_, response_, &response_len_);
|
||||
CheckStreamError(MISSING_MANDATORY_DVB_PARAMETER, response_, response_len_);
|
||||
}
|
||||
|
||||
TEST_F(EcmgClientHandlerTest, NotEnoughInjectedEntitlements) {
|
||||
BuildChannelSetupRequest(
|
||||
kChannelId, kSuperCasId, kAgeRestriction, kCryptoMode,
|
||||
{kTrackTypesHD, kTrackTypesSD},
|
||||
/*ContentId*/ "", /*ContentProvider*/ "",
|
||||
{absl::StrCat(kEntitlementKeyIdEven, kEntitlementKeyValueEven),
|
||||
absl::StrCat(kEntitlementKeyIdOdd, kEntitlementKeyValueOdd)},
|
||||
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, {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, kContentKeyEven}, {kCpNumber + 1, kContentKeyOdd}};
|
||||
BuildCwProvisionRequest(kChannelId, kStreamId, kCpNumber, cp_cw_combination,
|
||||
request_, &request_len_);
|
||||
handler_->HandleRequest(request_, response_, &response_len_);
|
||||
CheckStreamError(MISSING_MANDATORY_DVB_PARAMETER, response_, response_len_);
|
||||
}
|
||||
|
||||
TEST_F(EcmgClientHandlerTest, WrongMessageLength) {
|
||||
// Setup a channel with a wrong message length specified (too large).
|
||||
handler_->HandleRequest(kTestEcmgChannelSetupWrongMessageLength, response_,
|
||||
&response_len_);
|
||||
CheckChannelError(UNKNOWN_PARAMETER_TYPE_VALUE, response_, response_len_);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace cas
|
||||
} // namespace widevine
|
||||
|
||||
@@ -10,18 +10,19 @@
|
||||
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "example/test_emmg_messages.h"
|
||||
#include "media_cas_packager_sdk/internal/emmg_constants.h"
|
||||
#include "media_cas_packager_sdk/internal/mpeg2ts.h"
|
||||
#include "media_cas_packager_sdk/internal/simulcrypt_constants.h"
|
||||
#include "media_cas_packager_sdk/internal/simulcrypt_util.h"
|
||||
#include "media_cas_packager_sdk/internal/ts_packet.h"
|
||||
#include "media_cas_packager_sdk/internal/util.h"
|
||||
#include "protos/public/media_cas.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace cas {
|
||||
@@ -125,6 +126,36 @@ void Emmg::BuildStreamSetup() {
|
||||
Host16ToBigEndian(request_ + 3, &total_param_length);
|
||||
}
|
||||
|
||||
Status Emmg::GeneratePrivateData(const std::string& content_provider,
|
||||
const std::string& content_id, uint8_t* buffer) {
|
||||
DCHECK(buffer);
|
||||
// Generate payload.
|
||||
CaDescriptorPrivateData private_data;
|
||||
private_data.set_provider(content_provider);
|
||||
private_data.set_content_id(content_id);
|
||||
std::string private_data_str = private_data.SerializeAsString();
|
||||
std::string payload_filler(kMaxTsPayloadSize - private_data_str.size(), 0);
|
||||
|
||||
// Wrap the data with a TS header.
|
||||
TsPacket ecm_packet;
|
||||
ecm_packet.set_payload_unit_start_indicator(true);
|
||||
ecm_packet.set_pid(0);
|
||||
ecm_packet.set_payload(absl::StrCat(private_data_str, payload_filler));
|
||||
ecm_packet.set_adaptation_field_control(TsPacket::kPayloadOnly);
|
||||
ecm_packet.set_continuity_counter(continuity_counter_);
|
||||
continuity_counter_ = ++continuity_counter_ & 0xf;
|
||||
|
||||
// And write the packet.
|
||||
std::string ecm_ts_packet;
|
||||
Status status = ecm_packet.Write(&ecm_ts_packet);
|
||||
if (!status.ok()) {
|
||||
LOG(ERROR) << status.ToString();
|
||||
return status;
|
||||
}
|
||||
memcpy(buffer, ecm_ts_packet.data(), ecm_ts_packet.size());
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
void Emmg::BuildDataProvision() {
|
||||
bzero(request_, BUFFER_SIZE);
|
||||
request_length_ = 0;
|
||||
@@ -142,15 +173,11 @@ void Emmg::BuildDataProvision() {
|
||||
simulcrypt_util::AddUint16Param(EMMG_DATA_ID, emmg_config_->data_id, request_,
|
||||
&request_length_);
|
||||
|
||||
// Add a fake TS packet.
|
||||
uint16_t datagram_type = EMMG_DATAGRAM;
|
||||
Host16ToBigEndian(request_ + request_length_, &datagram_type);
|
||||
request_length_ += 2;
|
||||
uint16_t param_length = sizeof(kTestEmmgTsPacket); // Should be 188.
|
||||
Host16ToBigEndian(request_ + request_length_, ¶m_length);
|
||||
request_length_ += 2;
|
||||
memcpy(request_ + request_length_, kTestEmmgTsPacket, param_length);
|
||||
request_length_ += param_length;
|
||||
uint8_t datagram[kTsPacketSize];
|
||||
GeneratePrivateData(emmg_config_->content_provider, emmg_config_->content_id,
|
||||
datagram);
|
||||
simulcrypt_util::AddParam(EMMG_DATAGRAM, datagram, kTsPacketSize, request_,
|
||||
&request_length_);
|
||||
|
||||
uint16_t total_param_length = request_length_ - 5;
|
||||
Host16ToBigEndian(request_ + 3, &total_param_length);
|
||||
|
||||
@@ -10,13 +10,9 @@
|
||||
#define MEDIA_CAS_PACKAGER_SDK_INTERNAL_EMMG_H_
|
||||
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <cstdint>
|
||||
#include "glog/logging.h"
|
||||
#include "common/status.h"
|
||||
|
||||
#define BUFFER_SIZE (1024)
|
||||
@@ -32,6 +28,8 @@ struct EmmgConfig {
|
||||
uint16_t data_stream_id;
|
||||
uint16_t data_id;
|
||||
uint8_t data_type;
|
||||
std::string content_provider;
|
||||
std::string content_id;
|
||||
};
|
||||
|
||||
// A class that sends EMMG/PDG message to the MUX server.
|
||||
@@ -62,6 +60,8 @@ class Emmg {
|
||||
void SendStreamCloseRequest();
|
||||
void SendChannelClose();
|
||||
|
||||
Status GeneratePrivateData(const std::string& content_provider,
|
||||
const std::string& content_id, uint8_t* buffer);
|
||||
void ReceiveResponseAndVerify(uint16_t expected_type);
|
||||
void Send(uint16_t message_type);
|
||||
|
||||
@@ -72,6 +72,8 @@ class Emmg {
|
||||
// |server_socket_fd| is a file descriptor we can use to communicate
|
||||
// with the MUX server.
|
||||
int server_socket_fd_;
|
||||
// |continuity_counter| is incremented each time private data is generated.
|
||||
int continuity_counter_ = 0;
|
||||
};
|
||||
|
||||
} // namespace cas
|
||||
|
||||
@@ -8,6 +8,12 @@
|
||||
|
||||
#include "media_cas_packager_sdk/internal/emmg.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "testing/gunit.h"
|
||||
#include "absl/memory/memory.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
@@ -38,6 +44,8 @@ class EmmgTest : public ::testing::Test {
|
||||
config_.data_stream_id = 0x0001;
|
||||
config_.data_id = 0x0001;
|
||||
config_.data_type = 0x01;
|
||||
config_.content_provider = "widevine_test";
|
||||
config_.content_id = "CasTsFake";
|
||||
emmg_ = absl::make_unique<TestableEmmg>(&config_);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
#include "media_cas_packager_sdk/internal/fixed_key_fetcher.h"
|
||||
|
||||
#include "common/status.h"
|
||||
#include "protos/public/media_cas_encryption.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
#ifndef MEDIA_CAS_PACKAGER_SDK_INTERNAL_FIXED_KEY_FETCHER_H_
|
||||
#define MEDIA_CAS_PACKAGER_SDK_INTERNAL_FIXED_KEY_FETCHER_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "common/status.h"
|
||||
#include "media_cas_packager_sdk/internal/key_fetcher.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
#include <cstddef>
|
||||
|
||||
#include <cstdint>
|
||||
#include "base/macros.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace cas {
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
#include "media_cas_packager_sdk/internal/simulcrypt_util.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
|
||||
#include "glog/logging.h"
|
||||
|
||||
@@ -12,13 +12,8 @@
|
||||
#define MEDIA_CAS_PACKAGER_SDK_INTERNAL_SIMULCRYPT_UTIL_H_
|
||||
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <cstdint>
|
||||
#include "common/status.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace cas {
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "common/status.h"
|
||||
#include "common/string_util.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
@@ -33,11 +33,9 @@
|
||||
#ifndef MEDIA_CAS_PACKAGER_SDK_INTERNAL_TS_PACKET_H_
|
||||
#define MEDIA_CAS_PACKAGER_SDK_INTERNAL_TS_PACKET_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <cstdint>
|
||||
#include "base/macros.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "common/status.h"
|
||||
#include "media_cas_packager_sdk/internal/mpeg2ts.h"
|
||||
|
||||
@@ -8,12 +8,9 @@
|
||||
|
||||
#include "media_cas_packager_sdk/internal/ts_packet.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "testing/gmock.h"
|
||||
#include "testing/gunit.h"
|
||||
#include "media_cas_packager_sdk/internal/mpeg2ts.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace cas {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "media_cas_packager_sdk/internal/util.h"
|
||||
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
|
||||
|
||||
@@ -60,7 +60,6 @@ cc_library(
|
||||
deps = [
|
||||
":wv_cas_types",
|
||||
"//base",
|
||||
"@abseil_repo//absl/base:core_headers",
|
||||
"@abseil_repo//absl/strings",
|
||||
"//common:status",
|
||||
"//common:string_util",
|
||||
@@ -77,7 +76,6 @@ cc_test(
|
||||
srcs = ["wv_cas_ca_descriptor_test.cc"],
|
||||
deps = [
|
||||
":wv_cas_ca_descriptor",
|
||||
":wv_cas_types",
|
||||
"//testing:gunit_main",
|
||||
"//protos/public:media_cas_cc_proto",
|
||||
],
|
||||
@@ -113,7 +111,6 @@ cc_test(
|
||||
srcs = ["wv_cas_ecm_test.cc"],
|
||||
deps = [
|
||||
":wv_cas_ecm",
|
||||
":wv_cas_types",
|
||||
"//testing:gunit_main",
|
||||
"@abseil_repo//absl/strings",
|
||||
"//media_cas_packager_sdk/internal:mpeg2ts",
|
||||
@@ -131,10 +128,9 @@ cc_library(
|
||||
deps = [
|
||||
"//base",
|
||||
"//external:protobuf",
|
||||
"@abseil_repo//absl/base:core_headers",
|
||||
"@abseil_repo//absl/flags:flag",
|
||||
"@abseil_repo//absl/strings",
|
||||
"@curl_repo//:curl",
|
||||
"//util:error_space",
|
||||
"//common:signature_util",
|
||||
"//common:status",
|
||||
"//media_cas_packager_sdk/internal:key_fetcher",
|
||||
@@ -153,7 +149,9 @@ cc_test(
|
||||
"//base",
|
||||
"//external:protobuf",
|
||||
"//testing:gunit_main",
|
||||
"@abseil_repo//absl/flags:flag",
|
||||
"@abseil_repo//absl/strings",
|
||||
"//common:status",
|
||||
"//protos/public:media_cas_encryption_cc_proto",
|
||||
],
|
||||
)
|
||||
@@ -189,8 +187,8 @@ cc_binary(
|
||||
deps = [
|
||||
":wv_cas_types",
|
||||
"//base",
|
||||
"@abseil_repo//absl/base:core_headers",
|
||||
"@abseil_repo//absl/strings",
|
||||
"@abseil_repo//absl/flags:flag",
|
||||
"@abseil_repo//absl/flags:parse",
|
||||
"//media_cas_packager_sdk/internal:ecmg_client_handler",
|
||||
],
|
||||
)
|
||||
@@ -200,6 +198,8 @@ cc_binary(
|
||||
srcs = ["wv_emmg.cc"],
|
||||
deps = [
|
||||
"//base",
|
||||
"@abseil_repo//absl/flags:flag",
|
||||
"@abseil_repo//absl/flags:parse",
|
||||
"//media_cas_packager_sdk/internal:emmg",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "common/status.h"
|
||||
#include "common/string_util.h"
|
||||
#include "media_cas_packager_sdk/public/wv_cas_types.h"
|
||||
#include "protos/public/media_cas.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
@@ -10,8 +10,10 @@
|
||||
#define MEDIA_CAS_PACKAGER_SDK_PUBLIC_WV_CAS_CA_DESCRIPTOR_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <cstdint>
|
||||
#include "media_cas_packager_sdk/public/wv_cas_types.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
@@ -8,10 +8,7 @@
|
||||
|
||||
#include "media_cas_packager_sdk/public/wv_cas_ca_descriptor.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "testing/gunit.h"
|
||||
#include "media_cas_packager_sdk/public/wv_cas_types.h"
|
||||
#include "protos/public/media_cas.pb.h"
|
||||
|
||||
using ::testing::Test;
|
||||
|
||||
@@ -8,13 +8,15 @@
|
||||
|
||||
#include "media_cas_packager_sdk/public/wv_cas_ecm.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "absl/memory/memory.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "common/crypto_util.h"
|
||||
#include "common/status.h"
|
||||
#include "example/constants.h"
|
||||
@@ -22,7 +24,6 @@
|
||||
#include "media_cas_packager_sdk/internal/fixed_key_fetcher.h"
|
||||
#include "media_cas_packager_sdk/internal/mpeg2ts.h"
|
||||
#include "media_cas_packager_sdk/internal/util.h"
|
||||
#include "media_cas_packager_sdk/public/wv_cas_types.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace cas {
|
||||
|
||||
@@ -9,13 +9,9 @@
|
||||
#ifndef MEDIA_CAS_PACKAGER_SDK_PUBLIC_WV_CAS_ECM_H_
|
||||
#define MEDIA_CAS_PACKAGER_SDK_PUBLIC_WV_CAS_ECM_H_
|
||||
|
||||
#include <cstddef>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <cstdint>
|
||||
#include "media_cas_packager_sdk/public/wv_cas_types.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
@@ -8,11 +8,11 @@
|
||||
|
||||
#include "media_cas_packager_sdk/public/wv_cas_ecm.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "testing/gunit.h"
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "media_cas_packager_sdk/internal/mpeg2ts.h"
|
||||
#include "media_cas_packager_sdk/public/wv_cas_types.h"
|
||||
|
||||
using ::testing::Test;
|
||||
|
||||
|
||||
@@ -10,32 +10,32 @@
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
|
||||
#include "gflags/gflags.h"
|
||||
#include <cstdint>
|
||||
#include "glog/logging.h"
|
||||
#include "google/protobuf/util/json_util.h"
|
||||
#include "absl/flags/flag.h"
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "curl/curl.h"
|
||||
#include "curl/easy.h"
|
||||
#include "common/signature_util.h"
|
||||
#include "common/status.h"
|
||||
#include "protos/public/media_cas_encryption.pb.h"
|
||||
|
||||
using google::protobuf::util::JsonPrintOptions;
|
||||
using google::protobuf::util::JsonStringToMessage;
|
||||
using google::protobuf::util::MessageToJsonString;
|
||||
|
||||
DEFINE_string(
|
||||
license_server, "",
|
||||
"HTTP URL to the license server for making CAS encryption request");
|
||||
DEFINE_string(signing_provider, "",
|
||||
"Name of the provider signing the CAS encryption request");
|
||||
DEFINE_string(signing_key, "",
|
||||
"AES key (in hex) for signing the CAS encryption request");
|
||||
DEFINE_string(signing_iv, "",
|
||||
"AES iv (in hex) for signing the CAS encryption request");
|
||||
ABSL_FLAG(std::string, license_server, "",
|
||||
"HTTP URL to the license server for making CAS encryption request.");
|
||||
ABSL_FLAG(std::string, signing_provider, "",
|
||||
"Name of the provider signing the CAS encryption request.");
|
||||
ABSL_FLAG(std::string, signing_key, "",
|
||||
"AES key (in hex) for signing the CAS encryption request.");
|
||||
ABSL_FLAG(std::string, signing_iv, "",
|
||||
"AES iv (in hex) for signing the CAS encryption request.");
|
||||
|
||||
namespace widevine {
|
||||
namespace cas {
|
||||
@@ -43,8 +43,9 @@ namespace cas {
|
||||
Status WvCasKeyFetcher::RequestEntitlementKey(
|
||||
const std::string& request_string,
|
||||
std::string* signed_response_string) const {
|
||||
if (FLAGS_signing_provider.empty() || FLAGS_signing_key.empty() ||
|
||||
FLAGS_signing_iv.empty()) {
|
||||
if (absl::GetFlag(FLAGS_signing_provider).empty() ||
|
||||
absl::GetFlag(FLAGS_signing_key).empty() ||
|
||||
absl::GetFlag(FLAGS_signing_iv).empty()) {
|
||||
return Status(
|
||||
error::INVALID_ARGUMENT,
|
||||
"Flag 'signing_provider', 'signing_key' or 'signing_iv' is empty");
|
||||
@@ -73,13 +74,14 @@ Status WvCasKeyFetcher::RequestEntitlementKey(
|
||||
signed_request.set_request(request_json);
|
||||
std::string signature;
|
||||
if (!signature_util::GenerateAesSignature(
|
||||
request_json, absl::HexStringToBytes(FLAGS_signing_key),
|
||||
absl::HexStringToBytes(FLAGS_signing_iv), &signature)
|
||||
request_json,
|
||||
absl::HexStringToBytes(absl::GetFlag(FLAGS_signing_key)),
|
||||
absl::HexStringToBytes(absl::GetFlag(FLAGS_signing_iv)), &signature)
|
||||
.ok()) {
|
||||
return Status(error::INTERNAL, "Failed to sign the request.");
|
||||
}
|
||||
signed_request.set_signature(signature);
|
||||
signed_request.set_signer(FLAGS_signing_provider);
|
||||
signed_request.set_signer(absl::GetFlag(FLAGS_signing_provider));
|
||||
std::string signed_request_json;
|
||||
// NOTE: MessageToJsonString will automatically converts the 'request' and
|
||||
// 'signature' fields in SignedCasEncryptionRequest to base64, because they
|
||||
@@ -126,14 +128,15 @@ size_t AppendToString(void* ptr, size_t size, size_t count,
|
||||
Status WvCasKeyFetcher::MakeHttpRequest(const std::string& signed_request_json,
|
||||
std::string* http_response_json) const {
|
||||
CHECK(http_response_json);
|
||||
if (FLAGS_license_server.empty()) {
|
||||
if (absl::GetFlag(FLAGS_license_server).empty()) {
|
||||
return Status(error::INVALID_ARGUMENT, "Flag 'license_server' is empty");
|
||||
}
|
||||
CURL* curl;
|
||||
CURLcode curl_code;
|
||||
curl = curl_easy_init();
|
||||
if (curl) {
|
||||
curl_easy_setopt(curl, CURLOPT_URL, FLAGS_license_server.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_URL,
|
||||
absl::GetFlag(FLAGS_license_server).c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, signed_request_json.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, http_response_json);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &AppendToString);
|
||||
|
||||
@@ -11,14 +11,10 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "gflags/gflags.h"
|
||||
#include "absl/flags/declare.h"
|
||||
#include "common/status.h"
|
||||
#include "media_cas_packager_sdk/internal/key_fetcher.h"
|
||||
|
||||
DECLARE_string(license_server);
|
||||
DECLARE_string(signing_provider);
|
||||
DECLARE_string(signing_key);
|
||||
DECLARE_string(signing_iv);
|
||||
|
||||
namespace widevine {
|
||||
namespace cas {
|
||||
|
||||
@@ -55,4 +51,10 @@ class WvCasKeyFetcher : public KeyFetcher {
|
||||
} // namespace cas
|
||||
} // namespace widevine
|
||||
|
||||
// Exposed for testing and example.
|
||||
ABSL_DECLARE_FLAG(std::string, license_server);
|
||||
ABSL_DECLARE_FLAG(std::string, signing_provider);
|
||||
ABSL_DECLARE_FLAG(std::string, signing_key);
|
||||
ABSL_DECLARE_FLAG(std::string, signing_iv);
|
||||
|
||||
#endif // MEDIA_CAS_PACKAGER_SDK_PUBLIC_WV_CAS_KEY_FETCHER_H_
|
||||
|
||||
@@ -8,12 +8,13 @@
|
||||
|
||||
#include "media_cas_packager_sdk/public/wv_cas_key_fetcher.h"
|
||||
|
||||
#include "gflags/gflags.h"
|
||||
#include "glog/logging.h"
|
||||
#include "google/protobuf/text_format.h"
|
||||
#include "testing/gmock.h"
|
||||
#include "testing/gunit.h"
|
||||
#include "absl/flags/flag.h"
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "common/status.h"
|
||||
#include "protos/public/media_cas_encryption.pb.h"
|
||||
|
||||
using testing::_;
|
||||
@@ -63,9 +64,9 @@ class WvCasKeyFetcherTest : public ::testing::Test {
|
||||
public:
|
||||
WvCasKeyFetcherTest() {}
|
||||
void SetUp() override {
|
||||
FLAGS_signing_provider = kSigningProvider;
|
||||
FLAGS_signing_key = kSingingKey;
|
||||
FLAGS_signing_iv = kSingingIv;
|
||||
absl::SetFlag(&FLAGS_signing_provider, kSigningProvider);
|
||||
absl::SetFlag(&FLAGS_signing_key, kSingingKey);
|
||||
absl::SetFlag(&FLAGS_signing_iv, kSingingIv);
|
||||
|
||||
CHECK(
|
||||
google::protobuf::TextFormat::ParseFromString(kCasEncryptionRequest, &request_));
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
#include "media_cas_packager_sdk/public/wv_cas_types.h"
|
||||
|
||||
#include "testing/gmock.h"
|
||||
#include "testing/gunit.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
// Widevine MediaCAS ECMG server.
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
@@ -19,54 +20,37 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "gflags/gflags.h"
|
||||
#include <cstdint>
|
||||
#include "glog/logging.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/flags/flag.h"
|
||||
#include "absl/flags/parse.h"
|
||||
#include "media_cas_packager_sdk/internal/ecmg_client_handler.h"
|
||||
#include "media_cas_packager_sdk/public/wv_cas_types.h"
|
||||
|
||||
static constexpr int32_t kDefaultDelayStart = 200;
|
||||
static constexpr int32_t kDefaultDelayStop = 200;
|
||||
static constexpr int32_t kDefaultEcmRepPeriod = 100;
|
||||
static constexpr int32_t kDefaultMaxCompTime = 100;
|
||||
static constexpr int32_t kAccessCriteriaTransferMode = 0;
|
||||
static constexpr int32_t kNumberOfContentKeys = 2;
|
||||
static constexpr char kDefaultCryptoMode[] = "AesCtr";
|
||||
static constexpr bool kDefaultUseFixedFetcher = false;
|
||||
|
||||
DEFINE_int32(port, 0, "Server port number");
|
||||
|
||||
ABSL_FLAG(int32_t, port, 1234, "Server port number.");
|
||||
// ECMG related flags.
|
||||
// TODO(user): Consider adding flags 'ac_delay_start', 'ac_delay_stop',
|
||||
// 'transition_delay_start', 'transition_delay_stop'.
|
||||
DEFINE_int32(
|
||||
delay_start, kDefaultDelayStart,
|
||||
"This flag sets the DVB SimulCrypt delay_start parameter, in milliseconds");
|
||||
DEFINE_int32(
|
||||
delay_stop, kDefaultDelayStop,
|
||||
"This flag sets the DVB SimulCrypt delay_stop parameter, in milliseconds");
|
||||
DEFINE_int32(
|
||||
ecm_rep_period, kDefaultEcmRepPeriod,
|
||||
"It sets the DVB SimulCrypt parameter ECM_rep_period, in milliseconds");
|
||||
DEFINE_int32(
|
||||
max_comp_time, kDefaultMaxCompTime,
|
||||
"It sets the DVB SimulCrypt parameter max_comp_time, in milliseconds.");
|
||||
DEFINE_int32(
|
||||
access_criteria_transfer_mode, kAccessCriteriaTransferMode,
|
||||
ABSL_FLAG(int32_t, delay_start, 200, "delay_start, in milliseconds.");
|
||||
ABSL_FLAG(int32_t, delay_stop, 200, "delay_stop, in milliseconds.");
|
||||
ABSL_FLAG(int32_t, ecm_rep_period, 100, "ECM_rep_period, in milliseconds.");
|
||||
ABSL_FLAG(int32_t, max_comp_time, 100, "max_comp_time, in milliseconds.");
|
||||
ABSL_FLAG(
|
||||
int32_t, access_criteria_transfer_mode, 0,
|
||||
"If it equals 0, it indicates that the access_criteria parameter is "
|
||||
"required in the CW_provision message only when the contents of this "
|
||||
"parameter change. If it equals 1, it indicates that the ECMG requires the "
|
||||
"access_criteria parameter be present in each CW_provision message.");
|
||||
DEFINE_int32(number_of_content_keys, kNumberOfContentKeys,
|
||||
"It sets the number of content keys (CwPerMsg). Must be 1 (single "
|
||||
"key) or 2 (key rotation enabled). It also sets LeadCw as "
|
||||
"number_of_content_keys - 1.");
|
||||
DEFINE_string(crypto_mode, kDefaultCryptoMode,
|
||||
"Encryption mode. Choices are \"AesCtr\", \"AesCbc\", "
|
||||
"\"DvbCsa2\", \"DvbCsa3\", \"AesOfb\", \"AesScte\".");
|
||||
DEFINE_bool(use_fixed_fetcher, kDefaultUseFixedFetcher,
|
||||
"Use fixed fetcher to fetch mocked entitlement licenses for "
|
||||
"testing purposes.");
|
||||
ABSL_FLAG(int32_t, number_of_content_keys, 2,
|
||||
"It sets the number of content keys (CwPerMsg). Must be 1 (single "
|
||||
"key) or 2 (key rotation enabled). It also sets LeadCw as "
|
||||
"number_of_content_keys - 1.");
|
||||
ABSL_FLAG(std::string, crypto_mode, "AesCtr",
|
||||
"Encryption mode. Choices are \"AesCtr\", \"AesCbc\", "
|
||||
"\"DvbCsa2\", \"DvbCsa3\", \"AesOfb\", \"AesScte\".");
|
||||
ABSL_FLAG(bool, use_fixed_fetcher, false,
|
||||
"If set, use fixed fetcher to fetch mocked entitlement licenses when "
|
||||
"needed for testing purposes.");
|
||||
|
||||
#define LISTEN_QUEUE_SIZE (20)
|
||||
#define BUFFER_SIZE (1024)
|
||||
@@ -76,15 +60,20 @@ using widevine::cas::EcmgConfig;
|
||||
|
||||
void BuildEcmgConfig(EcmgConfig* config) {
|
||||
DCHECK(config);
|
||||
config->delay_start = FLAGS_delay_start;
|
||||
config->delay_stop = FLAGS_delay_stop;
|
||||
config->ecm_rep_period = FLAGS_ecm_rep_period;
|
||||
config->max_comp_time = FLAGS_max_comp_time;
|
||||
config->access_criteria_transfer_mode = FLAGS_access_criteria_transfer_mode;
|
||||
config->number_of_content_keys = FLAGS_number_of_content_keys;
|
||||
CHECK(StringToCryptoMode(FLAGS_crypto_mode, &config->crypto_mode))
|
||||
config->delay_start = absl::GetFlag(FLAGS_delay_start);
|
||||
config->delay_stop = absl::GetFlag(FLAGS_delay_stop);
|
||||
config->ecm_rep_period = absl::GetFlag(FLAGS_ecm_rep_period);
|
||||
config->max_comp_time = absl::GetFlag(FLAGS_max_comp_time);
|
||||
config->access_criteria_transfer_mode =
|
||||
absl::GetFlag(FLAGS_access_criteria_transfer_mode);
|
||||
CHECK(absl::GetFlag(FLAGS_number_of_content_keys) == 1 ||
|
||||
absl::GetFlag(FLAGS_number_of_content_keys) == 2)
|
||||
<< "--number_of_content_keys must be 1 or 2.";
|
||||
config->number_of_content_keys = absl::GetFlag(FLAGS_number_of_content_keys);
|
||||
CHECK(StringToCryptoMode(absl::GetFlag(FLAGS_crypto_mode),
|
||||
&config->crypto_mode))
|
||||
<< "Unknown crypto mode.";
|
||||
config->use_fixed_fetcher = FLAGS_use_fixed_fetcher;
|
||||
config->use_fixed_fetcher = absl::GetFlag(FLAGS_use_fixed_fetcher);
|
||||
}
|
||||
|
||||
void PrintMessage(const std::string& description, const char* const message,
|
||||
@@ -126,11 +115,7 @@ void ServeClient(int socket_fd, EcmgClientHandler* ecmg) {
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||
CHECK(FLAGS_port != 0) << "need --port";
|
||||
CHECK(FLAGS_number_of_content_keys == 1 || FLAGS_number_of_content_keys == 2)
|
||||
<< "--number_of_content_keys must be 1 or 2.";
|
||||
|
||||
absl::ParseCommandLine(argc, argv);
|
||||
EcmgConfig ecmg_config;
|
||||
BuildEcmgConfig(&ecmg_config);
|
||||
|
||||
@@ -139,7 +124,7 @@ int main(int argc, char** argv) {
|
||||
bzero(reinterpret_cast<char*>(&server_address), sizeof(server_address));
|
||||
server_address.sin_family = AF_INET;
|
||||
server_address.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
server_address.sin_port = htons(FLAGS_port);
|
||||
server_address.sin_port = htons(absl::GetFlag(FLAGS_port));
|
||||
|
||||
// Create a listening socket.
|
||||
int listen_socket_fd = socket(AF_INET, SOCK_STREAM, /* protocol= */ 0);
|
||||
|
||||
@@ -12,54 +12,69 @@
|
||||
#include <netinet/in.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#include <cstring>
|
||||
|
||||
#include "gflags/gflags.h"
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#include <cstdint>
|
||||
#include "glog/logging.h"
|
||||
#include "absl/flags/flag.h"
|
||||
#include "absl/flags/parse.h"
|
||||
#include "media_cas_packager_sdk/internal/emmg.h"
|
||||
|
||||
DEFINE_string(mux_address, "", "Mux server address");
|
||||
DEFINE_int32(mux_port, 0, "Mux server port number");
|
||||
|
||||
// EMMG specfic configs.
|
||||
DEFINE_int32(client_id, 0, "EMMG client_id");
|
||||
DEFINE_int32(section_tspkt_flag, 1, "EMMG section_tspkt_flag");
|
||||
DEFINE_int32(data_channel_id, 0, "EMMG data_channel_id");
|
||||
DEFINE_int32(data_stream_id, 0, "EMMG data_stream_id");
|
||||
DEFINE_int32(data_id, 0, "EMMG data_id");
|
||||
ABSL_FLAG(std::string, mux_address, "", "Mux server address.");
|
||||
ABSL_FLAG(int32_t, mux_port, 0, "Mux server port number.");
|
||||
// EMMG specific configs.
|
||||
// To facilitate uniqueness of client_id, the following rules apply:
|
||||
// in the case of EMMs or other CA related data, the two first bytes of the
|
||||
// client_id should be equal to the two bytes of the corresponding CA_system_id.
|
||||
ABSL_FLAG(int32_t, client_id, 0x4AD40000, "EMMG client_id.");
|
||||
// TODO(user): Currently it can only support section_tspkt_flag = 1.
|
||||
ABSL_FLAG(int32_t, section_tspkt_flag, 1,
|
||||
"EMMG section_tspkt_flag. Currently it can only be set to 1.");
|
||||
ABSL_FLAG(int32_t, data_channel_id, 0, "EMMG data_channel_id.");
|
||||
ABSL_FLAG(int32_t, data_stream_id, 0, "EMMG data_stream_id.");
|
||||
ABSL_FLAG(int32_t, data_id, 0, "EMMG data_id.");
|
||||
// data_type: type of data carried in the datagram in the stream:
|
||||
// 0x00: EMM;
|
||||
// 0x01: private data;
|
||||
// 0x02: DVB reserved (ECM);
|
||||
// other values: DVB reserved.
|
||||
DEFINE_int32(data_type, 0, "EMMG data_type");
|
||||
ABSL_FLAG(int32_t, data_type, 1, "EMMG data_type");
|
||||
ABSL_FLAG(std::string, content_provider, "", "Content provider");
|
||||
ABSL_FLAG(std::string, content_id, "", "Content id");
|
||||
|
||||
#define BUFFER_SIZE (1024)
|
||||
|
||||
void BuildEmmgConfig(widevine::cas::EmmgConfig *config) {
|
||||
CHECK(config);
|
||||
config->client_id = FLAGS_client_id;
|
||||
config->section_tspkt_flag = FLAGS_section_tspkt_flag;
|
||||
config->data_channel_id = FLAGS_data_channel_id;
|
||||
config->data_stream_id = FLAGS_data_stream_id;
|
||||
config->data_id = FLAGS_data_id;
|
||||
config->data_type = FLAGS_data_type;
|
||||
config->client_id = absl::GetFlag(FLAGS_client_id);
|
||||
config->section_tspkt_flag = absl::GetFlag(FLAGS_section_tspkt_flag);
|
||||
config->data_channel_id = absl::GetFlag(FLAGS_data_channel_id);
|
||||
config->data_stream_id = absl::GetFlag(FLAGS_data_stream_id);
|
||||
config->data_id = absl::GetFlag(FLAGS_data_id);
|
||||
config->data_type = absl::GetFlag(FLAGS_data_type);
|
||||
config->content_provider = absl::GetFlag(FLAGS_content_provider);
|
||||
config->content_id = absl::GetFlag(FLAGS_content_id);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||
CHECK(!FLAGS_mux_address.empty()) << "flag --mux_address is required";
|
||||
CHECK(FLAGS_mux_port != 0) << "flag --mux_port is required";
|
||||
absl::ParseCommandLine(argc, argv);
|
||||
CHECK(!absl::GetFlag(FLAGS_mux_address).empty())
|
||||
<< "flag --mux_address is required";
|
||||
CHECK(absl::GetFlag(FLAGS_mux_port) != 0) << "flag --mux_port is required";
|
||||
|
||||
// Create server address.
|
||||
struct hostent ret;
|
||||
struct hostent *server;
|
||||
char buffer[BUFFER_SIZE];
|
||||
int h_errnop = 0;
|
||||
int return_val = gethostbyname_r(FLAGS_mux_address.c_str(), &ret, buffer,
|
||||
sizeof(buffer), &server, &h_errnop);
|
||||
int return_val =
|
||||
gethostbyname_r(absl::GetFlag(FLAGS_mux_address).c_str(), &ret, buffer,
|
||||
sizeof(buffer), &server, &h_errnop);
|
||||
if (return_val != 0 || server == nullptr) {
|
||||
LOG(FATAL) << "gethostbyname_r failed for " << FLAGS_mux_address;
|
||||
LOG(FATAL) << "gethostbyname_r failed for "
|
||||
<< absl::GetFlag(FLAGS_mux_address);
|
||||
}
|
||||
struct sockaddr_in server_address;
|
||||
bzero(reinterpret_cast<char *>(&server_address), sizeof(server_address));
|
||||
@@ -67,7 +82,7 @@ int main(int argc, char **argv) {
|
||||
bcopy(server->h_addr,
|
||||
reinterpret_cast<char *>(&server_address.sin_addr.s_addr),
|
||||
server->h_length);
|
||||
server_address.sin_port = htons(FLAGS_mux_port);
|
||||
server_address.sin_port = htons(absl::GetFlag(FLAGS_mux_port));
|
||||
|
||||
// Connect to server.
|
||||
int server_socket_fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
|
||||
Reference in New Issue
Block a user