From e7173c82cd4a551401ff601d709fb70f2f2f6115 Mon Sep 17 00:00:00 2001 From: Fang Yu Date: Fri, 18 Jan 2019 10:38:34 -0800 Subject: [PATCH] 1) Change return type to WvCasStatus for functions in wv_cas_types.cc. 2) Add a binary wv_cas_types_example. 3) Surface wv_cas_key_fetcher *source code* to partner to serve as an example of how they would make a HTTP request to acquire an entitlement key from license server. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=229953562 --- example/BUILD | 18 ++ example/test_ecmg_messages.h | 18 +- example/test_emmg_messages.h | 146 +++++++++++ example/wv_cas_ecm_example.cc | 66 ++++- example/wv_cas_types_example.cc | 37 +++ media_cas_packager_sdk/internal/BUILD | 50 +++- .../internal/ecmg_client_handler.cc | 168 +++++------- .../internal/ecmg_client_handler_test.cc | 22 +- .../internal/ecmg_constants.h | 49 ++-- media_cas_packager_sdk/internal/emmg.cc | 247 ++++++++++++++++++ media_cas_packager_sdk/internal/emmg.h | 80 ++++++ .../internal/emmg_constants.h | 68 +++++ media_cas_packager_sdk/internal/emmg_test.cc | 93 +++++++ .../internal/simulcrypt_constants.h | 19 ++ .../internal/simulcrypt_util.cc | 93 +++++++ .../internal/simulcrypt_util.h | 59 +++++ media_cas_packager_sdk/internal/util.cc | 7 + media_cas_packager_sdk/internal/util.h | 1 + media_cas_packager_sdk/public/BUILD | 13 + .../public/wv_cas_key_fetcher.cc | 2 - media_cas_packager_sdk/public/wv_cas_types.cc | 20 +- media_cas_packager_sdk/public/wv_cas_types.h | 8 +- .../public/wv_cas_types_test.cc | 6 +- media_cas_packager_sdk/public/wv_ecmg.cc | 4 +- media_cas_packager_sdk/public/wv_emmg.cc | 92 +++++++ 25 files changed, 1213 insertions(+), 173 deletions(-) create mode 100644 example/test_emmg_messages.h create mode 100644 example/wv_cas_types_example.cc create mode 100644 media_cas_packager_sdk/internal/emmg.cc create mode 100644 media_cas_packager_sdk/internal/emmg.h create mode 100644 media_cas_packager_sdk/internal/emmg_constants.h create mode 100644 media_cas_packager_sdk/internal/emmg_test.cc create mode 100644 media_cas_packager_sdk/internal/simulcrypt_constants.h create mode 100644 media_cas_packager_sdk/internal/simulcrypt_util.cc create mode 100644 media_cas_packager_sdk/internal/simulcrypt_util.h create mode 100644 media_cas_packager_sdk/public/wv_emmg.cc diff --git a/example/BUILD b/example/BUILD index 23cae36..acd7179 100644 --- a/example/BUILD +++ b/example/BUILD @@ -18,7 +18,9 @@ filegroup( srcs = [ "test_ecmg_messages.h", "wv_cas_ecm_example.cc", + "wv_cas_types_example.cc", ":wv_cas_ecm_example", + ":wv_cas_types_example", ], ) @@ -32,10 +34,16 @@ cc_library( hdrs = ["test_ecmg_messages.h"], ) +cc_library( + name = "test_emmg_messages", + hdrs = ["test_emmg_messages.h"], +) + cc_binary( name = "wv_cas_ecm_example", 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", @@ -52,3 +60,13 @@ cc_binary( "//protos/public:media_cas_encryption_proto", ], ) + +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", + ], +) diff --git a/example/test_ecmg_messages.h b/example/test_ecmg_messages.h index 36e4a77..b4c7ae5 100644 --- a/example/test_ecmg_messages.h +++ b/example/test_ecmg_messages.h @@ -14,7 +14,7 @@ namespace widevine { namespace cas { -const char kTestChannelSetup[] = { +const char kTestEcmgChannelSetup[] = { '\x03', // protocol_version '\x00', '\x01', // message_type - Channel_setup '\x00', '\x0e', // message_length @@ -26,7 +26,7 @@ const char kTestChannelSetup[] = { '\x4a', '\xd4', '\x00', '\x00' // parameter_value }; -const char kTestChannelStatus[] = { +const char kTestEcmgChannelStatus[] = { '\x03', // protocol_version '\x00', '\x03', // message_type - Channel_status '\x00', '\x39', // message_length @@ -62,7 +62,7 @@ const char kTestChannelStatus[] = { '\x00', '\x64' // parameter_value }; -const char kTestStreamSetup[] = { +const char kTestEcmgStreamSetup[] = { '\x03', // protocol_version '\x01', '\x01', // message_type - Stream_setup '\x00', '\x18', // message_length @@ -80,7 +80,7 @@ const char kTestStreamSetup[] = { '\x00', '\x64' // parameter_value }; -const char kTestStreamStatus[] = { +const char kTestEcmgStreamStatus[] = { '\x03', // protocol_version '\x01', '\x03', // message_type - Stream_status '\x00', '\x17', // message_length @@ -98,7 +98,7 @@ const char kTestStreamStatus[] = { '\x01' // parameter_value }; -const char kTestCwProvision[] = { +const char kTestEcmgCwProvision[] = { '\x03', // protocol_version '\x02', '\x01', // message_type - CW_provision '\x00', '\x44', // message_length @@ -126,7 +126,7 @@ const char kTestCwProvision[] = { '\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f'}; // CW is encrypted using hardcoded fixed entitlement key. -const char kTestEcmResponse[] = { +const char kTestEcmgEcmResponse[] = { '\x03', // protocol_version '\x02', '\x02', // message_type - ECM_response '\x00', '\xd2', // message_length @@ -164,7 +164,7 @@ const char kTestEcmResponse[] = { '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff'}; -const char kTestStreamCloseRequest[] = { +const char kTestEcmgStreamCloseRequest[] = { '\x03', // protocol_version '\x01', '\x04', // message_type - Stream_close_request '\x00', '\x0c', // message_length @@ -176,7 +176,7 @@ const char kTestStreamCloseRequest[] = { '\x00', '\x01' // parameter_value }; -const char kTestStreamCloseResponse[] = { +const char kTestEcmgStreamCloseResponse[] = { '\x03', // protocol_version '\x01', '\x05', // message_type - Stream_close_response '\x00', '\x0c', // message_length @@ -188,7 +188,7 @@ const char kTestStreamCloseResponse[] = { '\x00', '\x01' // parameter_value }; -const char kTestChannelClose[] = { +const char kTestEcmgChannelClose[] = { '\x03', // protocol_version '\x00', '\x04', // message_type - Channel_close '\x00', '\x06', // message_length diff --git a/example/test_emmg_messages.h b/example/test_emmg_messages.h new file mode 100644 index 0000000..8f5df6f --- /dev/null +++ b/example/test_emmg_messages.h @@ -0,0 +1,146 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2019 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +// Example EMMG messages used in unit tests. + +#ifndef MEDIA_CAS_PACKAGER_SDK_EXAMPLE_TEST_EMMG_MESSAGES_H_ +#define MEDIA_CAS_PACKAGER_SDK_EXAMPLE_TEST_EMMG_MESSAGES_H_ + +namespace widevine { +namespace cas { + +const char kTestEmmgTsPacket[] = { + '\x47', '\x5F', '\xFF', '\x10', '\x00', '\x82', '\x70', '\x61', '\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', '\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', + '\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'}; + +const char kTestEmmgChannelSetup[] = { + '\x02', // protocol_version + '\x00', '\x11', // message_type - Channel_setup + '\x00', '\x13', // message_length + '\x00', '\x01', // parameter_type - client_id + '\x00', '\x04', // parameter_length + '\x4a', '\xd4', '\x00', '\x00', // parameter_value + '\x00', '\x03', // parameter_type - data_channel_id + '\x00', '\x02', // parameter_length + '\x00', '\x01', // parameter_value + '\x00', '\x02', // parameter_type - section_TSpkt_flag + '\x00', '\x01', // parameter_length + '\x01' // parameter_value +}; + +const char kTestEmmgStreamSetup[] = { + '\x02', // protocol_version + '\x01', '\x11', // message_type - Stream_setup + '\x00', '\x1f', // message_length + '\x00', '\x01', // parameter_type - client_id + '\x00', '\x04', // parameter_length + '\x4a', '\xd4', '\x00', '\x00', // parameter_value + '\x00', '\x03', // parameter_type - data_channel_id + '\x00', '\x02', // parameter_length + '\x00', '\x01', // parameter_value + '\x00', '\x04', // parameter_type - data_stream_id + '\x00', '\x02', // parameter_length + '\x00', '\x01', // parameter_value + '\x00', '\x08', // parameter_type - data_id + '\x00', '\x02', // parameter_length + '\x00', '\x01', // parameter_value + '\x00', '\x07', // parameter_type - data_type + '\x00', '\x01', // parameter_length + '\x01' // parameter_value - private data +}; + +const char kTestEmmgDataProvision[] = { + '\x02', // protocol_version + '\x02', '\x11', // message_type - Data_provision + '\x00', '\xda', // message_length + '\x00', '\x01', // parameter_type - client_id + '\x00', '\x04', // parameter_length + '\x4a', '\xd4', '\x00', '\x00', // parameter_value + '\x00', '\x03', // parameter_type - data_channel_id + '\x00', '\x02', // parameter_length + '\x00', '\x01', // parameter_value + '\x00', '\x04', // parameter_type - data_stream_id + '\x00', '\x02', // parameter_length + '\x00', '\x01', // parameter_value + '\x00', '\x08', // parameter_type - data_id + '\x00', '\x02', // parameter_length + '\x00', '\x01', // parameter_value + '\x00', '\x05', // parameter_type - datagram + '\x00', '\xbc', // parameter_length + '\x47', '\x5f', '\xff', '\x10', '\x00', '\x82', '\x70', '\x61', '\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', '\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', + '\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'}; + +const char kTestEmmgStreamCloseRequest[] = { + '\x02', // protocol_version + '\x01', '\x14', // message_type - Stream_close_request + '\x00', '\x14', // message_length + '\x00', '\x01', // parameter_type - client_id + '\x00', '\x04', // parameter_length + '\x4a', '\xd4', '\x00', '\x00', // parameter_value + '\x00', '\x03', // parameter_type - data_channel_id + '\x00', '\x02', // parameter_length + '\x00', '\x01', // parameter_value + '\x00', '\x04', // parameter_type - data_stream_id + '\x00', '\x02', // parameter_length + '\x00', '\x01' // parameter_value +}; + +const char kTestEmmgChannelClose[] = { + '\x02', // protocol_version + '\x00', '\x14', // message_type - Channel_close + '\x00', '\x0e', // message_length + '\x00', '\x01', // parameter_type - client_id + '\x00', '\x04', // parameter_length + '\x4a', '\xd4', '\x00', '\x00', // parameter_value + '\x00', '\x03', // parameter_type - data_channel_id + '\x00', '\x02', // parameter_length + '\x00', '\x01' // parameter_value +}; + +} // namespace cas +} // namespace widevine + +#endif // MEDIA_CAS_PACKAGER_SDK_EXAMPLE_TEST_EMMG_MESSAGES_H_ diff --git a/example/wv_cas_ecm_example.cc b/example/wv_cas_ecm_example.cc index 4c5dab4..d59a4d9 100644 --- a/example/wv_cas_ecm_example.cc +++ b/example/wv_cas_ecm_example.cc @@ -8,12 +8,23 @@ // Example of how to use the wv_cas_ecm library. +#include +#include #include #include +#include "gflags/gflags.h" #include "media_cas_packager_sdk/public/wv_cas_ecm.h" #include "media_cas_packager_sdk/public/wv_cas_types.h" +DEFINE_int32(content_iv_size, 8, "Content IV size"); +DEFINE_bool(key_rotation, true, "Whether key rotation is enabled"); +DEFINE_string(crypto_mode, "CSA2", "Only CBC, CTR, or CSA2 is allowed"); +DEFINE_int32(ecm_pid, 149, "PID for the ECM packet"); +DEFINE_string(output_file, "", + "If specified, generated ECM TS packet will be written to the " + "specified output file path"); + const char kCsaEvenKey[] = "even_key"; // 8 bytes const char kEvenContentIv8Bytes[] = "even_iv."; // 8 bytes const char kEvenEntitlementKeyId[] = "fake_key_id1...."; // 16 bytes @@ -24,26 +35,45 @@ const char kOddContentIv8Bytes[] = "odd_iv.."; // 8 bytes const char kOddEntitlementKeyId[] = "fake_key_id2...."; // 16 bytes const char kOddEntitlementKey[] = "fakefakefakefakefakefakefake2..."; // 32 bytes +const size_t kTsPacketSize = 188; + +widevine::cas::CryptoMode GetCryptoMode() { + if (FLAGS_crypto_mode.compare("CBC") == 0) { + return widevine::cas::CryptoMode::kAesCbc; + } + if (FLAGS_crypto_mode.compare("CTR") == 0) { + return widevine::cas::CryptoMode::kAesCtr; + } + return widevine::cas::CryptoMode::kDvbCsa2; +} int main(int argc, char **argv) { + gflags::ParseCommandLineFlags(&argc, &argv, true); + // Generate ECM. widevine::cas::WvCasEcm wv_cas_ecm; widevine::cas::WvCasStatus status = wv_cas_ecm.Initialize( - /* content_iv_size= */ 8, /* key_rotation_enabled= */ true, - widevine::cas::CryptoMode::kDvbCsa2); + FLAGS_content_iv_size, FLAGS_key_rotation, GetCryptoMode()); if (status != widevine::cas::OK) { std::cerr << "Failed to initialize WV CAS ECM, error: " << widevine::cas::GetWvCasStatusMessage(status) << std::endl; } std::string ecm; - status = wv_cas_ecm.GenerateEcm( - kCsaEvenKey, kEvenContentIv8Bytes, kEvenEntitlementKeyId, - kEvenEntitlementKey, kCsaOddKey, kOddContentIv8Bytes, - kOddEntitlementKeyId, kOddEntitlementKey, &ecm); + if (FLAGS_key_rotation) { + status = wv_cas_ecm.GenerateEcm( + kCsaEvenKey, kEvenContentIv8Bytes, kEvenEntitlementKeyId, + kEvenEntitlementKey, kCsaOddKey, kOddContentIv8Bytes, + kOddEntitlementKeyId, kOddEntitlementKey, &ecm); + } else { + status = wv_cas_ecm.GenerateSingleKeyEcm(kCsaEvenKey, kEvenContentIv8Bytes, + kEvenEntitlementKeyId, + kEvenEntitlementKey, &ecm); + } if (status != widevine::cas::OK) { std::cerr << "Failed to generate WV CAS ECM, error: " << widevine::cas::GetWvCasStatusMessage(status) << std::endl; + return -1; } else { std::cout << "ECM size: " << ecm.size() << std::endl; std::cout << "ECM bytes: "; @@ -52,6 +82,30 @@ int main(int argc, char **argv) { } std::cout << std::endl; } + // Generate ECM TS Packet. + uint8_t packet[kTsPacketSize]; + uint8_t continuity_counter; // not used. + status = wv_cas_ecm.GenerateTsPacket(ecm, FLAGS_ecm_pid, + /* table_id= */ 0x80, + &continuity_counter, packet); + if (status != widevine::cas::OK) { + std::cerr << "Failed to create ECM TS packet" << std::endl; + return -1; + } else { + std::cout << "TS packet bytes: "; + for (size_t i = 0; i < kTsPacketSize; i++) { + printf("'\\x%02x', ", static_cast(packet[i])); + } + std::cout << std::endl; + } + // Write ECM TS Packet to a file. + if (!FLAGS_output_file.empty()) { + std::ofstream file; + file.open(FLAGS_output_file.c_str(), std::ios_base::binary); + assert(file.is_open()); + file.write(reinterpret_cast(packet), kTsPacketSize); + file.close(); + } return 0; } diff --git a/example/wv_cas_types_example.cc b/example/wv_cas_types_example.cc new file mode 100644 index 0000000..d116d76 --- /dev/null +++ b/example/wv_cas_types_example.cc @@ -0,0 +1,37 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2019 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +// Example of how to use types/functions in wv_cas_types. + +#include + +#include "gflags/gflags.h" +#include "glog/logging.h" +#include "media_cas_packager_sdk/public/wv_cas_types.h" + +DEFINE_string(function_to_call, "CreateWvCasEncryptionRequestJson", + "Function in wv_cas_types to exercise"); + +void CallCreateWvCasEncryptionRequestJson() { + widevine::cas::WvCasEncryptionRequest request; + request.content_id = "cont_id cont_id "; + request.provider = "widevine_test"; + request.track_types = {"SD"}; + request.key_rotation = true; + std::string request_json; + widevine::cas::CreateWvCasEncryptionRequestJson(request, &request_json); + LOG(INFO) << FLAGS_function_to_call << " returns " << request_json; +} + +int main(int argc, char **argv) { + gflags::ParseCommandLineFlags(&argc, &argv, true); + if (FLAGS_function_to_call.compare("CreateWvCasEncryptionRequestJson") == 0) { + CallCreateWvCasEncryptionRequestJson(); + } + return 0; +} diff --git a/media_cas_packager_sdk/internal/BUILD b/media_cas_packager_sdk/internal/BUILD index 050cc83..1960c90 100644 --- a/media_cas_packager_sdk/internal/BUILD +++ b/media_cas_packager_sdk/internal/BUILD @@ -56,9 +56,10 @@ cc_library( srcs = ["ecm_generator.cc"], hdrs = ["ecm_generator.h"], deps = [ + ":ecm", "//base", + "@abseil_repo//absl/base:core_headers", "//common:status", - "//media_cas_packager_sdk/internal:ecm", ], ) @@ -81,12 +82,14 @@ cc_library( hdrs = [ "ecmg_client_handler.h", "ecmg_constants.h", + "simulcrypt_constants.h", ], deps = [ ":ecm", ":ecm_generator", ":fixed_key_fetcher", ":mpeg2ts", + ":simulcrypt_util", ":util", "//base", "@abseil_repo//absl/base:core_headers", @@ -112,6 +115,36 @@ cc_test( ], ) +cc_library( + name = "emmg", + srcs = ["emmg.cc"], + hdrs = [ + "emmg.h", + "emmg_constants.h", + "simulcrypt_constants.h", + ], + deps = [ + ":simulcrypt_util", + ":util", + "//base", + "@abseil_repo//absl/base:core_headers", + "//common:status", + "//example:test_emmg_messages", + ], +) + +cc_test( + name = "emmg_test", + size = "small", + srcs = ["emmg_test.cc"], + deps = [ + ":emmg", + "//testing:gunit_main", + "@abseil_repo//absl/memory", + "//example:test_emmg_messages", + ], +) + cc_library( name = "fixed_key_fetcher", srcs = [ @@ -146,6 +179,21 @@ cc_library( ], ) +cc_library( + name = "simulcrypt_util", + srcs = ["simulcrypt_util.cc"], + hdrs = [ + "simulcrypt_constants.h", + "simulcrypt_util.h", + ], + deps = [ + ":util", + "//base", + "@abseil_repo//absl/base:core_headers", + "//common:status", + ], +) + cc_library( name = "ts_packet", srcs = [ diff --git a/media_cas_packager_sdk/internal/ecmg_client_handler.cc b/media_cas_packager_sdk/internal/ecmg_client_handler.cc index 59b1b51..797906d 100644 --- a/media_cas_packager_sdk/internal/ecmg_client_handler.cc +++ b/media_cas_packager_sdk/internal/ecmg_client_handler.cc @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -19,10 +20,13 @@ #include "absl/memory/memory.h" #include "absl/strings/str_cat.h" #include "common/crypto_util.h" +#include "common/status.h" #include "example/constants.h" #include "media_cas_packager_sdk/internal/ecm_generator.h" #include "media_cas_packager_sdk/internal/ecmg_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/util.h" #include "media_cas_packager_sdk/public/wv_cas_ecm.h" #include "media_cas_packager_sdk/public/wv_cas_types.h" @@ -183,81 +187,16 @@ Status HandleParameters(const char* const request, size_t request_length, return OkStatus(); } -// Add 'protocol_version', 'message_type', 'message_length' to the message. -// TODO(user): Per jfore@, consider pass in a pointer to a structure -// #pragma pack(push, 1) // exact fit - no padding -// struct MessageHeader{ -// uint8_t protocol_version; -// uint16_t message_type; -// uint16_t message_length; -// }; -// #pragma pack(pop) // restore previous pack -void BuildMessageHeader(uint16_t message_type, char* message, - size_t* message_length) { - DCHECK(message); - DCHECK(message_length); - *message_length = 0; - message[*message_length] = ECMG_SCS_PROTOCOL_VERSION; - *message_length += PROTOCOL_VERSION_SIZE; - Host16ToBigEndian(message + *message_length, &message_type); - *message_length += MESSAGE_TYPE_SIZE; - // NOTE: 'message_length' needs to be updated later after we have added all - // the params so we know the exact length of the all the params. - // Use 0 for 'total_param_length' until we know the length of the message. - uint16_t total_param_length = 0; - Host16ToBigEndian(message + *message_length, &total_param_length); - *message_length += MESSAGE_LENGTH_SIZE; -} - -// Add a uint16_t parameter to the message. -void AddUint16Param(uint16_t param_type, uint16_t param_value, char* message, - size_t* message_length) { - DCHECK(message); - DCHECK(message_length); - Host16ToBigEndian(message + *message_length, ¶m_type); - *message_length += 2; - uint16_t param_length = 2; - Host16ToBigEndian(message + *message_length, ¶m_length); - *message_length += 2; - Host16ToBigEndian(message + *message_length, ¶m_value); - *message_length += param_length; -} - -// Add a uint8_t parameter to the message. -void AddUint8Param(uint16_t param_type, uint8_t param_value, char* message, - size_t* message_length) { - DCHECK(message); - DCHECK(message_length); - Host16ToBigEndian(message + *message_length, ¶m_type); - *message_length += 2; - uint16_t param_length = 1; - Host16ToBigEndian(message + *message_length, ¶m_length); - *message_length += 2; - memcpy(message + *message_length, ¶m_value, param_length); - *message_length += param_length; -} - -// Add a param that is |param_length| bytes long. -void AddParam(uint16_t param_type, uint8_t* param_value, uint16_t param_length, - char* message, size_t* message_length) { - DCHECK(param_value); - DCHECK(message); - DCHECK(message_length); - Host16ToBigEndian(message + *message_length, ¶m_type); - *message_length += 2; - Host16ToBigEndian(message + *message_length, ¶m_length); - *message_length += 2; - memcpy(message + *message_length, param_value, param_length); - *message_length += param_length; -} - void BuildChannelError(uint16_t channel_id, uint16_t error_status, char* message, size_t* message_length) { DCHECK(message); DCHECK(message_length); - BuildMessageHeader(ECMG_CHANNEL_ERROR, message, message_length); - AddUint16Param(ECM_CHANNEL_ID, channel_id, message, message_length); - AddUint16Param(ERROR_STATUS, error_status, message, message_length); + simulcrypt_util::BuildMessageHeader( + ECMG_SCS_PROTOCOL_VERSION, ECMG_CHANNEL_ERROR, message, message_length); + simulcrypt_util::AddUint16Param(ECM_CHANNEL_ID, channel_id, message, + message_length); + simulcrypt_util::AddUint16Param(ERROR_STATUS, error_status, message, + message_length); // No setting Error_information parameter yet. uint16_t total_param_length = *message_length - 5; Host16ToBigEndian(message + 3, &total_param_length); @@ -268,25 +207,33 @@ void BuildChannelStatus(uint16_t channel_id, EcmgConfig* config, char* message, DCHECK(config); DCHECK(message); DCHECK(message_length); - BuildMessageHeader(ECMG_CHANNEL_STATUS, message, message_length); - AddUint16Param(ECM_CHANNEL_ID, channel_id, message, message_length); - AddUint8Param(SECTION_TSPKT_FLAG, kSectionTSpktFlag, message, message_length); + simulcrypt_util::BuildMessageHeader( + ECMG_SCS_PROTOCOL_VERSION, ECMG_CHANNEL_STATUS, message, message_length); + simulcrypt_util::AddUint16Param(ECM_CHANNEL_ID, channel_id, message, + message_length); + simulcrypt_util::AddUint8Param(SECTION_TSPKT_FLAG, kSectionTSpktFlag, message, + message_length); // No setting AC_delay_start parameter yet. // No setting AC_delay_stop parameter yet. - AddUint16Param(DELAY_START, config->delay_start, message, message_length); - AddUint16Param(DELAY_STOP, config->delay_stop, message, message_length); + simulcrypt_util::AddUint16Param(DELAY_START, config->delay_start, message, + message_length); + simulcrypt_util::AddUint16Param(DELAY_STOP, config->delay_stop, message, + message_length); // No setting transition_delay_start parameter yet. // No setting transition_delay_stop parameter yet. - AddUint16Param(ECM_REP_PERIOD, config->ecm_rep_period, message, - message_length); - AddUint16Param(MAX_STREAMS, kMaxStream, message, message_length); + simulcrypt_util::AddUint16Param(ECM_REP_PERIOD, config->ecm_rep_period, + message, message_length); + simulcrypt_util::AddUint16Param(MAX_STREAMS, kMaxStream, message, + message_length); // min_CP_duration needs to be at least max_comp_time. So we just use // max_comp_time here for now. - AddUint16Param(MIN_CP_DURATION, config->max_comp_time, message, - message_length); - AddUint8Param(LEAD_CW, kLeadCw, message, message_length); - AddUint8Param(CW_PER_MESSAGE, kCwPerMsg, message, message_length); - AddUint16Param(MAX_COMP_TIME, config->max_comp_time, message, message_length); + simulcrypt_util::AddUint16Param(MIN_CP_DURATION, config->max_comp_time, + message, message_length); + simulcrypt_util::AddUint8Param(LEAD_CW, kLeadCw, message, message_length); + simulcrypt_util::AddUint8Param(CW_PER_MESSAGE, kCwPerMsg, message, + message_length); + simulcrypt_util::AddUint16Param(MAX_COMP_TIME, config->max_comp_time, message, + message_length); uint16_t total_param_length = *message_length - 5; Host16ToBigEndian(message + 3, &total_param_length); } @@ -295,10 +242,14 @@ void BuildStreamError(uint16_t channel_id, uint16_t stream_id, uint16_t error_st char* message, size_t* message_length) { DCHECK(message); DCHECK(message_length); - BuildMessageHeader(ECMG_STREAM_ERROR, message, message_length); - AddUint16Param(ECM_CHANNEL_ID, channel_id, message, message_length); - AddUint16Param(ECM_STREAM_ID, stream_id, message, message_length); - AddUint16Param(ERROR_STATUS, error_status, message, message_length); + simulcrypt_util::BuildMessageHeader( + ECMG_SCS_PROTOCOL_VERSION, ECMG_STREAM_ERROR, message, message_length); + simulcrypt_util::AddUint16Param(ECM_CHANNEL_ID, channel_id, message, + message_length); + simulcrypt_util::AddUint16Param(ECM_STREAM_ID, stream_id, message, + message_length); + simulcrypt_util::AddUint16Param(ERROR_STATUS, error_status, message, + message_length); // No setting Error_information parameter yet. uint16_t total_param_length = *message_length - 5; Host16ToBigEndian(message + 3, &total_param_length); @@ -309,12 +260,16 @@ void BuildStreamStatus(uint16_t channel_id, uint16_t stream_id, uint16_t ecm_id, size_t* message_length) { DCHECK(message); DCHECK(message_length); - BuildMessageHeader(ECMG_STREAM_STATUS, message, message_length); - AddUint16Param(ECM_CHANNEL_ID, channel_id, message, message_length); - AddUint16Param(ECM_STREAM_ID, stream_id, message, message_length); - AddUint16Param(ECM_ID, ecm_id, message, message_length); - AddUint8Param(ACCESS_CRITERIA_TRANSFER_MODE, access_criteria_transfer_mode, - message, message_length); + simulcrypt_util::BuildMessageHeader( + ECMG_SCS_PROTOCOL_VERSION, ECMG_STREAM_STATUS, message, message_length); + simulcrypt_util::AddUint16Param(ECM_CHANNEL_ID, channel_id, message, + message_length); + simulcrypt_util::AddUint16Param(ECM_STREAM_ID, stream_id, message, + message_length); + simulcrypt_util::AddUint16Param(ECM_ID, ecm_id, message, message_length); + simulcrypt_util::AddUint8Param(ACCESS_CRITERIA_TRANSFER_MODE, + access_criteria_transfer_mode, message, + message_length); uint16_t total_param_length = *message_length - 5; Host16ToBigEndian(message + 3, &total_param_length); } @@ -323,9 +278,13 @@ void BuildStreamCloseResponse(uint16_t channel_id, uint16_t stream_id, char* message, size_t* message_length) { DCHECK(message); DCHECK(message_length); - BuildMessageHeader(ECMG_STREAM_CLOSE_RESPONSE, message, message_length); - AddUint16Param(ECM_CHANNEL_ID, channel_id, message, message_length); - AddUint16Param(ECM_STREAM_ID, stream_id, message, message_length); + simulcrypt_util::BuildMessageHeader(ECMG_SCS_PROTOCOL_VERSION, + ECMG_STREAM_CLOSE_RESPONSE, message, + message_length); + simulcrypt_util::AddUint16Param(ECM_CHANNEL_ID, channel_id, message, + message_length); + simulcrypt_util::AddUint16Param(ECM_STREAM_ID, stream_id, message, + message_length); uint16_t total_param_length = *message_length - 5; Host16ToBigEndian(message + 3, &total_param_length); } @@ -336,11 +295,16 @@ void BuildEcmResponse(uint16_t channel_id, uint16_t stream_id, uint16_t cp_numbe DCHECK(ecm_datagram); DCHECK(message); DCHECK(message_length); - BuildMessageHeader(ECMG_ECM_RESPONSE, message, message_length); - AddUint16Param(ECM_CHANNEL_ID, channel_id, message, message_length); - AddUint16Param(ECM_STREAM_ID, stream_id, message, message_length); - AddUint16Param(CP_NUMBER, cp_number, message, message_length); - AddParam(ECM_DATAGRAM, ecm_datagram, kTsPacketSize, message, message_length); + simulcrypt_util::BuildMessageHeader( + ECMG_SCS_PROTOCOL_VERSION, ECMG_ECM_RESPONSE, message, message_length); + simulcrypt_util::AddUint16Param(ECM_CHANNEL_ID, channel_id, message, + message_length); + simulcrypt_util::AddUint16Param(ECM_STREAM_ID, stream_id, message, + message_length); + simulcrypt_util::AddUint16Param(CP_NUMBER, cp_number, message, + message_length); + simulcrypt_util::AddParam(ECM_DATAGRAM, ecm_datagram, kTsPacketSize, message, + message_length); uint16_t total_param_length = *message_length - 5; Host16ToBigEndian(message + 3, &total_param_length); } 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 3220d90..786d574 100644 --- a/media_cas_packager_sdk/internal/ecmg_client_handler_test.cc +++ b/media_cas_packager_sdk/internal/ecmg_client_handler_test.cc @@ -56,27 +56,31 @@ TEST_F(EcmgClientHandlerTest, SuccessSequence) { char response[BUFFER_SIZE]; size_t response_length; - client_handler_->HandleRequest(kTestChannelSetup, response, &response_length); + client_handler_->HandleRequest(kTestEcmgChannelSetup, response, + &response_length); EXPECT_EQ(62, response_length); - EXPECT_EQ(0, memcmp(kTestChannelStatus, response, response_length)); + EXPECT_EQ(0, memcmp(kTestEcmgChannelStatus, response, response_length)); - client_handler_->HandleRequest(kTestStreamSetup, response, &response_length); + client_handler_->HandleRequest(kTestEcmgStreamSetup, response, + &response_length); EXPECT_EQ(28, response_length); - EXPECT_EQ(0, memcmp(kTestStreamStatus, response, response_length)); + EXPECT_EQ(0, memcmp(kTestEcmgStreamStatus, response, response_length)); - client_handler_->HandleRequest(kTestCwProvision, response, &response_length); + client_handler_->HandleRequest(kTestEcmgCwProvision, response, + &response_length); EXPECT_EQ(215, response_length); // Only comparing the bytes in front of the ECM_datagram, because // random wrapping IV is generated each time causing the ECM_datagram // to be non-deterministic. - EXPECT_EQ(0, memcmp(kTestEcmResponse, response, 27)); + EXPECT_EQ(0, memcmp(kTestEcmgEcmResponse, response, 27)); - client_handler_->HandleRequest(kTestStreamCloseRequest, response, + client_handler_->HandleRequest(kTestEcmgStreamCloseRequest, response, &response_length); EXPECT_EQ(17, response_length); - EXPECT_EQ(0, memcmp(kTestStreamCloseResponse, response, response_length)); + EXPECT_EQ(0, memcmp(kTestEcmgStreamCloseResponse, response, response_length)); - client_handler_->HandleRequest(kTestChannelClose, response, &response_length); + client_handler_->HandleRequest(kTestEcmgChannelClose, response, + &response_length); EXPECT_EQ(0, response_length); } diff --git a/media_cas_packager_sdk/internal/ecmg_constants.h b/media_cas_packager_sdk/internal/ecmg_constants.h index 618021a..d44fd40 100644 --- a/media_cas_packager_sdk/internal/ecmg_constants.h +++ b/media_cas_packager_sdk/internal/ecmg_constants.h @@ -62,35 +62,30 @@ // ECMG protocol error values. #define INVAID_MESSAGE (0x0001) -#define UNSUPPORTED_PROTOCOL_VERSION (0X0002) -#define UNKNOWN_MESSAGE_TYPE_VALUE (0X0003) -#define MESSAGE_TOO_LONG (0X0004) -#define UNKNOWN_SUPER_CAS_ID_VALUE (0X0005) -#define UNKNOWN_ECM_CHANNEL_ID_VALUE (0X0006) -#define UNKNOWN_ECM_STREAM_ID_VALUE (0X0007) -#define TOO_MANY_CHANNELS_ON_THIS_ECMG (0X0008) -#define TOO_MANY_ECM_STREAMS_ON_THIS_CHANNEL (0X0009) -#define TOO_MANY_ECM_STREAMS_ON_THIS_ECMG (0X000A) -#define NOT_ENOUGH_CONTROL_WORDS_TO_COMPUTE_ECM (0X000B) -#define ECMG_OUT_OF_STORAGE_CAPACITY (0X000C) -#define ECMG_OUT_OF_COMPUTATIONAL_RESOURCES (0X000D) -#define UNKNOWN_PARAMETER_TYPE_VALUE (0X000E) -#define INCONSISTENT_LENGTH_FOR_DVB_PARAMETER (0X000F) -#define MISSING_MANDATORY_DVB_PARAMETER (0X0010) -#define INVALID_VALUE_FOR_DVB_PARAMETER (0X0011) -#define UNKNOWN_ECM_ID_VALUE (0X0012) -#define ECM_CHANNEL_ID_VALUE_ALREADY_IN_USE (0X0013) -#define ECM_STREAM_ID_VALUE_ALREADY_IN_USE (0X0014) -#define ECM_ID_VALUE_ALREADY_IN_USE (0X0015) -#define UNKNOWN_ERROR (0X7000) -#define UNRECOVERABLE_ERROR (0X7001) +#define UNSUPPORTED_PROTOCOL_VERSION (0x0002) +#define UNKNOWN_MESSAGE_TYPE_VALUE (0x0003) +#define MESSAGE_TOO_LONG (0x0004) +#define UNKNOWN_SUPER_CAS_ID_VALUE (0x0005) +#define UNKNOWN_ECM_CHANNEL_ID_VALUE (0x0006) +#define UNKNOWN_ECM_STREAM_ID_VALUE (0x0007) +#define TOO_MANY_CHANNELS_ON_THIS_ECMG (0x0008) +#define TOO_MANY_ECM_STREAMS_ON_THIS_CHANNEL (0x0009) +#define TOO_MANY_ECM_STREAMS_ON_THIS_ECMG (0x000A) +#define NOT_ENOUGH_CONTROL_WORDS_TO_COMPUTE_ECM (0x000B) +#define ECMG_OUT_OF_STORAGE_CAPACITY (0x000C) +#define ECMG_OUT_OF_COMPUTATIONAL_RESOURCES (0x000D) +#define UNKNOWN_PARAMETER_TYPE_VALUE (0x000E) +#define INCONSISTENT_LENGTH_FOR_DVB_PARAMETER (0x000F) +#define MISSING_MANDATORY_DVB_PARAMETER (0x0010) +#define INVALID_VALUE_FOR_DVB_PARAMETER (0x0011) +#define UNKNOWN_ECM_ID_VALUE (0x0012) +#define ECM_CHANNEL_ID_VALUE_ALREADY_IN_USE (0x0013) +#define ECM_STREAM_ID_VALUE_ALREADY_IN_USE (0x0014) +#define ECM_ID_VALUE_ALREADY_IN_USE (0x0015) +#define UNKNOWN_ERROR (0x7000) +#define UNRECOVERABLE_ERROR (0x7001) // Size (in # of bytes) of various fields. -#define PROTOCOL_VERSION_SIZE (1) -#define MESSAGE_TYPE_SIZE (2) -#define MESSAGE_LENGTH_SIZE (2) -#define PARAMETER_TYPE_SIZE (2) -#define PARAMETER_LENGTH_SIZE (2) #define SUPER_CAS_ID_SIZE (4) #define ECM_CHANNEL_ID_SIZE (2) #define ECM_ID_SIZE (2) diff --git a/media_cas_packager_sdk/internal/emmg.cc b/media_cas_packager_sdk/internal/emmg.cc new file mode 100644 index 0000000..5e7e0a3 --- /dev/null +++ b/media_cas_packager_sdk/internal/emmg.cc @@ -0,0 +1,247 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2019 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "media_cas_packager_sdk/internal/emmg.h" + +#include +#include +#include +#include +#include + +#include "glog/logging.h" +#include "example/test_emmg_messages.h" +#include "media_cas_packager_sdk/internal/emmg_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/util.h" + +namespace widevine { +namespace cas { + +namespace { + +void Print(const char* const msg, size_t msg_size) { + for (size_t i = 0; i < msg_size; i++) { + printf("'\\x%02x', ", static_cast(*(msg + i))); + } + std::cout << std::endl; +} + +std::string MessageTypeToString(uint16_t message_type) { + switch (message_type) { + case EMMG_CHANNEL_SETUP: + return "EMMG_CHANNEL_SETUP"; + case EMMG_CHANNEL_STATUS: + return "EMMG_CHANNEL_STATUS"; + case EMMG_CHANNEL_CLOSE: + return "EMMG_CHANNEL_CLOSE"; + case EMMG_CHANNEL_ERROR: + return "EMMG_CHANNEL_ERROR"; + case EMMG_STREAM_SETUP: + return "EMMG_STREAM_SETUP"; + case EMMG_STREAM_TEST: + return "EMMG_STREAM_TEST"; + case EMMG_STREAM_STATUS: + return "EMMG_STREAM_STATUS"; + case EMMG_STREAM_CLOSE_REQUEST: + return "EMMG_STREAM_CLOSE_REQUEST"; + case EMMG_STREAM_CLOSE_RESPONSE: + return "EMMG_STREAM_CLOSE_RESPONSE"; + case EMMG_STREAM_ERROR: + return "EMMG_STREAM_ERROR"; + case EMMG_STREAM_BW_REQUEST: + return "EMMG_STREAM_BW_REQUEST"; + case EMMG_STREAM_BW_ALLOCATION: + return ""; + case EMMG_DATA_PROVISION: + return "EMMG_DATA_PROVISION"; + default: + return "UNRECOGNIZED_MESSAGE_TYPE"; + } +} + +} // namespace + +Emmg::Emmg(EmmgConfig* emmg_config, int server_socket_fd) { + CHECK(emmg_config); + emmg_config_ = emmg_config; + server_socket_fd_ = server_socket_fd; +} + +void Emmg::Start() { + SendChannelSetup(); + SendStreamSetup(); + // TODO(user): Send stream_BW_request message. + // TODO(user): Send stream_BW_allocation message. + SendDataProvision(); + SendStreamCloseRequest(); + SendChannelClose(); +} + +void Emmg::BuildChannelSetup() { + bzero(request_, BUFFER_SIZE); + request_length_ = 0; + simulcrypt_util::BuildMessageHeader(EMMG_MUX_PROTOCOL_VERSION, + EMMG_CHANNEL_SETUP, request_, + &request_length_); + simulcrypt_util::AddUint32Param(EMMG_CLIENT_ID, emmg_config_->client_id, + request_, &request_length_); + simulcrypt_util::AddUint16Param(EMMG_DATA_CHANNEL_ID, + emmg_config_->data_channel_id, request_, + &request_length_); + simulcrypt_util::AddUint8Param(EMMG_SECTION_TSPKT_FLAG, + emmg_config_->section_tspkt_flag, request_, + &request_length_); + uint16_t total_param_length = request_length_ - 5; + Host16ToBigEndian(request_ + 3, &total_param_length); +} + +void Emmg::BuildStreamSetup() { + bzero(request_, BUFFER_SIZE); + request_length_ = 0; + simulcrypt_util::BuildMessageHeader( + EMMG_MUX_PROTOCOL_VERSION, EMMG_STREAM_SETUP, request_, &request_length_); + simulcrypt_util::AddUint32Param(EMMG_CLIENT_ID, emmg_config_->client_id, + request_, &request_length_); + simulcrypt_util::AddUint16Param(EMMG_DATA_CHANNEL_ID, + emmg_config_->data_channel_id, request_, + &request_length_); + simulcrypt_util::AddUint16Param(EMMG_DATA_STREAM_ID, + emmg_config_->data_stream_id, request_, + &request_length_); + simulcrypt_util::AddUint16Param(EMMG_DATA_ID, emmg_config_->data_id, request_, + &request_length_); + simulcrypt_util::AddUint8Param(EMMG_DATA_TYPE, emmg_config_->data_type, + request_, &request_length_); + uint16_t total_param_length = request_length_ - 5; + Host16ToBigEndian(request_ + 3, &total_param_length); +} + +void Emmg::BuildDataProvision() { + bzero(request_, BUFFER_SIZE); + request_length_ = 0; + simulcrypt_util::BuildMessageHeader(EMMG_MUX_PROTOCOL_VERSION, + EMMG_DATA_PROVISION, request_, + &request_length_); + simulcrypt_util::AddUint32Param(EMMG_CLIENT_ID, emmg_config_->client_id, + request_, &request_length_); + simulcrypt_util::AddUint16Param(EMMG_DATA_CHANNEL_ID, + emmg_config_->data_channel_id, request_, + &request_length_); + simulcrypt_util::AddUint16Param(EMMG_DATA_STREAM_ID, + emmg_config_->data_stream_id, request_, + &request_length_); + 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; + + uint16_t total_param_length = request_length_ - 5; + Host16ToBigEndian(request_ + 3, &total_param_length); +} + +void Emmg::BuildStreamCloseRequest() { + bzero(request_, BUFFER_SIZE); + request_length_ = 0; + simulcrypt_util::BuildMessageHeader(EMMG_MUX_PROTOCOL_VERSION, + EMMG_STREAM_CLOSE_REQUEST, request_, + &request_length_); + simulcrypt_util::AddUint32Param(EMMG_CLIENT_ID, emmg_config_->client_id, + request_, &request_length_); + simulcrypt_util::AddUint16Param(EMMG_DATA_CHANNEL_ID, + emmg_config_->data_channel_id, request_, + &request_length_); + simulcrypt_util::AddUint16Param(EMMG_DATA_STREAM_ID, + emmg_config_->data_stream_id, request_, + &request_length_); + uint16_t total_param_length = request_length_ - 5; + Host16ToBigEndian(request_ + 3, &total_param_length); +} + +void Emmg::BuildChannelClose() { + bzero(request_, BUFFER_SIZE); + request_length_ = 0; + simulcrypt_util::BuildMessageHeader(EMMG_MUX_PROTOCOL_VERSION, + EMMG_CHANNEL_CLOSE, request_, + &request_length_); + simulcrypt_util::AddUint32Param(EMMG_CLIENT_ID, emmg_config_->client_id, + request_, &request_length_); + simulcrypt_util::AddUint16Param(EMMG_DATA_CHANNEL_ID, + emmg_config_->data_channel_id, request_, + &request_length_); + uint16_t total_param_length = request_length_ - 5; + Host16ToBigEndian(request_ + 3, &total_param_length); +} + +void Emmg::SendChannelSetup() { + BuildChannelSetup(); + Send(EMMG_CHANNEL_SETUP); + ReceiveResponseAndVerify(EMMG_CHANNEL_STATUS); +} + +void Emmg::SendStreamSetup() { + BuildStreamSetup(); + Send(EMMG_STREAM_SETUP); + ReceiveResponseAndVerify(EMMG_STREAM_STATUS); +} + +void Emmg::SendDataProvision() { + BuildDataProvision(); + Send(EMMG_DATA_PROVISION); + // No response for Data_provision message. +} + +void Emmg::SendStreamCloseRequest() { + BuildStreamCloseRequest(); + Send(EMMG_STREAM_CLOSE_REQUEST); + ReceiveResponseAndVerify(EMMG_STREAM_CLOSE_RESPONSE); +} + +void Emmg::SendChannelClose() { + BuildChannelClose(); + Send(EMMG_CHANNEL_CLOSE); + // No response for Channel_close message. +} + +void Emmg::ReceiveResponseAndVerify(uint16_t expected_type) { + // Read response from socket. + bzero(response_, BUFFER_SIZE); + response_length_ = recv(server_socket_fd_, response_, BUFFER_SIZE, 0); + if (response_length_ == 0) { + LOG(FATAL) << "Expect a response from server but nothing was received"; + } + // Verify request type (2nd and 3rd bytes) match the expected type. + uint16_t request_type; + BigEndianToHost16(&request_type, response_ + 1); + if (request_type != expected_type) { + LOG(ERROR) << "Unexpected non " << MessageTypeToString(expected_type) + << " type message received"; + LOG(ERROR) << "Response: "; + Print(response_, response_length_); + return; + } +} + +void Emmg::Send(uint16_t message_type) { + if (send(server_socket_fd_, request_, request_length_, 0) < 0) { + LOG(FATAL) << "Failed to send " << MessageTypeToString(message_type) + << " message to server"; + } +} + +} // namespace cas +} // namespace widevine diff --git a/media_cas_packager_sdk/internal/emmg.h b/media_cas_packager_sdk/internal/emmg.h new file mode 100644 index 0000000..5dd131c --- /dev/null +++ b/media_cas_packager_sdk/internal/emmg.h @@ -0,0 +1,80 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2019 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef MEDIA_CAS_PACKAGER_SDK_INTERNAL_EMMG_H_ +#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) + +namespace widevine { +namespace cas { + +// A struct that captures the Emmg configs. +struct EmmgConfig { + uint32_t client_id; + uint8_t section_tspkt_flag; + uint16_t data_channel_id; + uint16_t data_stream_id; + uint16_t data_id; + uint8_t data_type; +}; + +// A class that sends EMMG/PDG message to the MUX server. +class Emmg { + public: + Emmg(EmmgConfig* emmg_config, int server_socket_fd); + Emmg(const Emmg&) = delete; + Emmg& operator=(const Emmg&) = delete; + virtual ~Emmg() = default; + + void Start(); + + protected: + // Protected visibility for unit testing. + void BuildChannelSetup(); + void BuildStreamSetup(); + void BuildDataProvision(); + void BuildStreamCloseRequest(); + void BuildChannelClose(); + + size_t request_length_ = 0; + char request_[BUFFER_SIZE]; + + private: + void SendChannelSetup(); + void SendStreamSetup(); + void SendDataProvision(); + void SendStreamCloseRequest(); + void SendChannelClose(); + + void ReceiveResponseAndVerify(uint16_t expected_type); + void Send(uint16_t message_type); + + char response_[BUFFER_SIZE]; + size_t response_length_ = 0; + + EmmgConfig* emmg_config_; + // |server_socket_fd| is a file descriptor we can use to communicate + // with the MUX server. + int server_socket_fd_; +}; + +} // namespace cas +} // namespace widevine + +#endif // MEDIA_CAS_PACKAGER_SDK_INTERNAL_EMMG_H_ diff --git a/media_cas_packager_sdk/internal/emmg_constants.h b/media_cas_packager_sdk/internal/emmg_constants.h new file mode 100644 index 0000000..01d99b8 --- /dev/null +++ b/media_cas_packager_sdk/internal/emmg_constants.h @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2019 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef MEDIA_CAS_PACKAGER_SDK_INTERNAL_EMMG_CONSTANTS_H_ +#define MEDIA_CAS_PACKAGER_SDK_INTERNAL_EMMG_CONSTANTS_H_ + +// EMMG/PDG <=> MUX protocol_version. +#define EMMG_MUX_PROTOCOL_VERSION (0x02) + +// EMMG message type values. +#define EMMG_CHANNEL_SETUP (0x0011) +#define EMMG_CHANNEL_TEST (0x0012) +#define EMMG_CHANNEL_STATUS (0x0013) +#define EMMG_CHANNEL_CLOSE (0x0014) +#define EMMG_CHANNEL_ERROR (0x0015) +#define EMMG_STREAM_SETUP (0x0111) +#define EMMG_STREAM_TEST (0x0112) +#define EMMG_STREAM_STATUS (0x0113) +#define EMMG_STREAM_CLOSE_REQUEST (0x0114) +#define EMMG_STREAM_CLOSE_RESPONSE (0x0115) +#define EMMG_STREAM_ERROR (0x0116) +#define EMMG_STREAM_BW_REQUEST (0x0117) +#define EMMG_STREAM_BW_ALLOCATION (0x0118) +#define EMMG_DATA_PROVISION (0x0211) + +// EMMG parameter type values. +#define EMMG_DVB_RESERVED (0x0000) +#define EMMG_CLIENT_ID (0x0001) +#define EMMG_SECTION_TSPKT_FLAG (0x0002) +#define EMMG_DATA_CHANNEL_ID (0x0003) +#define EMMG_DATA_STREAM_ID (0x0004) +#define EMMG_DATAGRAM (0x0005) +#define EMMG_BANDWIDTH (0x0006) +#define EMMG_DATA_TYPE (0x0007) +#define EMMG_DATA_ID (0x0008) +#define EMMG_ERROR_STATUS (0x7000) +#define EMMG_ERROR_INFORMATION (0x7001) + +// EMMG protocol error values. +#define INVALID_MESSAGE (0x0001) +#define UNSUPPORTED_PROTOCOL_VERSION (0x0002) +#define UNKNOWN_MESSAGE_TYPE_VALUE (0x0003) +#define MESSAGE_TOO_LONG (0x0004) +#define UNKNOWN_DATA_STREAM_ID_VALUE (0x0005) +#define UNKNOWN_DATA_CHANNEL_ID_VALUE (0x0006) +#define TOO_MANY_CHANNELS_ON_THIS_MUX (0x0007) +#define TOO_MANY_DATA_STREAMS_ON_THIS_CHANNEL (0x0008) +#define TOO_MANY_DATA_STREAMS_ON_THIS_MUX (0x0009) +#define UNKNOWN_PARAMETER_TYPE (0x000A) +#define INCONSISTENT_LENGTH_FOR_DVB_PARAMETER (0x000B) +#define MISSING_MANDATORY_DVB_PARAMETER (0x000C) +#define INVALID_VALUE_FOR_DVB_PARAMETER (0x000D) +#define UNKNOWN_CLIENT_ID_VALUE (0x000E) +#define EXCEEDED_BANDWIDTH (0x000F) +#define UNKNOWN_DATA_ID_VALUE (0x0010) +#define DATA_CHANNEL_ID_VALUE_ALREADY_IN_USE (0x0011) +#define DATA_STREAM_ID_VALUE_ALREADY_IN_USE (0x0012) +#define DATA_ID_VALUE_ALREADY_IN_USE (0x0013) +#define CLIENT_ID_VALUE_ALREADY_IN_USE (0x0014) +#define UNKNOWN_ERROR (0x7000) +#define UNRECOVERABLE_ERROR (0x7001) + +#endif // MEDIA_CAS_PACKAGER_SDK_INTERNAL_EMMG_CONSTANTS_H_ diff --git a/media_cas_packager_sdk/internal/emmg_test.cc b/media_cas_packager_sdk/internal/emmg_test.cc new file mode 100644 index 0000000..2b38773 --- /dev/null +++ b/media_cas_packager_sdk/internal/emmg_test.cc @@ -0,0 +1,93 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2019 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "media_cas_packager_sdk/internal/emmg.h" + +#include "testing/gunit.h" +#include "absl/memory/memory.h" +#include "example/test_emmg_messages.h" + +namespace widevine { +namespace cas { +namespace { + +class TestableEmmg : public Emmg { + public: + explicit TestableEmmg(EmmgConfig* emmg_config) + : Emmg(emmg_config, /* server_socket_fd= */ 1) {} + void PublicBuildChannelSetup() { BuildChannelSetup(); } + void PublicBuildStreamSetup() { BuildStreamSetup(); } + void PublicBuildDataProvision() { BuildDataProvision(); } + void PublicBuildStreamCloseRequest() { BuildStreamCloseRequest(); } + void PublicBuildChannelClose() { BuildChannelClose(); } + char* GetRequest() { return request_; } +}; + +class EmmgTest : public ::testing::Test { + protected: + EmmgTest() { + config_.client_id = 0x4AD40000; + config_.section_tspkt_flag = 0x01; + config_.data_channel_id = 0x0001; + config_.data_stream_id = 0x0001; + config_.data_id = 0x0001; + config_.data_type = 0x01; + emmg_ = absl::make_unique(&config_); + } + + protected: + // Helper function for debugging the tests. + void PrintMessage(const std::string& description, const char* const message, + size_t length) { + printf("%s ", description.c_str()); + fflush(stdout); + for (size_t i = 0; i < length; i++) { + printf("'\\x%02x', ", static_cast(*(message + i))); + fflush(stdout); + } + printf("\n"); + fflush(stdout); + } + + EmmgConfig config_; + std::unique_ptr emmg_; +}; + +TEST_F(EmmgTest, BuildChannelSetup) { + emmg_->PublicBuildChannelSetup(); + EXPECT_EQ(0, memcmp(kTestEmmgChannelSetup, emmg_->GetRequest(), + sizeof(kTestEmmgChannelSetup))); +} + +TEST_F(EmmgTest, BuildStreamSetup) { + emmg_->PublicBuildStreamSetup(); + EXPECT_EQ(0, memcmp(kTestEmmgStreamSetup, emmg_->GetRequest(), + sizeof(kTestEmmgStreamSetup))); +} + +TEST_F(EmmgTest, BuildDataProvision) { + emmg_->PublicBuildDataProvision(); + EXPECT_EQ(0, memcmp(kTestEmmgDataProvision, emmg_->GetRequest(), + sizeof(kTestEmmgDataProvision))); +} + +TEST_F(EmmgTest, BuildStreamCloseRequest) { + emmg_->PublicBuildStreamCloseRequest(); + EXPECT_EQ(0, memcmp(kTestEmmgStreamCloseRequest, emmg_->GetRequest(), + sizeof(kTestEmmgStreamCloseRequest))); +} + +TEST_F(EmmgTest, BuildChannelClose) { + emmg_->PublicBuildChannelClose(); + EXPECT_EQ(0, memcmp(kTestEmmgChannelClose, emmg_->GetRequest(), + sizeof(kTestEmmgChannelClose))); +} + +} // namespace +} // namespace cas +} // namespace widevine diff --git a/media_cas_packager_sdk/internal/simulcrypt_constants.h b/media_cas_packager_sdk/internal/simulcrypt_constants.h new file mode 100644 index 0000000..2d1a7e5 --- /dev/null +++ b/media_cas_packager_sdk/internal/simulcrypt_constants.h @@ -0,0 +1,19 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2019 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef MEDIA_CAS_PACKAGER_SDK_INTERNAL_SIMULCRYPT_CONSTANTS_H_ +#define MEDIA_CAS_PACKAGER_SDK_INTERNAL_SIMULCRYPT_CONSTANTS_H_ + +// Size (in # of bytes) of various fields. +#define PROTOCOL_VERSION_SIZE (1) +#define MESSAGE_TYPE_SIZE (2) +#define MESSAGE_LENGTH_SIZE (2) +#define PARAMETER_TYPE_SIZE (2) +#define PARAMETER_LENGTH_SIZE (2) + +#endif // MEDIA_CAS_PACKAGER_SDK_INTERNAL_SIMULCRYPT_CONSTANTS_H_ diff --git a/media_cas_packager_sdk/internal/simulcrypt_util.cc b/media_cas_packager_sdk/internal/simulcrypt_util.cc new file mode 100644 index 0000000..8abb16b --- /dev/null +++ b/media_cas_packager_sdk/internal/simulcrypt_util.cc @@ -0,0 +1,93 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2019 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "media_cas_packager_sdk/internal/simulcrypt_util.h" + +#include +#include + +#include "glog/logging.h" +#include "media_cas_packager_sdk/internal/simulcrypt_constants.h" +#include "media_cas_packager_sdk/internal/util.h" + +namespace widevine { +namespace cas { +namespace simulcrypt_util { + +void BuildMessageHeader(uint8_t protocol_version, uint16_t message_type, + char* message, size_t* message_length) { + DCHECK(message); + DCHECK(message_length); + *message_length = 0; + message[*message_length] = protocol_version; + *message_length += PROTOCOL_VERSION_SIZE; + Host16ToBigEndian(message + *message_length, &message_type); + *message_length += MESSAGE_TYPE_SIZE; + // NOTE: 'message_length' needs to be updated later after we have added all + // the params so we know the exact length of the all the params. + // Use 0 for 'total_param_length' until we know the length of the message. + uint16_t total_param_length = 0; + Host16ToBigEndian(message + *message_length, &total_param_length); + *message_length += MESSAGE_LENGTH_SIZE; +} + +void AddUint32Param(uint16_t param_type, uint32_t param_value, char* message, + size_t* message_length) { + DCHECK(message); + DCHECK(message_length); + Host16ToBigEndian(message + *message_length, ¶m_type); + *message_length += 2; + uint16_t param_length = 4; + Host16ToBigEndian(message + *message_length, ¶m_length); + *message_length += 2; + Host32ToBigEndian(message + *message_length, ¶m_value); + *message_length += param_length; +} + +void AddUint16Param(uint16_t param_type, uint16_t param_value, char* message, + size_t* message_length) { + DCHECK(message); + DCHECK(message_length); + Host16ToBigEndian(message + *message_length, ¶m_type); + *message_length += 2; + uint16_t param_length = 2; + Host16ToBigEndian(message + *message_length, ¶m_length); + *message_length += 2; + Host16ToBigEndian(message + *message_length, ¶m_value); + *message_length += param_length; +} + +void AddUint8Param(uint16_t param_type, uint8_t param_value, char* message, + size_t* message_length) { + DCHECK(message); + DCHECK(message_length); + Host16ToBigEndian(message + *message_length, ¶m_type); + *message_length += 2; + uint16_t param_length = 1; + Host16ToBigEndian(message + *message_length, ¶m_length); + *message_length += 2; + memcpy(message + *message_length, ¶m_value, param_length); + *message_length += param_length; +} + +void AddParam(uint16_t param_type, uint8_t* param_value, uint16_t param_length, + char* message, size_t* message_length) { + DCHECK(param_value); + DCHECK(message); + DCHECK(message_length); + Host16ToBigEndian(message + *message_length, ¶m_type); + *message_length += 2; + Host16ToBigEndian(message + *message_length, ¶m_length); + *message_length += 2; + memcpy(message + *message_length, param_value, param_length); + *message_length += param_length; +} + +} // namespace simulcrypt_util +} // namespace cas +} // namespace widevine diff --git a/media_cas_packager_sdk/internal/simulcrypt_util.h b/media_cas_packager_sdk/internal/simulcrypt_util.h new file mode 100644 index 0000000..acd1f1d --- /dev/null +++ b/media_cas_packager_sdk/internal/simulcrypt_util.h @@ -0,0 +1,59 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2019 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// +// +// Utility functions for building Simulcrypt messages. + +#ifndef MEDIA_CAS_PACKAGER_SDK_INTERNAL_SIMULCRYPT_UTIL_H_ +#define MEDIA_CAS_PACKAGER_SDK_INTERNAL_SIMULCRYPT_UTIL_H_ + +#include +#include +#include +#include +#include + +#include +#include "common/status.h" + +namespace widevine { +namespace cas { +namespace simulcrypt_util { + +// Add 'protocol_version', 'message_type', 'message_length' to the message. +// TODO(user): Per jfore@, consider pass in a pointer to a structure +// #pragma pack(push, 1) // exact fit - no padding +// struct MessageHeader{ +// uint8_t protocol_version; +// uint16_t message_type; +// uint16_t message_length; +// }; +// #pragma pack(pop) // restore previous pack +void BuildMessageHeader(uint8_t protocol_version, uint16_t message_type, + char* message, size_t* message_length); + +// Add a uint32_t parameter to the message. +void AddUint32Param(uint16_t param_type, uint32_t param_value, char* message, + size_t* message_length); + +// Add a uint16_t parameter to the message. +void AddUint16Param(uint16_t param_type, uint16_t param_value, char* message, + size_t* message_length); + +// Add a uint8_t parameter to the message. +void AddUint8Param(uint16_t param_type, uint8_t param_value, char* message, + size_t* message_length); + +// Add a param that is |param_length| bytes long. +void AddParam(uint16_t param_type, uint8_t* param_value, uint16_t param_length, + char* message, size_t* message_length); + +} // namespace simulcrypt_util +} // namespace cas +} // namespace widevine + +#endif // MEDIA_CAS_PACKAGER_SDK_INTERNAL_SIMULCRYPT_UTIL_H_ diff --git a/media_cas_packager_sdk/internal/util.cc b/media_cas_packager_sdk/internal/util.cc index 301477c..c891f55 100644 --- a/media_cas_packager_sdk/internal/util.cc +++ b/media_cas_packager_sdk/internal/util.cc @@ -57,6 +57,13 @@ void Host16ToBigEndian(void* destination, const int16_t* source) { memcpy(destination, &big_endian_number, 2); } +void Host32ToBigEndian(void* destination, const uint32_t* source) { + DCHECK(destination); + DCHECK(source); + uint32_t big_endian_number = htonl(*source); + memcpy(destination, &big_endian_number, 4); +} + Status InsertEcmAsTsPacket(const std::string& ecm, ProgramId pid, uint8_t table_id, ContinuityCounter* cc, uint8_t* buffer, ssize_t* bytes_modified) { diff --git a/media_cas_packager_sdk/internal/util.h b/media_cas_packager_sdk/internal/util.h index 2b6ba71..fb9472d 100644 --- a/media_cas_packager_sdk/internal/util.h +++ b/media_cas_packager_sdk/internal/util.h @@ -33,6 +33,7 @@ void BigEndianToHost32(uint32_t* destination, const void* source); // |destination|. void Host16ToBigEndian(void* destination, const uint16_t* source); void Host16ToBigEndian(void* destination, const int16_t* source); +void Host32ToBigEndian(void* destination, const uint32_t* source); // Packages an ECM as a TS packet and inserts it into a buffer. // Args: diff --git a/media_cas_packager_sdk/public/BUILD b/media_cas_packager_sdk/public/BUILD index 1c60d69..4e9635b 100644 --- a/media_cas_packager_sdk/public/BUILD +++ b/media_cas_packager_sdk/public/BUILD @@ -22,6 +22,8 @@ filegroup( name = "binary_release_files", srcs = glob(["*.h"]) + [ ":wv_ecmg", + "wv_cas_key_fetcher.cc", + "wv_cas_types.cc", ], ) @@ -127,7 +129,9 @@ cc_library( "@abseil_repo//absl/base:core_headers", "@abseil_repo//absl/strings", "@curl_repo//:curl", + "//util:error_space", "//common:signature_util", + "//common:status", "//media_cas_packager_sdk/internal:key_fetcher", "//protos/public:media_cas_encryption_proto", ], @@ -181,3 +185,12 @@ cc_binary( "//media_cas_packager_sdk/internal:ecmg_client_handler", ], ) + +cc_binary( + name = "wv_emmg", + srcs = ["wv_emmg.cc"], + deps = [ + "//base", + "//media_cas_packager_sdk/internal:emmg", + ], +) 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 6e4be17..d8efb49 100644 --- a/media_cas_packager_sdk/public/wv_cas_key_fetcher.cc +++ b/media_cas_packager_sdk/public/wv_cas_key_fetcher.cc @@ -103,8 +103,6 @@ Status WvCasKeyFetcher::RequestEntitlementKey(const std::string& request_string, } // Processes signed response. - // TODO(b/114741232): Seems we are getting CasEncryptionResponse back instead - // of SignedCasEncryptionResponse. LOG(INFO) << "Json CasEncryptionResponse: " << http_response.response(); CasEncryptionResponse response; if (!JsonStringToMessage(http_response.response(), &response).ok()) { diff --git a/media_cas_packager_sdk/public/wv_cas_types.cc b/media_cas_packager_sdk/public/wv_cas_types.cc index 2464112..02d2da8 100644 --- a/media_cas_packager_sdk/public/wv_cas_types.cc +++ b/media_cas_packager_sdk/public/wv_cas_types.cc @@ -79,8 +79,8 @@ bool StringToCryptoMode(const std::string& str, CryptoMode* mode) { return false; } -Status CreateWvCasEncryptionRequestJson(const WvCasEncryptionRequest& request, - std::string* request_json) { +WvCasStatus CreateWvCasEncryptionRequestJson( + const WvCasEncryptionRequest& request, std::string* request_json) { CHECK(request_json); CasEncryptionRequest request_proto; @@ -100,23 +100,23 @@ Status CreateWvCasEncryptionRequestJson(const WvCasEncryptionRequest& request, // NOTE: MessageToJsonString will automatically converts 'bytes' type fields // to base64. For example content ID '21140844' becomes 'MjExNDA4NDQ='. if (!MessageToJsonString(request_proto, request_json, print_options).ok()) { - return Status(error::INTERNAL, - "Failed to convert request message to json."); + LOG(ERROR) << "Failed to convert request message to json."; + return INTERNAL; } - return OkStatus(); + return OK; } -Status ParseWvCasEncryptionResponseJson(const std::string& response_json, - WvCasEncryptionResponse* response) { +WvCasStatus ParseWvCasEncryptionResponseJson( + const std::string& response_json, WvCasEncryptionResponse* response) { CHECK(response); CasEncryptionResponse response_proto; // NOTE: JsonStringToMessage will automatically perform base64 decode for // 'bytes' type fields. if (!JsonStringToMessage(response_json, &response_proto).ok()) { - return Status(error::INTERNAL, - "Failed to convert response json to message."); + LOG(ERROR) << "Failed to convert response json to message."; + return INTERNAL; } response->status = @@ -133,7 +133,7 @@ Status ParseWvCasEncryptionResponseJson(const std::string& response_json, response->entitlement_keys.push_back(key_info); } - return OkStatus(); + return OK; } } // namespace cas diff --git a/media_cas_packager_sdk/public/wv_cas_types.h b/media_cas_packager_sdk/public/wv_cas_types.h index 2d42894..c269a62 100644 --- a/media_cas_packager_sdk/public/wv_cas_types.h +++ b/media_cas_packager_sdk/public/wv_cas_types.h @@ -114,15 +114,15 @@ struct WvCasEncryptionResponse { // request JSON message. // And that signed JSON message can be sent to Widevine license server for // aquiring entitlement keys. -Status CreateWvCasEncryptionRequestJson(const WvCasEncryptionRequest& request, - std::string* request_json); +WvCasStatus CreateWvCasEncryptionRequestJson( + const WvCasEncryptionRequest& request, std::string* request_json); // Parses a WvCasEncryptionResponse in JSON format, returns a // WvCasEncryptionResponse. // |response_json| is supposed to be the 'response' field in the signed // response from Widevine license server. -Status ParseWvCasEncryptionResponseJson(const std::string& response_json, - WvCasEncryptionResponse* response); +WvCasStatus ParseWvCasEncryptionResponseJson(const std::string& response_json, + WvCasEncryptionResponse* response); } // namespace cas } // namespace widevine 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 a12e4da..04b3cec 100644 --- a/media_cas_packager_sdk/public/wv_cas_types_test.cc +++ b/media_cas_packager_sdk/public/wv_cas_types_test.cc @@ -56,7 +56,8 @@ TEST(WvCasTypesTest, CreateWvCasEncryptionRequestJson) { request.key_rotation = true; std::string actual_request_json; - EXPECT_OK(CreateWvCasEncryptionRequestJson(request, &actual_request_json)); + EXPECT_EQ(OK, + CreateWvCasEncryptionRequestJson(request, &actual_request_json)); // Content_id has been base64 encoded. std::string expected_request_json = @@ -76,7 +77,8 @@ TEST(WvCasTypesTest, ParseWvCasEncryptionResponseJson) { "\"key_slot\":\"ODD\"}]}"; WvCasEncryptionResponse actual_response; - EXPECT_OK(ParseWvCasEncryptionResponseJson(response_json, &actual_response)); + EXPECT_EQ(OK, + ParseWvCasEncryptionResponseJson(response_json, &actual_response)); EXPECT_EQ(WvCasEncryptionResponse::Status::OK, actual_response.status); // 21140844 is base64 decode of "MjExNDA4NDQ=". diff --git a/media_cas_packager_sdk/public/wv_ecmg.cc b/media_cas_packager_sdk/public/wv_ecmg.cc index 9a90d4a..8393aec 100644 --- a/media_cas_packager_sdk/public/wv_ecmg.cc +++ b/media_cas_packager_sdk/public/wv_ecmg.cc @@ -6,15 +6,17 @@ // widevine-licensing@google.com. //////////////////////////////////////////////////////////////////////////////// -// Example server that listens on a port for Simulcrypt API messages. +// Widevine MediaCAS ECMG server. #include #include #include #include +#include #include #include #include +#include #include "gflags/gflags.h" #include "glog/logging.h" diff --git a/media_cas_packager_sdk/public/wv_emmg.cc b/media_cas_packager_sdk/public/wv_emmg.cc new file mode 100644 index 0000000..6d0dad6 --- /dev/null +++ b/media_cas_packager_sdk/public/wv_emmg.cc @@ -0,0 +1,92 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2019 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +// Widevine MediaCAS EMMG server. + +#include +#include +#include +#include +#include + +#include "gflags/gflags.h" +#include "glog/logging.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"); +// 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"); + +#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; +} + +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"; + + // 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); + if (return_val != 0 || server == nullptr) { + LOG(FATAL) << "gethostbyname_r failed for " << FLAGS_mux_address; + } + struct sockaddr_in server_address; + bzero(reinterpret_cast(&server_address), sizeof(server_address)); + server_address.sin_family = AF_INET; + bcopy(server->h_addr, + reinterpret_cast(&server_address.sin_addr.s_addr), + server->h_length); + server_address.sin_port = htons(FLAGS_mux_port); + + // Connect to server. + int server_socket_fd = socket(AF_INET, SOCK_STREAM, 0); + if (server_socket_fd < 0) { + LOG(FATAL) << "Failed to create socket."; + } + if (connect(server_socket_fd, + reinterpret_cast(&server_address), + sizeof(server_address)) < 0) { + LOG(FATAL) << "Failed to connect to server."; + } + + // Send EMMG messages. + widevine::cas::EmmgConfig emmg_config; + BuildEmmgConfig(&emmg_config); + widevine::cas::Emmg emmg(&emmg_config, server_socket_fd); + emmg.Start(); + + close(server_socket_fd); + + return 0; +}