Update includes and BUILD

This commit is contained in:
Lu Chen
2020-02-05 11:21:51 -08:00
parent 5c42bf9b7f
commit ac564bb46f
50 changed files with 510 additions and 377 deletions

View File

@@ -80,6 +80,12 @@ class CertificateClientCert : public ClientCert {
widevine::ClientIdentification::TokenType type() const override { widevine::ClientIdentification::TokenType type() const override {
return ClientIdentification::DRM_DEVICE_CERTIFICATE; 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: private:
std::unique_ptr<ClientCertAlgorithm> algorithm_; std::unique_ptr<ClientCertAlgorithm> algorithm_;

View File

@@ -68,6 +68,8 @@ class ClientCert {
virtual bool signed_by_provisioner() const = 0; virtual bool signed_by_provisioner() const = 0;
virtual uint32_t system_id() const = 0; virtual uint32_t system_id() const = 0;
virtual widevine::ClientIdentification::TokenType type() 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 } // namespace widevine

View File

@@ -22,29 +22,50 @@ using oemcrypto_core_message::serialize::
using oemcrypto_core_message::serialize::CreateCoreRenewalResponse; using oemcrypto_core_message::serialize::CreateCoreRenewalResponse;
using widevine::Sha256_Hash; using widevine::Sha256_Hash;
// TODO(user): Check the Core*RequestFromMessage and
// CreateCore*ResponseFromProto return value when b/148472911 is fixed.
namespace widevine { namespace widevine {
namespace core_message_util { namespace core_message_util {
void GetCoreProvisioningResponse(const std::string& provisioning_response, bool GetCoreProvisioningResponse(
std::string* core_message) { 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; oemcrypto_core_message::ODK_ProvisioningRequest odk_provisioning_request;
CoreProvisioningRequestFromMessage(*core_message, &odk_provisioning_request); CoreProvisioningRequestFromMessage(request_core_message,
CreateCoreProvisioningResponseFromProto( &odk_provisioning_request);
provisioning_response, odk_provisioning_request, core_message); 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; oemcrypto_core_message::ODK_RenewalRequest odk_renewal_request;
CoreRenewalRequestFromMessage(*core_message, &odk_renewal_request); CoreRenewalRequestFromMessage(request_core_message, &odk_renewal_request);
CreateCoreRenewalResponse(odk_renewal_request, core_message); // 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, bool GetCoreNewLicenseResponse(const std::string& license,
std::string* core_message) { const std::string& request_core_message,
const bool nonce_required,
std::string* response_core_message) {
oemcrypto_core_message::ODK_LicenseRequest odk_license_request; oemcrypto_core_message::ODK_LicenseRequest odk_license_request;
CoreLicenseRequestFromMessage(*core_message, &odk_license_request); CoreLicenseRequestFromMessage(request_core_message, &odk_license_request);
std::string core_request_sha256 = Sha256_Hash(*core_message); std::string core_request_sha256 = Sha256_Hash(request_core_message);
CreateCoreLicenseResponseFromProto(license, odk_license_request, CreateCoreLicenseResponseFromProto(license, odk_license_request,
core_request_sha256, core_message); core_request_sha256, nonce_required,
response_core_message);
return true;
} }
} // namespace core_message_util } // namespace core_message_util

View File

@@ -13,20 +13,27 @@
namespace widevine { namespace widevine {
namespace core_message_util { namespace core_message_util {
// Gets the core message from |provisioning_response|. The output is held in // Gets the |response_core_message| by parsing |request_core_message| and
// |core_message|. The provisioning response to be sent will be updated with // |serialized_provisioning_response|. The output is held in
// this |core_message|. // |response_core_message|.
void GetCoreProvisioningResponse(const std::string& provisioning_response, bool GetCoreProvisioningResponse(
std::string* core_message); 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 // Gets the |response_core_message| by parsing |request_core_message| for
// is held in |core_message|. // release and renewal response. The output is held in |response_core_message|.
void GetCoreRenewalOrReleaseLicenseResponse(std::string* 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|. // Gets the |response_core_message| by parsing |request_core_message| and
// The license to be sent will be updated with this |core_message|. // |license| for new license response. The output is held in
void GetCoreNewLicenseResponse(const std::string& license, // |response_core_message|.
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);
} // namespace core_message_util } // namespace core_message_util
} // namespace widevine } // namespace widevine

View File

@@ -78,6 +78,8 @@ class MockClientCert : public ClientCert {
MOCK_CONST_METHOD0(signer_serial_number, std::string &()); MOCK_CONST_METHOD0(signer_serial_number, std::string &());
MOCK_CONST_METHOD0(signer_creation_time_seconds, uint32_t()); MOCK_CONST_METHOD0(signer_creation_time_seconds, uint32_t());
MOCK_CONST_METHOD0(type, ClientIdentification::TokenType()); 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_METHOD0(signed_by_provisioner, bool());
MOCK_CONST_METHOD3(VerifySignature, Status(const std::string &message, MOCK_CONST_METHOD3(VerifySignature, Status(const std::string &message,
const std::string &signature, const std::string &signature,

View File

@@ -43,6 +43,10 @@ class KeyboxClientCert : public ClientCert {
widevine::ClientIdentification::TokenType type() const override { widevine::ClientIdentification::TokenType type() const override {
return ClientIdentification::KEYBOX; 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 // Set the system-wide pre-provisioning keys; argument must be human-readable
// hex digits. // hex digits.

View File

@@ -11,6 +11,7 @@
#include "common/rot_id_generator.h" #include "common/rot_id_generator.h"
#include <memory> #include <memory>
#include <vector>
#include "google/protobuf/util/message_differencer.h" #include "google/protobuf/util/message_differencer.h"
#include "testing/gmock.h" #include "testing/gmock.h"
@@ -109,9 +110,9 @@ TEST_F(RootOfTrustIdGeneratorTest, GenerateIdSuccess) {
// Verify hashed unique id matches. // Verify hashed unique id matches.
std::string unique_id_hash = generator.GenerateUniqueIdHash(kTestUniqueId); std::string unique_id_hash = generator.GenerateUniqueIdHash(kTestUniqueId);
EXPECT_TRUE(IsRotIdRevoked(root_of_trust_id.encrypted_unique_id(), EXPECT_TRUE(IsRotIdRevoked<std::vector<std::string>>(
kTestSystemId, root_of_trust_id.unique_id_hash(), root_of_trust_id.encrypted_unique_id(), kTestSystemId,
{unique_id_hash})); root_of_trust_id.unique_id_hash(), {unique_id_hash}));
} }
TEST_F(RootOfTrustIdGeneratorTest, GenerateIdUniqueSuccess) { TEST_F(RootOfTrustIdGeneratorTest, GenerateIdUniqueSuccess) {
@@ -156,7 +157,7 @@ TEST_F(RootOfTrustIdGeneratorTest, GenerateIdUniqueSuccess) {
// Verify hashed unique id matches. // Verify hashed unique id matches.
std::string unique_id_hash = generator.GenerateUniqueIdHash(kTestUniqueId); 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.encrypted_unique_id(), kTestSystemId,
second_root_of_trust_id.unique_id_hash(), {unique_id_hash})); second_root_of_trust_id.unique_id_hash(), {unique_id_hash}));
} }

View File

@@ -21,25 +21,6 @@
namespace widevine { 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, std::string GenerateRotIdHash(const std::string& salt, uint32_t system_id,
const std::string& unique_id_hash) { const std::string& unique_id_hash) {
if (salt.empty() || unique_id_hash.empty()) { if (salt.empty() || unique_id_hash.empty()) {

View File

@@ -21,15 +21,6 @@
namespace widevine { 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 // 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 // |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 // 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, std::string GenerateRotIdHash(const std::string& salt, uint32_t system_id,
const std::string& unique_id_hash); 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 } // namespace widevine
#endif // COMMON_ROT_ID_UTIL_H_ #endif // COMMON_ROT_ID_UTIL_H_

View File

@@ -31,28 +31,28 @@ constexpr uint32_t kOtherFakeSystemId = 9876;
namespace widevine { namespace widevine {
TEST(RotIdUtilTest, IsRotIdRevokedMatches) { TEST(RotIdUtilTest, IsRotIdRevokedMatches) {
ASSERT_TRUE(IsRotIdRevoked(kFakeEncryptedId, kFakeSystemId, ASSERT_TRUE(IsRotIdRevoked<std::vector<std::string>>(
absl::HexStringToBytes(kRotIdHashHex), kFakeEncryptedId, kFakeSystemId, absl::HexStringToBytes(kRotIdHashHex),
{"NO MATCH UNIQUE ID HASH 1", kFakeUniqueIdHash})); {"NO MATCH UNIQUE ID HASH 1", kFakeUniqueIdHash}));
} }
TEST(RotIdUtilTest, IsRotIdRevokedNoMatchSystemId) { TEST(RotIdUtilTest, IsRotIdRevokedNoMatchSystemId) {
ASSERT_FALSE( ASSERT_FALSE(IsRotIdRevoked<std::vector<std::string>>(
IsRotIdRevoked(kFakeEncryptedId, kOtherFakeSystemId, kFakeEncryptedId, kOtherFakeSystemId,
absl::HexStringToBytes(kRotIdHashHex), absl::HexStringToBytes(kRotIdHashHex),
{"NO MATCH UNIQUE ID HASH 1", kFakeUniqueIdHash})); {"NO MATCH UNIQUE ID HASH 1", kFakeUniqueIdHash}));
} }
TEST(RotIdUtilTest, IsRotIdRevokedNoMatch) { TEST(RotIdUtilTest, IsRotIdRevokedNoMatch) {
ASSERT_FALSE(IsRotIdRevoked( ASSERT_FALSE(IsRotIdRevoked<std::vector<std::string>>(
kFakeEncryptedId, kFakeSystemId, kFakeUniqueIdHash, kFakeEncryptedId, kFakeSystemId, kFakeUniqueIdHash,
{"NO MATCH UNIQUE ID HASH 1", "NO MATCH UNIQUE ID HASH 2"})); {"NO MATCH UNIQUE ID HASH 1", "NO MATCH UNIQUE ID HASH 2"}));
} }
TEST(RotIdUtilTest, IsRotIdRevokedEmptyList) { TEST(RotIdUtilTest, IsRotIdRevokedEmptyList) {
ASSERT_FALSE(IsRotIdRevoked(kFakeEncryptedId, kFakeSystemId, ASSERT_FALSE(IsRotIdRevoked<std::vector<std::string>>(
kFakeUniqueIdHash, kFakeEncryptedId, kFakeSystemId, kFakeUniqueIdHash,
{/* Intentionally empty vector */})); {/* Intentionally empty vector */}));
} }
// This test really only ensures the stability of the implementation. If the // This test really only ensures the stability of the implementation. If the

View File

@@ -86,10 +86,10 @@ class SecurityProfileList {
mutable absl::Mutex mutex_; mutable absl::Mutex mutex_;
// Widevine security profiles // Widevine security profiles
std::vector<SecurityProfile> security_profiles_ GUARDED_BY(mutex_); std::vector<SecurityProfile> security_profiles_ ABSL_GUARDED_BY(mutex_);
// Custom security profiles // Custom security profiles
std::map<std::string, SecurityProfile> custom_security_profiles_ std::map<std::string, SecurityProfile> custom_security_profiles_
GUARDED_BY(mutex_); ABSL_GUARDED_BY(mutex_);
}; };
} // namespace widevine } // namespace widevine

View File

@@ -46,7 +46,6 @@ cc_binary(
srcs = ["wv_cas_ecm_example.cc"], srcs = ["wv_cas_ecm_example.cc"],
deps = [ deps = [
"//base", "//base",
"@abseil_repo//absl/base:core_headers",
"//media_cas_packager_sdk/public:wv_cas_ecm", "//media_cas_packager_sdk/public:wv_cas_ecm",
"//media_cas_packager_sdk/public:wv_cas_types", "//media_cas_packager_sdk/public:wv_cas_types",
], ],
@@ -57,6 +56,8 @@ cc_binary(
srcs = ["wv_cas_key_fetcher_example.cc"], srcs = ["wv_cas_key_fetcher_example.cc"],
deps = [ deps = [
"//base", "//base",
"@abseil_repo//absl/flags:flag",
"@abseil_repo//absl/flags:parse",
"//common:status", "//common:status",
"//media_cas_packager_sdk/public:wv_cas_key_fetcher", "//media_cas_packager_sdk/public:wv_cas_key_fetcher",
"//protos/public:media_cas_encryption_cc_proto", "//protos/public:media_cas_encryption_cc_proto",
@@ -66,9 +67,5 @@ cc_binary(
cc_binary( cc_binary(
name = "wv_cas_types_example", name = "wv_cas_types_example",
srcs = ["wv_cas_types_example.cc"], srcs = ["wv_cas_types_example.cc"],
deps = [ deps = ["//media_cas_packager_sdk/public:wv_cas_types"],
"//base",
"@abseil_repo//absl/base:core_headers",
"//media_cas_packager_sdk/public:wv_cas_types",
],
) )

View File

@@ -384,6 +384,30 @@ constexpr char kTestStreamErrorResponse[] = {
'\x00', '\x00' // parameter_value: actual value varies. '\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 cas
} // namespace widevine } // namespace widevine

View File

@@ -91,7 +91,10 @@ const char kTestEmmgDataProvision[] = {
'\x00', '\x01', // parameter_value '\x00', '\x01', // parameter_value
'\x00', '\x05', // parameter_type - datagram '\x00', '\x05', // parameter_type - datagram
'\x00', '\xbc', // parameter_length '\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', '\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', '\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', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
'\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
'\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
'\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
'\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
'\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
'\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00'};
'\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'};
const char kTestEmmgStreamCloseRequest[] = { const char kTestEmmgStreamCloseRequest[] = {
'\x02', // protocol_version '\x02', // protocol_version

View File

@@ -8,11 +8,15 @@
// Example of how to use the wv_cas_ecm library. // Example of how to use the wv_cas_ecm library.
#include <stddef.h>
#include <stdio.h>
#include <cassert> #include <cassert>
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <string> #include <string>
#include <cstdint>
#include "media_cas_packager_sdk/public/wv_cas_ecm.h" #include "media_cas_packager_sdk/public/wv_cas_ecm.h"
#include "media_cas_packager_sdk/public/wv_cas_types.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; std::cout << std::endl;
} }
// Write ECM TS Packet to a file. // Write ECM TS Packet to a file.
std::ofstream file; std::ofstream file;
file.open(kOutputFile, std::ios_base::binary); file.open(kOutputFile, std::ios_base::binary);
assert(file.is_open()); assert(file.is_open());
file.write(reinterpret_cast<char *>(packet), kTsPacketSize); file.write(reinterpret_cast<char*>(packet), kTsPacketSize);
file.close(); file.close();
return 0; return 0;
} }

View File

@@ -7,36 +7,39 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include <iostream>
#include <string> #include <string>
#include "gflags/gflags.h"
#include "glog/logging.h" #include "glog/logging.h"
#include "absl/flags/flag.h"
#include "absl/flags/parse.h"
#include "common/status.h" #include "common/status.h"
#include "media_cas_packager_sdk/public/wv_cas_key_fetcher.h" #include "media_cas_packager_sdk/public/wv_cas_key_fetcher.h"
#include "protos/public/media_cas_encryption.pb.h" #include "protos/public/media_cas_encryption.pb.h"
DEFINE_string(content_id, "21140844", "Content ID"); ABSL_FLAG(std::string, content_id, "21140844", "Content ID");
DEFINE_bool(key_rotation, true, "Whether key rotation is enabled"); ABSL_FLAG(bool, key_rotation, true, "Whether key rotation is enabled");
DEFINE_string(track_type, "SD", "Provider name"); ABSL_FLAG(std::string, track_type, "SD", "Provider name");
int main(int argc, char **argv) { int main(int argc, char **argv) {
gflags::ParseCommandLineFlags(&argc, &argv, true); absl::ParseCommandLine(argc, argv);
CHECK(!FLAGS_content_id.empty() && !FLAGS_track_type.empty()) CHECK(!absl::GetFlag(FLAGS_content_id).empty() &&
!absl::GetFlag(FLAGS_track_type).empty())
<< "Flags 'content_id' and 'track_type' are required"; << "Flags 'content_id' and 'track_type' are required";
// Required flags in key fetcher. // Required flags in key fetcher.
CHECK(!FLAGS_license_server.empty() && !FLAGS_signing_provider.empty() && CHECK(!absl::GetFlag(FLAGS_license_server).empty() &&
!FLAGS_signing_key.empty() && !FLAGS_signing_iv.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' " << "Flags 'license_server', 'signing_provider', 'signing_key' "
"and 'signing_iv' are required"; "and 'signing_iv' are required";
std::string request_str; std::string request_str;
widevine::CasEncryptionRequest request; widevine::CasEncryptionRequest request;
request.set_provider(FLAGS_signing_provider); request.set_provider(absl::GetFlag(FLAGS_signing_provider));
request.set_content_id(FLAGS_content_id); request.set_content_id(absl::GetFlag(FLAGS_content_id));
request.set_key_rotation(FLAGS_key_rotation); request.set_key_rotation(absl::GetFlag(FLAGS_key_rotation));
// Only 1 track in this example. // 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(); LOG(INFO) << "Request: " << request.ShortDebugString();
if (!request.SerializeToString(&request_str)) { if (!request.SerializeToString(&request_str)) {
LOG(ERROR) << "Failed to serialize request"; LOG(ERROR) << "Failed to serialize request";

View File

@@ -10,6 +10,7 @@
#include <iostream> #include <iostream>
#include <string> #include <string>
#include <vector>
#include "media_cas_packager_sdk/public/wv_cas_types.h" #include "media_cas_packager_sdk/public/wv_cas_types.h"

View File

@@ -26,13 +26,11 @@ cc_library(
hdrs = ["ecm.h"], hdrs = ["ecm.h"],
deps = [ deps = [
"//base", "//base",
"@abseil_repo//absl/base:core_headers",
"@abseil_repo//absl/strings", "@abseil_repo//absl/strings",
"//common:aes_cbc_util", "//common:aes_cbc_util",
"//common:status", "//common:status",
"//common:string_util", "//common:string_util",
"//media_cas_packager_sdk/public:wv_cas_types", "//media_cas_packager_sdk/public:wv_cas_types",
"//protos/public:media_cas_cc_proto",
"//protos/public:media_cas_encryption_cc_proto", "//protos/public:media_cas_encryption_cc_proto",
], ],
) )
@@ -44,8 +42,6 @@ cc_test(
deps = [ deps = [
":ecm", ":ecm",
"//testing:gunit_main", "//testing:gunit_main",
"//common:status",
"//media_cas_packager_sdk/public:wv_cas_types",
"//protos/public:media_cas_encryption_cc_proto", "//protos/public:media_cas_encryption_cc_proto",
], ],
) )
@@ -57,7 +53,6 @@ cc_library(
deps = [ deps = [
":ecm", ":ecm",
"//base", "//base",
"@abseil_repo//absl/base:core_headers",
"//common:status", "//common:status",
], ],
) )
@@ -91,15 +86,12 @@ cc_library(
":simulcrypt_util", ":simulcrypt_util",
":util", ":util",
"//base", "//base",
"@abseil_repo//absl/base:core_headers",
"@abseil_repo//absl/container:node_hash_map", "@abseil_repo//absl/container:node_hash_map",
"@abseil_repo//absl/memory", "@abseil_repo//absl/memory",
"@abseil_repo//absl/strings", "@abseil_repo//absl/strings",
"@abseil_repo//absl/strings:str_format",
"//common:crypto_util", "//common:crypto_util",
"//common:random_util", "//common:random_util",
"//common:status", "//common:status",
"//example:constants",
"//media_cas_packager_sdk/public:wv_cas_ecm", "//media_cas_packager_sdk/public:wv_cas_ecm",
"//media_cas_packager_sdk/public:wv_cas_key_fetcher", "//media_cas_packager_sdk/public:wv_cas_key_fetcher",
"//media_cas_packager_sdk/public:wv_cas_types", "//media_cas_packager_sdk/public:wv_cas_types",
@@ -132,12 +124,13 @@ cc_library(
], ],
deps = [ deps = [
":simulcrypt_util", ":simulcrypt_util",
":ts_packet",
":util", ":util",
"//base", "//base",
"@abseil_repo//absl/base:core_headers", "@abseil_repo//absl/strings",
"@abseil_repo//absl/strings:str_format", "@abseil_repo//absl/strings:str_format",
"//common:status", "//common:status",
"//example:test_emmg_messages", "//protos/public:media_cas_cc_proto",
], ],
) )
@@ -182,10 +175,7 @@ cc_library(
hdrs = [ hdrs = [
"mpeg2ts.h", "mpeg2ts.h",
], ],
deps = [ deps = ["//base"],
"//base",
"@abseil_repo//absl/base:core_headers",
],
) )
cc_library( cc_library(
@@ -198,8 +188,6 @@ cc_library(
deps = [ deps = [
":util", ":util",
"//base", "//base",
"@abseil_repo//absl/base:core_headers",
"//common:status",
], ],
) )
@@ -215,7 +203,6 @@ cc_library(
deps = [ deps = [
":mpeg2ts", ":mpeg2ts",
"//base", "//base",
"@abseil_repo//absl/base:core_headers",
"@abseil_repo//absl/strings", "@abseil_repo//absl/strings",
"//common:status", "//common:status",
"//common:string_util", "//common:string_util",
@@ -227,7 +214,6 @@ cc_test(
size = "small", size = "small",
srcs = ["ts_packet_test.cc"], srcs = ["ts_packet_test.cc"],
deps = [ deps = [
":mpeg2ts",
":ts_packet", ":ts_packet",
"//base", "//base",
"//testing:gunit_main", "//testing:gunit_main",
@@ -245,7 +231,6 @@ cc_library(
":mpeg2ts", ":mpeg2ts",
":ts_packet", ":ts_packet",
"//base", "//base",
"@abseil_repo//absl/base:core_headers",
"//common:status", "//common:status",
], ],
) )
@@ -258,6 +243,8 @@ cc_test(
], ],
deps = [ deps = [
":util", ":util",
"//base",
"//testing:gunit_main", "//testing:gunit_main",
"//common:status",
], ],
) )

View File

@@ -8,14 +8,13 @@
#include "media_cas_packager_sdk/internal/ecm.h" #include "media_cas_packager_sdk/internal/ecm.h"
#include <stddef.h>
#include <bitset> #include <bitset>
#include <utility>
#include <vector>
#include "glog/logging.h" #include "glog/logging.h"
#include "absl/strings/str_cat.h" #include "absl/strings/str_cat.h"
#include "common/aes_cbc_util.h" #include "common/aes_cbc_util.h"
#include "common/status.h"
#include "common/string_util.h" #include "common/string_util.h"
#include "protos/public/media_cas_encryption.pb.h" #include "protos/public/media_cas_encryption.pb.h"

View File

@@ -16,9 +16,9 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
#include <cstdint>
#include "common/status.h" #include "common/status.h"
#include "media_cas_packager_sdk/public/wv_cas_types.h" #include "media_cas_packager_sdk/public/wv_cas_types.h"
#include "protos/public/media_cas.pb.h"
namespace widevine { namespace widevine {
namespace cas { namespace cas {

View File

@@ -8,9 +8,6 @@
#include "media_cas_packager_sdk/internal/ecm_generator.h" #include "media_cas_packager_sdk/internal/ecm_generator.h"
#include <memory>
#include <string>
#include "testing/gmock.h" #include "testing/gmock.h"
#include "testing/gunit.h" #include "testing/gunit.h"
#include "absl/memory/memory.h" #include "absl/memory/memory.h"

View File

@@ -8,14 +8,8 @@
#include "media_cas_packager_sdk/internal/ecm.h" #include "media_cas_packager_sdk/internal/ecm.h"
#include <stddef.h>
#include <string>
#include "testing/gmock.h" #include "testing/gmock.h"
#include "testing/gunit.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" #include "protos/public/media_cas_encryption.pb.h"
using ::testing::Return; using ::testing::Return;

View File

@@ -8,22 +8,13 @@
#include "media_cas_packager_sdk/internal/ecmg_client_handler.h" #include "media_cas_packager_sdk/internal/ecmg_client_handler.h"
#include <cstddef>
#include <cstdio>
#include <cstring> #include <cstring>
#include <iostream>
#include <memory>
#include <utility>
#include <vector>
#include "glog/logging.h" #include "glog/logging.h"
#include "absl/memory/memory.h" #include "absl/memory/memory.h"
#include "absl/strings/str_cat.h" #include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "common/crypto_util.h" #include "common/crypto_util.h"
#include "common/random_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/ecmg_constants.h"
#include "media_cas_packager_sdk/internal/fixed_key_fetcher.h" #include "media_cas_packager_sdk/internal/fixed_key_fetcher.h"
#include "media_cas_packager_sdk/internal/mpeg2ts.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/internal/util.h"
#include "media_cas_packager_sdk/public/wv_cas_ecm.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_key_fetcher.h"
#include "media_cas_packager_sdk/public/wv_cas_types.h"
// CA System ID for Widevine. // CA System ID for Widevine.
static constexpr uint16_t kWidevineSystemId = 0x4AD4; static constexpr uint16_t kWidevineSystemId = 0x4AD4;
@@ -96,7 +86,7 @@ Status ProcessPrivateParameters(const char* const request, uint16_t param_type,
break; break;
case AGE_RESTRICTION: case AGE_RESTRICTION:
if (param_length != AGE_RESTRICTION_SIZE) { if (param_length != AGE_RESTRICTION_SIZE) {
return Status(error::INVALID_ARGUMENT, return Status(error::FAILED_PRECONDITION,
absl::StrCat("Invalid parameter length ", param_length, absl::StrCat("Invalid parameter length ", param_length,
" for parameter type ", param_type)); " for parameter type ", param_type));
} }
@@ -106,7 +96,7 @@ Status ProcessPrivateParameters(const char* const request, uint16_t param_type,
case ENTITLEMENT_ID_KEY_COMBINATION: { case ENTITLEMENT_ID_KEY_COMBINATION: {
if (param_length != if (param_length !=
kEntitlementKeyIdSizeBytes + kEntitlementKeyValueSizeBytes) { kEntitlementKeyIdSizeBytes + kEntitlementKeyValueSizeBytes) {
return Status(error::INVALID_ARGUMENT, return Status(error::FAILED_PRECONDITION,
absl::StrCat("Invalid parameter length ", param_length, absl::StrCat("Invalid parameter length ", param_length,
" for parameter type ", param_type)); " for parameter type ", param_type));
} }
@@ -185,7 +175,7 @@ Status HandleParameters(const char* const request, size_t request_length,
} }
case CP_DURATION: case CP_DURATION:
if (param_length != CP_DURATION_SIZE) { if (param_length != CP_DURATION_SIZE) {
return Status(error::INVALID_ARGUMENT, return Status(error::FAILED_PRECONDITION,
absl::StrCat("Invalid parameter length ", param_length, absl::StrCat("Invalid parameter length ", param_length,
" for parameter type ", param_type)); " for parameter type ", param_type));
} }
@@ -194,7 +184,7 @@ Status HandleParameters(const char* const request, size_t request_length,
break; break;
case CP_NUMBER: case CP_NUMBER:
if (param_length != CP_NUMBER_SIZE) { if (param_length != CP_NUMBER_SIZE) {
return Status(error::INVALID_ARGUMENT, return Status(error::FAILED_PRECONDITION,
absl::StrCat("Invalid parameter length ", param_length, absl::StrCat("Invalid parameter length ", param_length,
" for parameter type ", param_type)); " for parameter type ", param_type));
} }
@@ -208,7 +198,7 @@ Status HandleParameters(const char* const request, size_t request_length,
break; break;
case ECM_CHANNEL_ID: case ECM_CHANNEL_ID:
if (param_length != ECM_CHANNEL_ID_SIZE) { if (param_length != ECM_CHANNEL_ID_SIZE) {
return Status(error::INVALID_ARGUMENT, return Status(error::FAILED_PRECONDITION,
absl::StrCat("Invalid parameter length ", param_length, absl::StrCat("Invalid parameter length ", param_length,
" for parameter type ", param_type)); " for parameter type ", param_type));
} }
@@ -217,7 +207,7 @@ Status HandleParameters(const char* const request, size_t request_length,
break; break;
case ECM_ID: case ECM_ID:
if (param_length != ECM_ID_SIZE) { if (param_length != ECM_ID_SIZE) {
return Status(error::INVALID_ARGUMENT, return Status(error::FAILED_PRECONDITION,
absl::StrCat("Invalid parameter length ", param_length, absl::StrCat("Invalid parameter length ", param_length,
" for parameter type ", param_type)); " for parameter type ", param_type));
} }
@@ -226,7 +216,7 @@ Status HandleParameters(const char* const request, size_t request_length,
break; break;
case ECM_STREAM_ID: case ECM_STREAM_ID:
if (param_length != ECM_STREAM_ID_SIZE) { if (param_length != ECM_STREAM_ID_SIZE) {
return Status(error::INVALID_ARGUMENT, return Status(error::FAILED_PRECONDITION,
absl::StrCat("Invalid parameter length ", param_length, absl::StrCat("Invalid parameter length ", param_length,
" for parameter type ", param_type)); " for parameter type ", param_type));
} }
@@ -235,7 +225,7 @@ Status HandleParameters(const char* const request, size_t request_length,
break; break;
case NOMINAL_CP_DURATION: case NOMINAL_CP_DURATION:
if (param_length != NOMINAL_CP_DURATION_SIZE) { if (param_length != NOMINAL_CP_DURATION_SIZE) {
return Status(error::INVALID_ARGUMENT, return Status(error::FAILED_PRECONDITION,
absl::StrCat("Invalid parameter length ", param_length, absl::StrCat("Invalid parameter length ", param_length,
" for parameter type ", param_type)); " for parameter type ", param_type));
} }
@@ -244,7 +234,7 @@ Status HandleParameters(const char* const request, size_t request_length,
break; break;
case SUPER_CAS_ID: case SUPER_CAS_ID:
if (param_length != SUPER_CAS_ID_SIZE) { if (param_length != SUPER_CAS_ID_SIZE) {
return Status(error::INVALID_ARGUMENT, return Status(error::FAILED_PRECONDITION,
absl::StrCat("Invalid parameter length ", param_length, absl::StrCat("Invalid parameter length ", param_length,
" for parameter type ", param_type)); " for parameter type ", param_type));
} }
@@ -357,10 +347,14 @@ uint16_t StatusToDvbErrorCode(const Status& status) {
return 0; return 0;
} }
switch (status.error_code()) { switch (status.error_code()) {
case error::FAILED_PRECONDITION:
return INCONSISTENT_LENGTH_FOR_DVB_PARAMETER;
case error::INVALID_ARGUMENT: case error::INVALID_ARGUMENT:
return INVALID_VALUE_FOR_DVB_PARAMETER; return INVALID_VALUE_FOR_DVB_PARAMETER;
case error::NOT_FOUND: case error::NOT_FOUND:
return MISSING_MANDATORY_DVB_PARAMETER; return MISSING_MANDATORY_DVB_PARAMETER;
case error::UNIMPLEMENTED:
return UNKNOWN_PARAMETER_TYPE_VALUE;
case error::INTERNAL: case error::INTERNAL:
default: default:
return UNKNOWN_ERROR; return UNKNOWN_ERROR;
@@ -454,22 +448,8 @@ void EcmgClientHandler::HandleRequest(const char* const request, char* response,
Status status = HandleParameters(request + offset, request_length, &params); Status status = HandleParameters(request + offset, request_length, &params);
if (!status.ok()) { if (!status.ok()) {
LOG(ERROR) << status.ToString(); LOG(ERROR) << status.ToString();
switch (status.error_code()) { BuildChannelError(params.ecm_channel_id, StatusToDvbErrorCode(status),
case error::INVALID_ARGUMENT: status.error_message(), response, response_length);
// 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);
}
return; return;
} }
switch (request_type) { switch (request_type) {
@@ -535,7 +515,7 @@ void EcmgClientHandler::HandleChannelSetup(const EcmgParameters& params,
channel_id_ = params.ecm_channel_id; channel_id_ = params.ecm_channel_id;
channel_id_set_ = true; channel_id_set_ = true;
Status status = UpdatePrivateParameters(params, false); Status status = UpdateChannelPrivateParameters(params);
if (!status.ok()) { if (!status.ok()) {
LOG(ERROR) << status.ToString(); LOG(ERROR) << status.ToString();
BuildChannelError(params.ecm_channel_id, StatusToDvbErrorCode(status), 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] = absl::make_unique<EcmgStreamInfo>();
streams_info_[params.ecm_stream_id]->ecm_id = params.ecm_id; streams_info_[params.ecm_stream_id]->ecm_id = params.ecm_id;
Status status = UpdatePrivateParameters(params, true); Status status = UpdateStreamPrivateParameters(params);
if (!status.ok()) { if (!status.ok()) {
LOG(ERROR) << status.ToString(); LOG(ERROR) << status.ToString();
BuildStreamError(params.ecm_channel_id, params.ecm_stream_id, 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. // Update private parameters based on access_criteria if any.
Status status = UpdatePrivateParameters(params, true); Status status = UpdateStreamPrivateParameters(params);
if (!status.ok()) { if (!status.ok()) {
LOG(ERROR) << status.ToString(); LOG(ERROR) << status.ToString();
BuildStreamError(params.ecm_channel_id, params.ecm_stream_id, 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); params.cp_number, ecm_datagram, response, response_length);
} }
Status EcmgClientHandler::UpdatePrivateParameters(const EcmgParameters& params, Status EcmgClientHandler::UpdateCommonPrivateParameters(
bool stream_specific) { const EcmgParameters& params) {
EcmgStreamInfo* stream_info =
stream_specific ? streams_info_[params.ecm_stream_id].get() : nullptr;
if (params.age_restriction != 0xff) { if (params.age_restriction != 0xff) {
if (params.age_restriction > kMaxAllowedAgeRestriction) { if (params.age_restriction > kMaxAllowedAgeRestriction) {
return {error::INVALID_ARGUMENT, "Age restriction too large."}; return {error::INVALID_ARGUMENT, "Age restriction too large."};
} }
age_restriction_ = params.age_restriction; 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()) { if (!params.track_types.empty()) {
track_types_.assign(params.track_types.begin(), params.track_types.end()); 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()) { if (!params.content_id.empty()) {
content_id_ = params.content_id; content_id_ = params.content_id;
} }
if (!params.content_provider.empty()) { if (!params.content_provider.empty()) {
content_provider_ = params.content_provider; content_provider_ = params.content_provider;
} }
if (!params.content_ivs.empty()) { if (!params.content_ivs.empty()) {
if (params.content_ivs.size() < ecmg_config_->number_of_content_keys) { if (params.content_ivs.size() < ecmg_config_->number_of_content_keys) {
return {error::INVALID_ARGUMENT, return {error::INVALID_ARGUMENT,
@@ -778,19 +734,54 @@ Status EcmgClientHandler::UpdatePrivateParameters(const EcmgParameters& params,
return {error::INVALID_ARGUMENT, "Size of content ivs must match."}; 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()) { if (!params.entitlement_comb.empty()) {
entitlement_comb_.assign(params.entitlement_comb.begin(), entitlement_comb_.assign(params.entitlement_comb.begin(),
params.entitlement_comb.end()); 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(); return OkStatus();
} }

View File

@@ -10,13 +10,8 @@
#define MEDIA_CAS_PACKAGER_SDK_INTERNAL_ECMG_CLIENT_HANDLER_H_ #define MEDIA_CAS_PACKAGER_SDK_INTERNAL_ECMG_CLIENT_HANDLER_H_
#include <cstddef> #include <cstddef>
#include <iostream>
#include <list>
#include <map>
#include <memory> #include <memory>
#include <string> #include <string>
#include <unordered_set>
#include <utility>
#include <vector> #include <vector>
#include <cstdint> #include <cstdint>
@@ -116,12 +111,12 @@ class EcmgClientHandler {
size_t* response_length); size_t* response_length);
void HandleCwProvision(const EcmgParameters& params, char* response, void HandleCwProvision(const EcmgParameters& params, char* response,
size_t* response_length); 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| // Update private parameters using |params|.
// is true, |params| will only affect values of this stream. If it is false, Status UpdateChannelPrivateParameters(const EcmgParameters& params);
// |param| will affect all streams of the channel. Status UpdateStreamPrivateParameters(const EcmgParameters& params);
Status UpdatePrivateParameters(const EcmgParameters& params, Status UpdateCommonPrivateParameters(const EcmgParameters& params);
bool stream_specific);
// Check if all required parameters have been set. If so, initialize |ecm_| by // Check if all required parameters have been set. If so, initialize |ecm_| by
// fetching entitlement keys. // fetching entitlement keys.
Status CheckAndInitializeEcm(const EcmgParameters& params); Status CheckAndInitializeEcm(const EcmgParameters& params);

View File

@@ -8,9 +8,10 @@
#include "media_cas_packager_sdk/internal/ecmg_client_handler.h" #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 "testing/gunit.h"
#include "absl/memory/memory.h" #include "absl/memory/memory.h"
#include "absl/strings/str_cat.h" #include "absl/strings/str_cat.h"
@@ -455,6 +456,81 @@ TEST_F(EcmgClientHandlerTest, WrongParameterChannelId) {
CheckStreamError(UNKNOWN_ECM_CHANNEL_ID_VALUE, response_, response_len_); 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
} // namespace cas } // namespace cas
} // namespace widevine } // namespace widevine

View File

@@ -10,18 +10,19 @@
#include <sys/socket.h> #include <sys/socket.h>
#include <cstddef>
#include <cstdio>
#include <cstring> #include <cstring>
#include <iostream> #include <iostream>
#include "glog/logging.h" #include "glog/logging.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.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/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_constants.h"
#include "media_cas_packager_sdk/internal/simulcrypt_util.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 "media_cas_packager_sdk/internal/util.h"
#include "protos/public/media_cas.pb.h"
namespace widevine { namespace widevine {
namespace cas { namespace cas {
@@ -125,6 +126,36 @@ void Emmg::BuildStreamSetup() {
Host16ToBigEndian(request_ + 3, &total_param_length); 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() { void Emmg::BuildDataProvision() {
bzero(request_, BUFFER_SIZE); bzero(request_, BUFFER_SIZE);
request_length_ = 0; request_length_ = 0;
@@ -142,15 +173,11 @@ void Emmg::BuildDataProvision() {
simulcrypt_util::AddUint16Param(EMMG_DATA_ID, emmg_config_->data_id, request_, simulcrypt_util::AddUint16Param(EMMG_DATA_ID, emmg_config_->data_id, request_,
&request_length_); &request_length_);
// Add a fake TS packet. uint8_t datagram[kTsPacketSize];
uint16_t datagram_type = EMMG_DATAGRAM; GeneratePrivateData(emmg_config_->content_provider, emmg_config_->content_id,
Host16ToBigEndian(request_ + request_length_, &datagram_type); datagram);
request_length_ += 2; simulcrypt_util::AddParam(EMMG_DATAGRAM, datagram, kTsPacketSize, request_,
uint16_t param_length = sizeof(kTestEmmgTsPacket); // Should be 188. &request_length_);
Host16ToBigEndian(request_ + request_length_, &param_length);
request_length_ += 2;
memcpy(request_ + request_length_, kTestEmmgTsPacket, param_length);
request_length_ += param_length;
uint16_t total_param_length = request_length_ - 5; uint16_t total_param_length = request_length_ - 5;
Host16ToBigEndian(request_ + 3, &total_param_length); Host16ToBigEndian(request_ + 3, &total_param_length);

View File

@@ -10,13 +10,9 @@
#define MEDIA_CAS_PACKAGER_SDK_INTERNAL_EMMG_H_ #define MEDIA_CAS_PACKAGER_SDK_INTERNAL_EMMG_H_
#include <cstddef> #include <cstddef>
#include <memory>
#include <string> #include <string>
#include <utility>
#include <vector>
#include <cstdint> #include <cstdint>
#include "glog/logging.h"
#include "common/status.h" #include "common/status.h"
#define BUFFER_SIZE (1024) #define BUFFER_SIZE (1024)
@@ -32,6 +28,8 @@ struct EmmgConfig {
uint16_t data_stream_id; uint16_t data_stream_id;
uint16_t data_id; uint16_t data_id;
uint8_t data_type; uint8_t data_type;
std::string content_provider;
std::string content_id;
}; };
// A class that sends EMMG/PDG message to the MUX server. // A class that sends EMMG/PDG message to the MUX server.
@@ -62,6 +60,8 @@ class Emmg {
void SendStreamCloseRequest(); void SendStreamCloseRequest();
void SendChannelClose(); void SendChannelClose();
Status GeneratePrivateData(const std::string& content_provider,
const std::string& content_id, uint8_t* buffer);
void ReceiveResponseAndVerify(uint16_t expected_type); void ReceiveResponseAndVerify(uint16_t expected_type);
void Send(uint16_t message_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 // |server_socket_fd| is a file descriptor we can use to communicate
// with the MUX server. // with the MUX server.
int server_socket_fd_; int server_socket_fd_;
// |continuity_counter| is incremented each time private data is generated.
int continuity_counter_ = 0;
}; };
} // namespace cas } // namespace cas

View File

@@ -8,6 +8,12 @@
#include "media_cas_packager_sdk/internal/emmg.h" #include "media_cas_packager_sdk/internal/emmg.h"
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <memory>
#include "testing/gunit.h" #include "testing/gunit.h"
#include "absl/memory/memory.h" #include "absl/memory/memory.h"
#include "absl/strings/str_format.h" #include "absl/strings/str_format.h"
@@ -38,6 +44,8 @@ class EmmgTest : public ::testing::Test {
config_.data_stream_id = 0x0001; config_.data_stream_id = 0x0001;
config_.data_id = 0x0001; config_.data_id = 0x0001;
config_.data_type = 0x01; config_.data_type = 0x01;
config_.content_provider = "widevine_test";
config_.content_id = "CasTsFake";
emmg_ = absl::make_unique<TestableEmmg>(&config_); emmg_ = absl::make_unique<TestableEmmg>(&config_);
} }

View File

@@ -8,7 +8,6 @@
#include "media_cas_packager_sdk/internal/fixed_key_fetcher.h" #include "media_cas_packager_sdk/internal/fixed_key_fetcher.h"
#include "common/status.h"
#include "protos/public/media_cas_encryption.pb.h" #include "protos/public/media_cas_encryption.pb.h"
namespace widevine { namespace widevine {

View File

@@ -9,6 +9,9 @@
#ifndef MEDIA_CAS_PACKAGER_SDK_INTERNAL_FIXED_KEY_FETCHER_H_ #ifndef MEDIA_CAS_PACKAGER_SDK_INTERNAL_FIXED_KEY_FETCHER_H_
#define 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" #include "media_cas_packager_sdk/internal/key_fetcher.h"
namespace widevine { namespace widevine {

View File

@@ -13,7 +13,6 @@
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include "base/macros.h"
namespace widevine { namespace widevine {
namespace cas { namespace cas {

View File

@@ -8,7 +8,6 @@
#include "media_cas_packager_sdk/internal/simulcrypt_util.h" #include "media_cas_packager_sdk/internal/simulcrypt_util.h"
#include <cstddef>
#include <cstring> #include <cstring>
#include "glog/logging.h" #include "glog/logging.h"

View File

@@ -12,13 +12,8 @@
#define MEDIA_CAS_PACKAGER_SDK_INTERNAL_SIMULCRYPT_UTIL_H_ #define MEDIA_CAS_PACKAGER_SDK_INTERNAL_SIMULCRYPT_UTIL_H_
#include <cstddef> #include <cstddef>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <cstdint> #include <cstdint>
#include "common/status.h"
namespace widevine { namespace widevine {
namespace cas { namespace cas {

View File

@@ -12,7 +12,6 @@
#include "glog/logging.h" #include "glog/logging.h"
#include "absl/strings/str_cat.h" #include "absl/strings/str_cat.h"
#include "common/status.h"
#include "common/string_util.h" #include "common/string_util.h"
namespace widevine { namespace widevine {

View File

@@ -33,11 +33,9 @@
#ifndef MEDIA_CAS_PACKAGER_SDK_INTERNAL_TS_PACKET_H_ #ifndef MEDIA_CAS_PACKAGER_SDK_INTERNAL_TS_PACKET_H_
#define MEDIA_CAS_PACKAGER_SDK_INTERNAL_TS_PACKET_H_ #define MEDIA_CAS_PACKAGER_SDK_INTERNAL_TS_PACKET_H_
#include <memory>
#include <string> #include <string>
#include <cstdint> #include <cstdint>
#include "base/macros.h"
#include "absl/strings/string_view.h" #include "absl/strings/string_view.h"
#include "common/status.h" #include "common/status.h"
#include "media_cas_packager_sdk/internal/mpeg2ts.h" #include "media_cas_packager_sdk/internal/mpeg2ts.h"

View File

@@ -8,12 +8,9 @@
#include "media_cas_packager_sdk/internal/ts_packet.h" #include "media_cas_packager_sdk/internal/ts_packet.h"
#include <string>
#include "base/macros.h" #include "base/macros.h"
#include "testing/gmock.h" #include "testing/gmock.h"
#include "testing/gunit.h" #include "testing/gunit.h"
#include "media_cas_packager_sdk/internal/mpeg2ts.h"
namespace widevine { namespace widevine {
namespace cas { namespace cas {

View File

@@ -9,6 +9,7 @@
#include "media_cas_packager_sdk/internal/util.h" #include "media_cas_packager_sdk/internal/util.h"
#include <netinet/in.h> #include <netinet/in.h>
#include <cstddef> #include <cstddef>
#include <cstring> #include <cstring>

View File

@@ -60,7 +60,6 @@ cc_library(
deps = [ deps = [
":wv_cas_types", ":wv_cas_types",
"//base", "//base",
"@abseil_repo//absl/base:core_headers",
"@abseil_repo//absl/strings", "@abseil_repo//absl/strings",
"//common:status", "//common:status",
"//common:string_util", "//common:string_util",
@@ -77,7 +76,6 @@ cc_test(
srcs = ["wv_cas_ca_descriptor_test.cc"], srcs = ["wv_cas_ca_descriptor_test.cc"],
deps = [ deps = [
":wv_cas_ca_descriptor", ":wv_cas_ca_descriptor",
":wv_cas_types",
"//testing:gunit_main", "//testing:gunit_main",
"//protos/public:media_cas_cc_proto", "//protos/public:media_cas_cc_proto",
], ],
@@ -113,7 +111,6 @@ cc_test(
srcs = ["wv_cas_ecm_test.cc"], srcs = ["wv_cas_ecm_test.cc"],
deps = [ deps = [
":wv_cas_ecm", ":wv_cas_ecm",
":wv_cas_types",
"//testing:gunit_main", "//testing:gunit_main",
"@abseil_repo//absl/strings", "@abseil_repo//absl/strings",
"//media_cas_packager_sdk/internal:mpeg2ts", "//media_cas_packager_sdk/internal:mpeg2ts",
@@ -131,10 +128,9 @@ cc_library(
deps = [ deps = [
"//base", "//base",
"//external:protobuf", "//external:protobuf",
"@abseil_repo//absl/base:core_headers", "@abseil_repo//absl/flags:flag",
"@abseil_repo//absl/strings", "@abseil_repo//absl/strings",
"@curl_repo//:curl", "@curl_repo//:curl",
"//util:error_space",
"//common:signature_util", "//common:signature_util",
"//common:status", "//common:status",
"//media_cas_packager_sdk/internal:key_fetcher", "//media_cas_packager_sdk/internal:key_fetcher",
@@ -153,7 +149,9 @@ cc_test(
"//base", "//base",
"//external:protobuf", "//external:protobuf",
"//testing:gunit_main", "//testing:gunit_main",
"@abseil_repo//absl/flags:flag",
"@abseil_repo//absl/strings", "@abseil_repo//absl/strings",
"//common:status",
"//protos/public:media_cas_encryption_cc_proto", "//protos/public:media_cas_encryption_cc_proto",
], ],
) )
@@ -189,8 +187,8 @@ cc_binary(
deps = [ deps = [
":wv_cas_types", ":wv_cas_types",
"//base", "//base",
"@abseil_repo//absl/base:core_headers", "@abseil_repo//absl/flags:flag",
"@abseil_repo//absl/strings", "@abseil_repo//absl/flags:parse",
"//media_cas_packager_sdk/internal:ecmg_client_handler", "//media_cas_packager_sdk/internal:ecmg_client_handler",
], ],
) )
@@ -200,6 +198,8 @@ cc_binary(
srcs = ["wv_emmg.cc"], srcs = ["wv_emmg.cc"],
deps = [ deps = [
"//base", "//base",
"@abseil_repo//absl/flags:flag",
"@abseil_repo//absl/flags:parse",
"//media_cas_packager_sdk/internal:emmg", "//media_cas_packager_sdk/internal:emmg",
], ],
) )

View File

@@ -14,7 +14,6 @@
#include "absl/strings/str_cat.h" #include "absl/strings/str_cat.h"
#include "common/status.h" #include "common/status.h"
#include "common/string_util.h" #include "common/string_util.h"
#include "media_cas_packager_sdk/public/wv_cas_types.h"
#include "protos/public/media_cas.pb.h" #include "protos/public/media_cas.pb.h"
namespace widevine { namespace widevine {

View File

@@ -10,8 +10,10 @@
#define MEDIA_CAS_PACKAGER_SDK_PUBLIC_WV_CAS_CA_DESCRIPTOR_H_ #define MEDIA_CAS_PACKAGER_SDK_PUBLIC_WV_CAS_CA_DESCRIPTOR_H_
#include <stddef.h> #include <stddef.h>
#include <string> #include <string>
#include <cstdint>
#include "media_cas_packager_sdk/public/wv_cas_types.h" #include "media_cas_packager_sdk/public/wv_cas_types.h"
namespace widevine { namespace widevine {

View File

@@ -8,10 +8,7 @@
#include "media_cas_packager_sdk/public/wv_cas_ca_descriptor.h" #include "media_cas_packager_sdk/public/wv_cas_ca_descriptor.h"
#include <string>
#include "testing/gunit.h" #include "testing/gunit.h"
#include "media_cas_packager_sdk/public/wv_cas_types.h"
#include "protos/public/media_cas.pb.h" #include "protos/public/media_cas.pb.h"
using ::testing::Test; using ::testing::Test;

View File

@@ -8,13 +8,15 @@
#include "media_cas_packager_sdk/public/wv_cas_ecm.h" #include "media_cas_packager_sdk/public/wv_cas_ecm.h"
#include <stddef.h>
#include <string.h>
#include <sys/types.h>
#include <memory> #include <memory>
#include <utility>
#include <vector> #include <vector>
#include "glog/logging.h" #include "glog/logging.h"
#include "absl/memory/memory.h" #include "absl/memory/memory.h"
#include "absl/strings/str_cat.h"
#include "common/crypto_util.h" #include "common/crypto_util.h"
#include "common/status.h" #include "common/status.h"
#include "example/constants.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/fixed_key_fetcher.h"
#include "media_cas_packager_sdk/internal/mpeg2ts.h" #include "media_cas_packager_sdk/internal/mpeg2ts.h"
#include "media_cas_packager_sdk/internal/util.h" #include "media_cas_packager_sdk/internal/util.h"
#include "media_cas_packager_sdk/public/wv_cas_types.h"
namespace widevine { namespace widevine {
namespace cas { namespace cas {

View File

@@ -9,13 +9,9 @@
#ifndef MEDIA_CAS_PACKAGER_SDK_PUBLIC_WV_CAS_ECM_H_ #ifndef MEDIA_CAS_PACKAGER_SDK_PUBLIC_WV_CAS_ECM_H_
#define 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 <string>
#include <utility>
#include <vector>
#include <cstdint>
#include "media_cas_packager_sdk/public/wv_cas_types.h" #include "media_cas_packager_sdk/public/wv_cas_types.h"
namespace widevine { namespace widevine {

View File

@@ -8,11 +8,11 @@
#include "media_cas_packager_sdk/public/wv_cas_ecm.h" #include "media_cas_packager_sdk/public/wv_cas_ecm.h"
#include <string.h>
#include "testing/gunit.h" #include "testing/gunit.h"
#include "absl/strings/escaping.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/internal/mpeg2ts.h"
#include "media_cas_packager_sdk/public/wv_cas_types.h"
using ::testing::Test; using ::testing::Test;

View File

@@ -10,32 +10,32 @@
#include <stddef.h> #include <stddef.h>
#include <string.h> #include <string.h>
#include <string>
#include "gflags/gflags.h" #include <cstdint>
#include "glog/logging.h" #include "glog/logging.h"
#include "google/protobuf/util/json_util.h" #include "google/protobuf/util/json_util.h"
#include "absl/flags/flag.h"
#include "absl/strings/escaping.h" #include "absl/strings/escaping.h"
#include "absl/strings/str_cat.h" #include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h" #include "absl/strings/string_view.h"
#include "curl/curl.h" #include "curl/curl.h"
#include "curl/easy.h" #include "curl/easy.h"
#include "common/signature_util.h" #include "common/signature_util.h"
#include "common/status.h"
#include "protos/public/media_cas_encryption.pb.h" #include "protos/public/media_cas_encryption.pb.h"
using google::protobuf::util::JsonPrintOptions; using google::protobuf::util::JsonPrintOptions;
using google::protobuf::util::JsonStringToMessage; using google::protobuf::util::JsonStringToMessage;
using google::protobuf::util::MessageToJsonString; using google::protobuf::util::MessageToJsonString;
DEFINE_string( ABSL_FLAG(std::string, license_server, "",
license_server, "", "HTTP URL to the license server for making CAS encryption request.");
"HTTP URL to the license server for making CAS encryption request"); ABSL_FLAG(std::string, signing_provider, "",
DEFINE_string(signing_provider, "", "Name of the provider signing the CAS encryption request.");
"Name of the provider signing the CAS encryption request"); ABSL_FLAG(std::string, signing_key, "",
DEFINE_string(signing_key, "", "AES key (in hex) for signing the CAS encryption request.");
"AES key (in hex) for signing the CAS encryption request"); ABSL_FLAG(std::string, signing_iv, "",
DEFINE_string(signing_iv, "", "AES iv (in hex) for signing the CAS encryption request.");
"AES iv (in hex) for signing the CAS encryption request");
namespace widevine { namespace widevine {
namespace cas { namespace cas {
@@ -43,8 +43,9 @@ namespace cas {
Status WvCasKeyFetcher::RequestEntitlementKey( Status WvCasKeyFetcher::RequestEntitlementKey(
const std::string& request_string, const std::string& request_string,
std::string* signed_response_string) const { std::string* signed_response_string) const {
if (FLAGS_signing_provider.empty() || FLAGS_signing_key.empty() || if (absl::GetFlag(FLAGS_signing_provider).empty() ||
FLAGS_signing_iv.empty()) { absl::GetFlag(FLAGS_signing_key).empty() ||
absl::GetFlag(FLAGS_signing_iv).empty()) {
return Status( return Status(
error::INVALID_ARGUMENT, error::INVALID_ARGUMENT,
"Flag 'signing_provider', 'signing_key' or 'signing_iv' is empty"); "Flag 'signing_provider', 'signing_key' or 'signing_iv' is empty");
@@ -73,13 +74,14 @@ Status WvCasKeyFetcher::RequestEntitlementKey(
signed_request.set_request(request_json); signed_request.set_request(request_json);
std::string signature; std::string signature;
if (!signature_util::GenerateAesSignature( if (!signature_util::GenerateAesSignature(
request_json, absl::HexStringToBytes(FLAGS_signing_key), request_json,
absl::HexStringToBytes(FLAGS_signing_iv), &signature) absl::HexStringToBytes(absl::GetFlag(FLAGS_signing_key)),
absl::HexStringToBytes(absl::GetFlag(FLAGS_signing_iv)), &signature)
.ok()) { .ok()) {
return Status(error::INTERNAL, "Failed to sign the request."); return Status(error::INTERNAL, "Failed to sign the request.");
} }
signed_request.set_signature(signature); 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; std::string signed_request_json;
// NOTE: MessageToJsonString will automatically converts the 'request' and // NOTE: MessageToJsonString will automatically converts the 'request' and
// 'signature' fields in SignedCasEncryptionRequest to base64, because they // '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, Status WvCasKeyFetcher::MakeHttpRequest(const std::string& signed_request_json,
std::string* http_response_json) const { std::string* http_response_json) const {
CHECK(http_response_json); 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"); return Status(error::INVALID_ARGUMENT, "Flag 'license_server' is empty");
} }
CURL* curl; CURL* curl;
CURLcode curl_code; CURLcode curl_code;
curl = curl_easy_init(); curl = curl_easy_init();
if (curl) { 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_POSTFIELDS, signed_request_json.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEDATA, http_response_json); curl_easy_setopt(curl, CURLOPT_WRITEDATA, http_response_json);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &AppendToString); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &AppendToString);

View File

@@ -11,14 +11,10 @@
#include <string> #include <string>
#include "gflags/gflags.h" #include "absl/flags/declare.h"
#include "common/status.h"
#include "media_cas_packager_sdk/internal/key_fetcher.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 widevine {
namespace cas { namespace cas {
@@ -55,4 +51,10 @@ class WvCasKeyFetcher : public KeyFetcher {
} // namespace cas } // namespace cas
} // namespace widevine } // 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_ #endif // MEDIA_CAS_PACKAGER_SDK_PUBLIC_WV_CAS_KEY_FETCHER_H_

View File

@@ -8,12 +8,13 @@
#include "media_cas_packager_sdk/public/wv_cas_key_fetcher.h" #include "media_cas_packager_sdk/public/wv_cas_key_fetcher.h"
#include "gflags/gflags.h"
#include "glog/logging.h" #include "glog/logging.h"
#include "google/protobuf/text_format.h" #include "google/protobuf/text_format.h"
#include "testing/gmock.h" #include "testing/gmock.h"
#include "testing/gunit.h" #include "testing/gunit.h"
#include "absl/flags/flag.h"
#include "absl/strings/escaping.h" #include "absl/strings/escaping.h"
#include "common/status.h"
#include "protos/public/media_cas_encryption.pb.h" #include "protos/public/media_cas_encryption.pb.h"
using testing::_; using testing::_;
@@ -63,9 +64,9 @@ class WvCasKeyFetcherTest : public ::testing::Test {
public: public:
WvCasKeyFetcherTest() {} WvCasKeyFetcherTest() {}
void SetUp() override { void SetUp() override {
FLAGS_signing_provider = kSigningProvider; absl::SetFlag(&FLAGS_signing_provider, kSigningProvider);
FLAGS_signing_key = kSingingKey; absl::SetFlag(&FLAGS_signing_key, kSingingKey);
FLAGS_signing_iv = kSingingIv; absl::SetFlag(&FLAGS_signing_iv, kSingingIv);
CHECK( CHECK(
google::protobuf::TextFormat::ParseFromString(kCasEncryptionRequest, &request_)); google::protobuf::TextFormat::ParseFromString(kCasEncryptionRequest, &request_));

View File

@@ -8,7 +8,6 @@
#include "media_cas_packager_sdk/public/wv_cas_types.h" #include "media_cas_packager_sdk/public/wv_cas_types.h"
#include "testing/gmock.h"
#include "testing/gunit.h" #include "testing/gunit.h"
namespace widevine { namespace widevine {

View File

@@ -9,6 +9,7 @@
// Widevine MediaCAS ECMG server. // Widevine MediaCAS ECMG server.
#include <netinet/in.h> #include <netinet/in.h>
#include <stdlib.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <unistd.h> #include <unistd.h>
@@ -19,54 +20,37 @@
#include <iostream> #include <iostream>
#include <string> #include <string>
#include "gflags/gflags.h" #include <cstdint>
#include "glog/logging.h" #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/internal/ecmg_client_handler.h"
#include "media_cas_packager_sdk/public/wv_cas_types.h" #include "media_cas_packager_sdk/public/wv_cas_types.h"
static constexpr int32_t kDefaultDelayStart = 200; ABSL_FLAG(int32_t, port, 1234, "Server port number.");
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");
// ECMG related flags. // ECMG related flags.
// TODO(user): Consider adding flags 'ac_delay_start', 'ac_delay_stop', // TODO(user): Consider adding flags 'ac_delay_start', 'ac_delay_stop',
// 'transition_delay_start', 'transition_delay_stop'. // 'transition_delay_start', 'transition_delay_stop'.
DEFINE_int32( ABSL_FLAG(int32_t, delay_start, 200, "delay_start, in milliseconds.");
delay_start, kDefaultDelayStart, ABSL_FLAG(int32_t, delay_stop, 200, "delay_stop, in milliseconds.");
"This flag sets the DVB SimulCrypt delay_start parameter, in milliseconds"); ABSL_FLAG(int32_t, ecm_rep_period, 100, "ECM_rep_period, in milliseconds.");
DEFINE_int32( ABSL_FLAG(int32_t, max_comp_time, 100, "max_comp_time, in milliseconds.");
delay_stop, kDefaultDelayStop, ABSL_FLAG(
"This flag sets the DVB SimulCrypt delay_stop parameter, in milliseconds"); int32_t, access_criteria_transfer_mode, 0,
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,
"If it equals 0, it indicates that the access_criteria parameter is " "If it equals 0, it indicates that the access_criteria parameter is "
"required in the CW_provision message only when the contents of this " "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 " "parameter change. If it equals 1, it indicates that the ECMG requires the "
"access_criteria parameter be present in each CW_provision message."); "access_criteria parameter be present in each CW_provision message.");
DEFINE_int32(number_of_content_keys, kNumberOfContentKeys, ABSL_FLAG(int32_t, number_of_content_keys, 2,
"It sets the number of content keys (CwPerMsg). Must be 1 (single " "It sets the number of content keys (CwPerMsg). Must be 1 (single "
"key) or 2 (key rotation enabled). It also sets LeadCw as " "key) or 2 (key rotation enabled). It also sets LeadCw as "
"number_of_content_keys - 1."); "number_of_content_keys - 1.");
DEFINE_string(crypto_mode, kDefaultCryptoMode, ABSL_FLAG(std::string, crypto_mode, "AesCtr",
"Encryption mode. Choices are \"AesCtr\", \"AesCbc\", " "Encryption mode. Choices are \"AesCtr\", \"AesCbc\", "
"\"DvbCsa2\", \"DvbCsa3\", \"AesOfb\", \"AesScte\"."); "\"DvbCsa2\", \"DvbCsa3\", \"AesOfb\", \"AesScte\".");
DEFINE_bool(use_fixed_fetcher, kDefaultUseFixedFetcher, ABSL_FLAG(bool, use_fixed_fetcher, false,
"Use fixed fetcher to fetch mocked entitlement licenses for " "If set, use fixed fetcher to fetch mocked entitlement licenses when "
"testing purposes."); "needed for testing purposes.");
#define LISTEN_QUEUE_SIZE (20) #define LISTEN_QUEUE_SIZE (20)
#define BUFFER_SIZE (1024) #define BUFFER_SIZE (1024)
@@ -76,15 +60,20 @@ using widevine::cas::EcmgConfig;
void BuildEcmgConfig(EcmgConfig* config) { void BuildEcmgConfig(EcmgConfig* config) {
DCHECK(config); DCHECK(config);
config->delay_start = FLAGS_delay_start; config->delay_start = absl::GetFlag(FLAGS_delay_start);
config->delay_stop = FLAGS_delay_stop; config->delay_stop = absl::GetFlag(FLAGS_delay_stop);
config->ecm_rep_period = FLAGS_ecm_rep_period; config->ecm_rep_period = absl::GetFlag(FLAGS_ecm_rep_period);
config->max_comp_time = FLAGS_max_comp_time; config->max_comp_time = absl::GetFlag(FLAGS_max_comp_time);
config->access_criteria_transfer_mode = FLAGS_access_criteria_transfer_mode; config->access_criteria_transfer_mode =
config->number_of_content_keys = FLAGS_number_of_content_keys; absl::GetFlag(FLAGS_access_criteria_transfer_mode);
CHECK(StringToCryptoMode(FLAGS_crypto_mode, &config->crypto_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."; << "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, 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) { int main(int argc, char** argv) {
gflags::ParseCommandLineFlags(&argc, &argv, true); absl::ParseCommandLine(argc, argv);
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.";
EcmgConfig ecmg_config; EcmgConfig ecmg_config;
BuildEcmgConfig(&ecmg_config); BuildEcmgConfig(&ecmg_config);
@@ -139,7 +124,7 @@ int main(int argc, char** argv) {
bzero(reinterpret_cast<char*>(&server_address), sizeof(server_address)); bzero(reinterpret_cast<char*>(&server_address), sizeof(server_address));
server_address.sin_family = AF_INET; server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = htonl(INADDR_ANY); 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. // Create a listening socket.
int listen_socket_fd = socket(AF_INET, SOCK_STREAM, /* protocol= */ 0); int listen_socket_fd = socket(AF_INET, SOCK_STREAM, /* protocol= */ 0);

View File

@@ -12,54 +12,69 @@
#include <netinet/in.h> #include <netinet/in.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <unistd.h> #include <unistd.h>
#include <cstring>
#include "gflags/gflags.h" #include <cstring>
#include <string>
#include <cstdint>
#include "glog/logging.h" #include "glog/logging.h"
#include "absl/flags/flag.h"
#include "absl/flags/parse.h"
#include "media_cas_packager_sdk/internal/emmg.h" #include "media_cas_packager_sdk/internal/emmg.h"
DEFINE_string(mux_address, "", "Mux server address"); ABSL_FLAG(std::string, mux_address, "", "Mux server address.");
DEFINE_int32(mux_port, 0, "Mux server port number"); ABSL_FLAG(int32_t, mux_port, 0, "Mux server port number.");
// EMMG specific configs.
// EMMG specfic configs. // To facilitate uniqueness of client_id, the following rules apply:
DEFINE_int32(client_id, 0, "EMMG client_id"); // in the case of EMMs or other CA related data, the two first bytes of the
DEFINE_int32(section_tspkt_flag, 1, "EMMG section_tspkt_flag"); // client_id should be equal to the two bytes of the corresponding CA_system_id.
DEFINE_int32(data_channel_id, 0, "EMMG data_channel_id"); ABSL_FLAG(int32_t, client_id, 0x4AD40000, "EMMG client_id.");
DEFINE_int32(data_stream_id, 0, "EMMG data_stream_id"); // TODO(user): Currently it can only support section_tspkt_flag = 1.
DEFINE_int32(data_id, 0, "EMMG data_id"); 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: // data_type: type of data carried in the datagram in the stream:
// 0x00: EMM; // 0x00: EMM;
// 0x01: private data; // 0x01: private data;
// 0x02: DVB reserved (ECM); // 0x02: DVB reserved (ECM);
// other values: DVB reserved. // 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) #define BUFFER_SIZE (1024)
void BuildEmmgConfig(widevine::cas::EmmgConfig *config) { void BuildEmmgConfig(widevine::cas::EmmgConfig *config) {
CHECK(config); CHECK(config);
config->client_id = FLAGS_client_id; config->client_id = absl::GetFlag(FLAGS_client_id);
config->section_tspkt_flag = FLAGS_section_tspkt_flag; config->section_tspkt_flag = absl::GetFlag(FLAGS_section_tspkt_flag);
config->data_channel_id = FLAGS_data_channel_id; config->data_channel_id = absl::GetFlag(FLAGS_data_channel_id);
config->data_stream_id = FLAGS_data_stream_id; config->data_stream_id = absl::GetFlag(FLAGS_data_stream_id);
config->data_id = FLAGS_data_id; config->data_id = absl::GetFlag(FLAGS_data_id);
config->data_type = FLAGS_data_type; 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) { int main(int argc, char **argv) {
gflags::ParseCommandLineFlags(&argc, &argv, true); absl::ParseCommandLine(argc, argv);
CHECK(!FLAGS_mux_address.empty()) << "flag --mux_address is required"; CHECK(!absl::GetFlag(FLAGS_mux_address).empty())
CHECK(FLAGS_mux_port != 0) << "flag --mux_port is required"; << "flag --mux_address is required";
CHECK(absl::GetFlag(FLAGS_mux_port) != 0) << "flag --mux_port is required";
// Create server address. // Create server address.
struct hostent ret; struct hostent ret;
struct hostent *server; struct hostent *server;
char buffer[BUFFER_SIZE]; char buffer[BUFFER_SIZE];
int h_errnop = 0; int h_errnop = 0;
int return_val = gethostbyname_r(FLAGS_mux_address.c_str(), &ret, buffer, int return_val =
sizeof(buffer), &server, &h_errnop); gethostbyname_r(absl::GetFlag(FLAGS_mux_address).c_str(), &ret, buffer,
sizeof(buffer), &server, &h_errnop);
if (return_val != 0 || server == nullptr) { 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; struct sockaddr_in server_address;
bzero(reinterpret_cast<char *>(&server_address), sizeof(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, bcopy(server->h_addr,
reinterpret_cast<char *>(&server_address.sin_addr.s_addr), reinterpret_cast<char *>(&server_address.sin_addr.s_addr),
server->h_length); server->h_length);
server_address.sin_port = htons(FLAGS_mux_port); server_address.sin_port = htons(absl::GetFlag(FLAGS_mux_port));
// Connect to server. // Connect to server.
int server_socket_fd = socket(AF_INET, SOCK_STREAM, 0); int server_socket_fd = socket(AF_INET, SOCK_STREAM, 0);