From ac564bb46fe4bb051e45ef768063f4281f1e9087 Mon Sep 17 00:00:00 2001 From: Lu Chen Date: Wed, 5 Feb 2020 11:21:51 -0800 Subject: [PATCH] Update includes and BUILD --- common/certificate_client_cert.h | 6 + common/client_cert.h | 2 + common/core_message_util.cc | 47 +++++-- common/core_message_util.h | 31 ++-- common/device_status_list_test.cc | 2 + common/keybox_client_cert.h | 4 + common/rot_id_generator_test.cc | 9 +- common/rot_id_util.cc | 19 --- common/rot_id_util.h | 32 +++-- common/rot_id_util_test.cc | 22 +-- common/security_profile_list.h | 4 +- example/BUILD | 9 +- example/test_ecmg_messages.h | 24 ++++ example/test_emmg_messages.h | 22 +-- example/wv_cas_ecm_example.cc | 14 +- example/wv_cas_key_fetcher_example.cc | 29 ++-- example/wv_cas_types_example.cc | 1 + media_cas_packager_sdk/internal/BUILD | 25 +--- media_cas_packager_sdk/internal/ecm.cc | 5 +- media_cas_packager_sdk/internal/ecm.h | 2 +- .../internal/ecm_generator_test.cc | 3 - media_cas_packager_sdk/internal/ecm_test.cc | 6 - .../internal/ecmg_client_handler.cc | 133 ++++++++---------- .../internal/ecmg_client_handler.h | 17 +-- .../internal/ecmg_client_handler_test.cc | 80 ++++++++++- media_cas_packager_sdk/internal/emmg.cc | 51 +++++-- media_cas_packager_sdk/internal/emmg.h | 10 +- media_cas_packager_sdk/internal/emmg_test.cc | 8 ++ .../internal/fixed_key_fetcher.cc | 1 - .../internal/fixed_key_fetcher.h | 3 + media_cas_packager_sdk/internal/mpeg2ts.h | 1 - .../internal/simulcrypt_util.cc | 1 - .../internal/simulcrypt_util.h | 5 - media_cas_packager_sdk/internal/ts_packet.cc | 1 - media_cas_packager_sdk/internal/ts_packet.h | 2 - .../internal/ts_packet_test.cc | 3 - media_cas_packager_sdk/internal/util.cc | 1 + media_cas_packager_sdk/public/BUILD | 14 +- .../public/wv_cas_ca_descriptor.cc | 1 - .../public/wv_cas_ca_descriptor.h | 2 + .../public/wv_cas_ca_descriptor_test.cc | 3 - media_cas_packager_sdk/public/wv_cas_ecm.cc | 7 +- media_cas_packager_sdk/public/wv_cas_ecm.h | 6 +- .../public/wv_cas_ecm_test.cc | 4 +- .../public/wv_cas_key_fetcher.cc | 39 ++--- .../public/wv_cas_key_fetcher.h | 14 +- .../public/wv_cas_key_fetcher_test.cc | 9 +- .../public/wv_cas_types_test.cc | 1 - media_cas_packager_sdk/public/wv_ecmg.cc | 87 +++++------- media_cas_packager_sdk/public/wv_emmg.cc | 65 +++++---- 50 files changed, 510 insertions(+), 377 deletions(-) diff --git a/common/certificate_client_cert.h b/common/certificate_client_cert.h index 3c92677..5c4df86 100644 --- a/common/certificate_client_cert.h +++ b/common/certificate_client_cert.h @@ -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 algorithm_; diff --git a/common/client_cert.h b/common/client_cert.h index 14b7ba5..50aace3 100644 --- a/common/client_cert.h +++ b/common/client_cert.h @@ -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 diff --git a/common/core_message_util.cc b/common/core_message_util.cc index 21ec6ae..6c66a87 100644 --- a/common/core_message_util.cc +++ b/common/core_message_util.cc @@ -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 diff --git a/common/core_message_util.h b/common/core_message_util.h index f48cfa4..e6196b6 100644 --- a/common/core_message_util.h +++ b/common/core_message_util.h @@ -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 diff --git a/common/device_status_list_test.cc b/common/device_status_list_test.cc index 40ad55b..1746125 100644 --- a/common/device_status_list_test.cc +++ b/common/device_status_list_test.cc @@ -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, diff --git a/common/keybox_client_cert.h b/common/keybox_client_cert.h index 25d5391..fcdd50c 100644 --- a/common/keybox_client_cert.h +++ b/common/keybox_client_cert.h @@ -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. diff --git a/common/rot_id_generator_test.cc b/common/rot_id_generator_test.cc index 138dee9..91956b0 100644 --- a/common/rot_id_generator_test.cc +++ b/common/rot_id_generator_test.cc @@ -11,6 +11,7 @@ #include "common/rot_id_generator.h" #include +#include #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>( + 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>( second_root_of_trust_id.encrypted_unique_id(), kTestSystemId, second_root_of_trust_id.unique_id_hash(), {unique_id_hash})); } diff --git a/common/rot_id_util.cc b/common/rot_id_util.cc index aba65e5..f994baa 100644 --- a/common/rot_id_util.cc +++ b/common/rot_id_util.cc @@ -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& 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()) { diff --git a/common/rot_id_util.h b/common/rot_id_util.h index ca1fe5a..49a55dc 100644 --- a/common/rot_id_util.h +++ b/common/rot_id_util.h @@ -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& 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 +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_ diff --git a/common/rot_id_util_test.cc b/common/rot_id_util_test.cc index 050bed0..51bf6df 100644 --- a/common/rot_id_util_test.cc +++ b/common/rot_id_util_test.cc @@ -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>( + 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>( + kFakeEncryptedId, kOtherFakeSystemId, + absl::HexStringToBytes(kRotIdHashHex), + {"NO MATCH UNIQUE ID HASH 1", kFakeUniqueIdHash})); } TEST(RotIdUtilTest, IsRotIdRevokedNoMatch) { - ASSERT_FALSE(IsRotIdRevoked( + ASSERT_FALSE(IsRotIdRevoked>( 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>( + kFakeEncryptedId, kFakeSystemId, kFakeUniqueIdHash, + {/* Intentionally empty vector */})); } // This test really only ensures the stability of the implementation. If the diff --git a/common/security_profile_list.h b/common/security_profile_list.h index 835c0b6..352994f 100644 --- a/common/security_profile_list.h +++ b/common/security_profile_list.h @@ -86,10 +86,10 @@ class SecurityProfileList { mutable absl::Mutex mutex_; // Widevine security profiles - std::vector security_profiles_ GUARDED_BY(mutex_); + std::vector security_profiles_ ABSL_GUARDED_BY(mutex_); // Custom security profiles std::map custom_security_profiles_ - GUARDED_BY(mutex_); + ABSL_GUARDED_BY(mutex_); }; } // namespace widevine diff --git a/example/BUILD b/example/BUILD index c6f7dce..e8321bc 100644 --- a/example/BUILD +++ b/example/BUILD @@ -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"], ) diff --git a/example/test_ecmg_messages.h b/example/test_ecmg_messages.h index 3890e99..77a018e 100644 --- a/example/test_ecmg_messages.h +++ b/example/test_ecmg_messages.h @@ -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 diff --git a/example/test_emmg_messages.h b/example/test_emmg_messages.h index 8f5df6f..7fabcff 100644 --- a/example/test_emmg_messages.h +++ b/example/test_emmg_messages.h @@ -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 diff --git a/example/wv_cas_ecm_example.cc b/example/wv_cas_ecm_example.cc index ae97860..9f06fae 100644 --- a/example/wv_cas_ecm_example.cc +++ b/example/wv_cas_ecm_example.cc @@ -8,11 +8,15 @@ // Example of how to use the wv_cas_ecm library. +#include +#include + #include #include #include #include +#include #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(packet), kTsPacketSize); - file.close(); + std::ofstream file; + file.open(kOutputFile, std::ios_base::binary); + assert(file.is_open()); + file.write(reinterpret_cast(packet), kTsPacketSize); + file.close(); return 0; } diff --git a/example/wv_cas_key_fetcher_example.cc b/example/wv_cas_key_fetcher_example.cc index 40dc653..f79589c 100644 --- a/example/wv_cas_key_fetcher_example.cc +++ b/example/wv_cas_key_fetcher_example.cc @@ -7,36 +7,39 @@ //////////////////////////////////////////////////////////////////////////////// -#include #include -#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"; diff --git a/example/wv_cas_types_example.cc b/example/wv_cas_types_example.cc index c0bddda..239f391 100644 --- a/example/wv_cas_types_example.cc +++ b/example/wv_cas_types_example.cc @@ -10,6 +10,7 @@ #include #include +#include #include "media_cas_packager_sdk/public/wv_cas_types.h" diff --git a/media_cas_packager_sdk/internal/BUILD b/media_cas_packager_sdk/internal/BUILD index 803ac9d..b53a9cb 100644 --- a/media_cas_packager_sdk/internal/BUILD +++ b/media_cas_packager_sdk/internal/BUILD @@ -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", ], ) diff --git a/media_cas_packager_sdk/internal/ecm.cc b/media_cas_packager_sdk/internal/ecm.cc index a1c8290..5de63d5 100644 --- a/media_cas_packager_sdk/internal/ecm.cc +++ b/media_cas_packager_sdk/internal/ecm.cc @@ -8,14 +8,13 @@ #include "media_cas_packager_sdk/internal/ecm.h" +#include + #include -#include -#include #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" diff --git a/media_cas_packager_sdk/internal/ecm.h b/media_cas_packager_sdk/internal/ecm.h index d42afcf..ff2300b 100644 --- a/media_cas_packager_sdk/internal/ecm.h +++ b/media_cas_packager_sdk/internal/ecm.h @@ -16,9 +16,9 @@ #include #include +#include #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 { diff --git a/media_cas_packager_sdk/internal/ecm_generator_test.cc b/media_cas_packager_sdk/internal/ecm_generator_test.cc index 7e6fbde..8f2eb0a 100644 --- a/media_cas_packager_sdk/internal/ecm_generator_test.cc +++ b/media_cas_packager_sdk/internal/ecm_generator_test.cc @@ -8,9 +8,6 @@ #include "media_cas_packager_sdk/internal/ecm_generator.h" -#include -#include - #include "testing/gmock.h" #include "testing/gunit.h" #include "absl/memory/memory.h" diff --git a/media_cas_packager_sdk/internal/ecm_test.cc b/media_cas_packager_sdk/internal/ecm_test.cc index 098c21d..7773384 100644 --- a/media_cas_packager_sdk/internal/ecm_test.cc +++ b/media_cas_packager_sdk/internal/ecm_test.cc @@ -8,14 +8,8 @@ #include "media_cas_packager_sdk/internal/ecm.h" -#include - -#include - #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; diff --git a/media_cas_packager_sdk/internal/ecmg_client_handler.cc b/media_cas_packager_sdk/internal/ecmg_client_handler.cc index 7db3f59..78d9bc8 100644 --- a/media_cas_packager_sdk/internal/ecmg_client_handler.cc +++ b/media_cas_packager_sdk/internal/ecmg_client_handler.cc @@ -8,22 +8,13 @@ #include "media_cas_packager_sdk/internal/ecmg_client_handler.h" -#include -#include #include -#include -#include -#include -#include #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(); 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(); } diff --git a/media_cas_packager_sdk/internal/ecmg_client_handler.h b/media_cas_packager_sdk/internal/ecmg_client_handler.h index d967062..e5f0261 100644 --- a/media_cas_packager_sdk/internal/ecmg_client_handler.h +++ b/media_cas_packager_sdk/internal/ecmg_client_handler.h @@ -10,13 +10,8 @@ #define MEDIA_CAS_PACKAGER_SDK_INTERNAL_ECMG_CLIENT_HANDLER_H_ #include -#include -#include -#include #include #include -#include -#include #include #include @@ -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); diff --git a/media_cas_packager_sdk/internal/ecmg_client_handler_test.cc b/media_cas_packager_sdk/internal/ecmg_client_handler_test.cc index 535744a..263cac0 100644 --- a/media_cas_packager_sdk/internal/ecmg_client_handler_test.cc +++ b/media_cas_packager_sdk/internal/ecmg_client_handler_test.cc @@ -8,9 +8,10 @@ #include "media_cas_packager_sdk/internal/ecmg_client_handler.h" -#include +#include +#include +#include -#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 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 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 diff --git a/media_cas_packager_sdk/internal/emmg.cc b/media_cas_packager_sdk/internal/emmg.cc index 6c15478..55be4cd 100644 --- a/media_cas_packager_sdk/internal/emmg.cc +++ b/media_cas_packager_sdk/internal/emmg.cc @@ -10,18 +10,19 @@ #include -#include -#include #include #include #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); diff --git a/media_cas_packager_sdk/internal/emmg.h b/media_cas_packager_sdk/internal/emmg.h index 5dd131c..64e65a6 100644 --- a/media_cas_packager_sdk/internal/emmg.h +++ b/media_cas_packager_sdk/internal/emmg.h @@ -10,13 +10,9 @@ #define MEDIA_CAS_PACKAGER_SDK_INTERNAL_EMMG_H_ #include -#include #include -#include -#include #include -#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 diff --git a/media_cas_packager_sdk/internal/emmg_test.cc b/media_cas_packager_sdk/internal/emmg_test.cc index c51ef87..7ef212a 100644 --- a/media_cas_packager_sdk/internal/emmg_test.cc +++ b/media_cas_packager_sdk/internal/emmg_test.cc @@ -8,6 +8,12 @@ #include "media_cas_packager_sdk/internal/emmg.h" +#include +#include +#include + +#include + #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(&config_); } diff --git a/media_cas_packager_sdk/internal/fixed_key_fetcher.cc b/media_cas_packager_sdk/internal/fixed_key_fetcher.cc index bdc7641..ee24111 100644 --- a/media_cas_packager_sdk/internal/fixed_key_fetcher.cc +++ b/media_cas_packager_sdk/internal/fixed_key_fetcher.cc @@ -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 { diff --git a/media_cas_packager_sdk/internal/fixed_key_fetcher.h b/media_cas_packager_sdk/internal/fixed_key_fetcher.h index 32fd071..b8a62da 100644 --- a/media_cas_packager_sdk/internal/fixed_key_fetcher.h +++ b/media_cas_packager_sdk/internal/fixed_key_fetcher.h @@ -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 + +#include "common/status.h" #include "media_cas_packager_sdk/internal/key_fetcher.h" namespace widevine { diff --git a/media_cas_packager_sdk/internal/mpeg2ts.h b/media_cas_packager_sdk/internal/mpeg2ts.h index ca7fa7c..6ac5cf2 100644 --- a/media_cas_packager_sdk/internal/mpeg2ts.h +++ b/media_cas_packager_sdk/internal/mpeg2ts.h @@ -13,7 +13,6 @@ #include #include -#include "base/macros.h" namespace widevine { namespace cas { diff --git a/media_cas_packager_sdk/internal/simulcrypt_util.cc b/media_cas_packager_sdk/internal/simulcrypt_util.cc index 5263d6d..6fdc823 100644 --- a/media_cas_packager_sdk/internal/simulcrypt_util.cc +++ b/media_cas_packager_sdk/internal/simulcrypt_util.cc @@ -8,7 +8,6 @@ #include "media_cas_packager_sdk/internal/simulcrypt_util.h" -#include #include #include "glog/logging.h" diff --git a/media_cas_packager_sdk/internal/simulcrypt_util.h b/media_cas_packager_sdk/internal/simulcrypt_util.h index 649723f..4dcf3f8 100644 --- a/media_cas_packager_sdk/internal/simulcrypt_util.h +++ b/media_cas_packager_sdk/internal/simulcrypt_util.h @@ -12,13 +12,8 @@ #define MEDIA_CAS_PACKAGER_SDK_INTERNAL_SIMULCRYPT_UTIL_H_ #include -#include -#include -#include -#include #include -#include "common/status.h" namespace widevine { namespace cas { diff --git a/media_cas_packager_sdk/internal/ts_packet.cc b/media_cas_packager_sdk/internal/ts_packet.cc index 1db9c47..a0122ab 100644 --- a/media_cas_packager_sdk/internal/ts_packet.cc +++ b/media_cas_packager_sdk/internal/ts_packet.cc @@ -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 { diff --git a/media_cas_packager_sdk/internal/ts_packet.h b/media_cas_packager_sdk/internal/ts_packet.h index b7f0cbd..b3f3919 100644 --- a/media_cas_packager_sdk/internal/ts_packet.h +++ b/media_cas_packager_sdk/internal/ts_packet.h @@ -33,11 +33,9 @@ #ifndef MEDIA_CAS_PACKAGER_SDK_INTERNAL_TS_PACKET_H_ #define MEDIA_CAS_PACKAGER_SDK_INTERNAL_TS_PACKET_H_ -#include #include #include -#include "base/macros.h" #include "absl/strings/string_view.h" #include "common/status.h" #include "media_cas_packager_sdk/internal/mpeg2ts.h" diff --git a/media_cas_packager_sdk/internal/ts_packet_test.cc b/media_cas_packager_sdk/internal/ts_packet_test.cc index 68e1037..ae43b4b 100644 --- a/media_cas_packager_sdk/internal/ts_packet_test.cc +++ b/media_cas_packager_sdk/internal/ts_packet_test.cc @@ -8,12 +8,9 @@ #include "media_cas_packager_sdk/internal/ts_packet.h" -#include - #include "base/macros.h" #include "testing/gmock.h" #include "testing/gunit.h" -#include "media_cas_packager_sdk/internal/mpeg2ts.h" namespace widevine { namespace cas { diff --git a/media_cas_packager_sdk/internal/util.cc b/media_cas_packager_sdk/internal/util.cc index c844fb1..b7c4803 100644 --- a/media_cas_packager_sdk/internal/util.cc +++ b/media_cas_packager_sdk/internal/util.cc @@ -9,6 +9,7 @@ #include "media_cas_packager_sdk/internal/util.h" #include + #include #include diff --git a/media_cas_packager_sdk/public/BUILD b/media_cas_packager_sdk/public/BUILD index 58b7857..b299c26 100644 --- a/media_cas_packager_sdk/public/BUILD +++ b/media_cas_packager_sdk/public/BUILD @@ -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", ], ) diff --git a/media_cas_packager_sdk/public/wv_cas_ca_descriptor.cc b/media_cas_packager_sdk/public/wv_cas_ca_descriptor.cc index 6a60cc7..31a5cf3 100644 --- a/media_cas_packager_sdk/public/wv_cas_ca_descriptor.cc +++ b/media_cas_packager_sdk/public/wv_cas_ca_descriptor.cc @@ -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 { diff --git a/media_cas_packager_sdk/public/wv_cas_ca_descriptor.h b/media_cas_packager_sdk/public/wv_cas_ca_descriptor.h index ad62229..7323d56 100644 --- a/media_cas_packager_sdk/public/wv_cas_ca_descriptor.h +++ b/media_cas_packager_sdk/public/wv_cas_ca_descriptor.h @@ -10,8 +10,10 @@ #define MEDIA_CAS_PACKAGER_SDK_PUBLIC_WV_CAS_CA_DESCRIPTOR_H_ #include + #include +#include #include "media_cas_packager_sdk/public/wv_cas_types.h" namespace widevine { diff --git a/media_cas_packager_sdk/public/wv_cas_ca_descriptor_test.cc b/media_cas_packager_sdk/public/wv_cas_ca_descriptor_test.cc index 71037d5..e0244d6 100644 --- a/media_cas_packager_sdk/public/wv_cas_ca_descriptor_test.cc +++ b/media_cas_packager_sdk/public/wv_cas_ca_descriptor_test.cc @@ -8,10 +8,7 @@ #include "media_cas_packager_sdk/public/wv_cas_ca_descriptor.h" -#include - #include "testing/gunit.h" -#include "media_cas_packager_sdk/public/wv_cas_types.h" #include "protos/public/media_cas.pb.h" using ::testing::Test; diff --git a/media_cas_packager_sdk/public/wv_cas_ecm.cc b/media_cas_packager_sdk/public/wv_cas_ecm.cc index 16579a3..6b8c8dc 100644 --- a/media_cas_packager_sdk/public/wv_cas_ecm.cc +++ b/media_cas_packager_sdk/public/wv_cas_ecm.cc @@ -8,13 +8,15 @@ #include "media_cas_packager_sdk/public/wv_cas_ecm.h" +#include +#include +#include + #include -#include #include #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 { diff --git a/media_cas_packager_sdk/public/wv_cas_ecm.h b/media_cas_packager_sdk/public/wv_cas_ecm.h index 00f6ff1..fafc608 100644 --- a/media_cas_packager_sdk/public/wv_cas_ecm.h +++ b/media_cas_packager_sdk/public/wv_cas_ecm.h @@ -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 -#include -#include #include -#include -#include +#include #include "media_cas_packager_sdk/public/wv_cas_types.h" namespace widevine { diff --git a/media_cas_packager_sdk/public/wv_cas_ecm_test.cc b/media_cas_packager_sdk/public/wv_cas_ecm_test.cc index d7dc64a..9fb0327 100644 --- a/media_cas_packager_sdk/public/wv_cas_ecm_test.cc +++ b/media_cas_packager_sdk/public/wv_cas_ecm_test.cc @@ -8,11 +8,11 @@ #include "media_cas_packager_sdk/public/wv_cas_ecm.h" +#include + #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; diff --git a/media_cas_packager_sdk/public/wv_cas_key_fetcher.cc b/media_cas_packager_sdk/public/wv_cas_key_fetcher.cc index 8960a70..6b9e7fb 100644 --- a/media_cas_packager_sdk/public/wv_cas_key_fetcher.cc +++ b/media_cas_packager_sdk/public/wv_cas_key_fetcher.cc @@ -10,32 +10,32 @@ #include #include -#include -#include "gflags/gflags.h" +#include #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); diff --git a/media_cas_packager_sdk/public/wv_cas_key_fetcher.h b/media_cas_packager_sdk/public/wv_cas_key_fetcher.h index 4ae15ec..815c93d 100644 --- a/media_cas_packager_sdk/public/wv_cas_key_fetcher.h +++ b/media_cas_packager_sdk/public/wv_cas_key_fetcher.h @@ -11,14 +11,10 @@ #include -#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_ diff --git a/media_cas_packager_sdk/public/wv_cas_key_fetcher_test.cc b/media_cas_packager_sdk/public/wv_cas_key_fetcher_test.cc index b1445d8..8cb55c5 100644 --- a/media_cas_packager_sdk/public/wv_cas_key_fetcher_test.cc +++ b/media_cas_packager_sdk/public/wv_cas_key_fetcher_test.cc @@ -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_)); diff --git a/media_cas_packager_sdk/public/wv_cas_types_test.cc b/media_cas_packager_sdk/public/wv_cas_types_test.cc index 31bb873..00078e1 100644 --- a/media_cas_packager_sdk/public/wv_cas_types_test.cc +++ b/media_cas_packager_sdk/public/wv_cas_types_test.cc @@ -8,7 +8,6 @@ #include "media_cas_packager_sdk/public/wv_cas_types.h" -#include "testing/gmock.h" #include "testing/gunit.h" namespace widevine { diff --git a/media_cas_packager_sdk/public/wv_ecmg.cc b/media_cas_packager_sdk/public/wv_ecmg.cc index fa88faf..a20d4ad 100644 --- a/media_cas_packager_sdk/public/wv_ecmg.cc +++ b/media_cas_packager_sdk/public/wv_ecmg.cc @@ -9,6 +9,7 @@ // Widevine MediaCAS ECMG server. #include +#include #include #include @@ -19,54 +20,37 @@ #include #include -#include "gflags/gflags.h" +#include #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(&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); diff --git a/media_cas_packager_sdk/public/wv_emmg.cc b/media_cas_packager_sdk/public/wv_emmg.cc index 6d0dad6..16ba392 100644 --- a/media_cas_packager_sdk/public/wv_emmg.cc +++ b/media_cas_packager_sdk/public/wv_emmg.cc @@ -12,54 +12,69 @@ #include #include #include -#include -#include "gflags/gflags.h" +#include +#include + +#include #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(&server_address), sizeof(server_address)); @@ -67,7 +82,7 @@ int main(int argc, char **argv) { bcopy(server->h_addr, reinterpret_cast(&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);