diff --git a/example/simulcrypt_client b/example/simulcrypt_client new file mode 100644 index 0000000..a6e8299 Binary files /dev/null and b/example/simulcrypt_client differ diff --git a/example/simulcrypt_client.cc b/example/simulcrypt_client.cc new file mode 100644 index 0000000..f2a6979 --- /dev/null +++ b/example/simulcrypt_client.cc @@ -0,0 +1,80 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2018 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 client that talks to server_main. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gflags/gflags.h" +#include "glog/logging.h" + +DEFINE_string(server, "", "Server host name"); +DEFINE_int32(port, 0, "Server port number"); + +constexpr uint32_t kBufferSize = 256; + +int main(int argc, char **argv) { + gflags::ParseCommandLineFlags(&argc, &argv, true); + CHECK(!FLAGS_server.empty()) << "need --server"; + CHECK(FLAGS_port != 0) << "need --port"; + + struct hostent server; + { + int buflen = 1024; + char buf[1024]; + struct hostent *result; + int h_errnop; + gethostbyname_r(/* __name= */ FLAGS_server.c_str(), + /* __result_buf= */ &server, /* __buf= */ buf, + /* __buflen= */ buflen, + /* __result= */ &result, /* __h_errnop= */ &h_errnop); + } + + struct sockaddr_in server_addr; + bzero(reinterpret_cast(&server_addr), sizeof(server_addr)); + server_addr.sin_family = AF_INET; + // TODO(user): Consider using inet_pton() to populate server_addr.sin_addr. + bcopy(server.h_addr, reinterpret_cast(&server_addr.sin_addr.s_addr), + server.h_length); + server_addr.sin_port = htons(FLAGS_port); + + int socket_fd = socket(AF_INET, SOCK_STREAM, /* protocol= */ 0); + CHECK(socket_fd >= 0) << "failed to opening socket"; + CHECK(connect(socket_fd, (struct sockaddr *)&server_addr, + sizeof(server_addr)) >= 0) + << "failed to connect to socket"; + + printf("Please enter the message: "); + char buffer[kBufferSize]; + bzero(buffer, kBufferSize); + fgets(buffer, kBufferSize - 1, stdin); + int total_bytes_written = 0; + while (total_bytes_written != strlen(buffer)) { + int num_bytes_written = write(socket_fd, buffer, strlen(buffer)); + if (num_bytes_written < 0) { + LOG(FATAL) << "ERROR writing to socket: " << strerror(errno); + } + total_bytes_written += num_bytes_written; + } + bzero(buffer, kBufferSize); + if (read(socket_fd, buffer, kBufferSize - 1) < 0) { + LOG(FATAL) << "ERROR reading from socket: " << strerror(errno); + } + printf("Read from buffer: %s\n", buffer); + + close(socket_fd); + return 0; +} diff --git a/example/test_simulcrypt_messages.h b/example/test_simulcrypt_messages.h new file mode 100644 index 0000000..7bff06d --- /dev/null +++ b/example/test_simulcrypt_messages.h @@ -0,0 +1,166 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2018 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 Simulcrypt messages used in unit tests and example client. + +#ifndef MEDIA_CAS_PACKAGER_SDK_EXAMPLE_TEST_SIMULCRYPT_MESSAGES_H_ +#define MEDIA_CAS_PACKAGER_SDK_EXAMPLE_TEST_SIMULCRYPT_MESSAGES_H_ + +namespace widevine { +namespace cas { + +const char kTestEcmgStreamSetupMessage[] = { // protocol_version + '\x01', + // message_type - Stream_set-up + '\x01', '\x01', + // message_length + // 18 bytes below, 3 parameters 6 bytes each + '\x00', '\x12', + // parameter_type - ECM_channel_id + '\x00', '\x0e', + // parameter_length + '\x00', '\x02', + // parameter_value + '\x00', '\x01', + // parameter_type - ECM_stream_id + '\x00', '\x0f', + // parameter_length + '\x00', '\x02', + // parameter_value + '\x00', '\x02', + // parameter_type - nominal_CP_duration + '\x00', '\x10', + // parameter_length + '\x00', '\x02', + // parameter_value + '\x00', '\x03'}; + +const char kTestEcmgCwProvisionMessageWithOneCw[] = { + // protocol_version + '\x01', + // message_type - CW_provision + '\x02', '\x01', + // message_length + // 64 bytes below, 3 * 6 + 8 + 10 + 1 * 22 + 6 + '\x00', '\x40', + // parameter_type - ECM_channel_id + '\x00', '\x0e', + // parameter_length + '\x00', '\x02', + // parameter_value + '\x00', '\x01', + // parameter_type - ECM_stream_id + '\x00', '\x0f', + // parameter_length + '\x00', '\x02', + // parameter_value + '\x00', '\x02', + // parameter_type - CP_number + '\x00', '\x12', + // parameter_length + '\x00', '\x02', + // parameter_value + '\x00', '\xa1', + // parameter_type - access_criteria + '\x00', '\x0d', + // parameter_length + '\x00', '\x04', + // parameter_value + '\x00', '\x00', '\x00', '\x00', + // parameter_type - CW_encryption + '\x00', '\x18', + // parameter_length + '\x00', '\x06', + // parameter_value + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + // parameter_type - CP_CW_combination + '\x00', '\x14', + // parameter_length - 2 + 16 + '\x00', '\x12', + // parameter_value - CP then CW + // CP + '\x00', '\xa1', + // CW (16 bytes) + '\x11', '\x11', '\x11', '\x11', '\x11', '\x11', '\x11', '\x11', '\x11', + '\x11', '\x11', '\x11', '\x11', '\x11', '\x11', '\x11', + // parameter_type - CP_duration + '\x00', '\x13', + // parameter_length + '\x00', '\x02', + // parameter_value + '\x00', '\x0a'}; + +const char kTestEcmgCwProvisionMessageWithTwoCw[] = { + // protocol_version + '\x01', + // message_type - CW_provision + '\x02', '\x01', + // message_length + // 86 bytes below, 3 * 6 + 8 + 10 + 2 * 22 + 6 + '\x00', '\x56', + // parameter_type - ECM_channel_id + '\x00', '\x0e', + // parameter_length + '\x00', '\x02', + // parameter_value + '\x00', '\x01', + // parameter_type - ECM_stream_id + '\x00', '\x0f', + // parameter_length + '\x00', '\x02', + // parameter_value + '\x00', '\x02', + // parameter_type - CP_number + '\x00', '\x12', + // parameter_length + '\x00', '\x02', + // parameter_value + '\x00', '\xa1', + // parameter_type - access_criteria + '\x00', '\x0d', + // parameter_length + '\x00', '\x04', + // parameter_value + '\x00', '\x00', '\x00', '\x00', + // parameter_type - CW_encryption + '\x00', '\x18', + // parameter_length + '\x00', '\x06', + // parameter_value + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + // parameter_type - CP_CW_combination + '\x00', '\x14', + // parameter_length - 2 + 16 + '\x00', '\x12', + // parameter_value - CP then CW + // CP + '\x00', '\xa1', + // CW (16 bytes) + '\x11', '\x11', '\x11', '\x11', '\x11', '\x11', '\x11', '\x11', '\x11', + '\x11', '\x11', '\x11', '\x11', '\x11', '\x11', '\x11', + // parameter_type - CP_CW_combination + '\x00', '\x14', + // parameter_length - 2 + 16 + '\x00', '\x12', + // parameter_value - CP then CW + // CP + '\x00', '\xa2', + // CW (16 bytes) + '\x22', '\x22', '\x22', '\x22', '\x22', '\x22', '\x22', '\x22', '\x22', + '\x22', '\x22', '\x22', '\x22', '\x22', '\x22', '\x22', + // parameter_type - CP_duration + '\x00', '\x13', + // parameter_length + '\x00', '\x02', + // parameter_value + '\x00', '\x0a'}; + +} // namespace cas +} // namespace widevine + +#endif // MEDIA_CAS_PACKAGER_SDK_EXAMPLE_TEST_SIMULCRYPT_MESSAGES_H_ diff --git a/libmedia_cas_packager_sdk.so b/libmedia_cas_packager_sdk.so index ccfd6ae..6b5e6d0 100755 Binary files a/libmedia_cas_packager_sdk.so and b/libmedia_cas_packager_sdk.so differ diff --git a/media_cas_packager_sdk/public/ecm_generator.h b/media_cas_packager_sdk/public/ecm_generator.h deleted file mode 100644 index a95733d..0000000 --- a/media_cas_packager_sdk/public/ecm_generator.h +++ /dev/null @@ -1,95 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Copyright 2018 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_PUBLIC_ECM_GENERATOR_H_ -#define MEDIA_CAS_PACKAGER_SDK_PUBLIC_ECM_GENERATOR_H_ - -#include -#include -#include -#include -#include - -#include -#include "util/status.h" -#include "media_cas_packager_sdk/internal/ecm.h" - -namespace widevine { -namespace cas { - -// KeyParameters carries key information for a single encryption key. -// Instances of this struct are owned by an EcmParameters struct. -struct KeyParameters { - std::string key_id; - std::string key_data; - std::string wrapped_key_data; - std::string wrapped_key_iv; - std::vector content_ivs; -}; - -// EcmParameters holds information that is needed by the EcmGenerator. It is -// partially set up with data from a KeyGenerator (obtained via GenerateKey()). -// IVs are added later. -// TODO(user): may need a starting crypto period index. -struct EcmParameters { - static constexpr int kDefaultIVSize = 8; - int iv_size = kDefaultIVSize; - bool current_key_even = true; - uint32_t current_key_index = 0; - std::string entitlement_key_id; - bool rotation_enabled = true; - uint32_t rotation_periods; - std::vector key_params; - uint16_t program_id; - uint64_t rotation_period_microseconds; - // For video, this is zero. For audio, this is the size of the audio frame, - // which is a constant in the audio track. - uint16_t offset = 0; -}; - -// ECM Generator for Widevine/MediaCAS entitled keys. -class CasEcmGenerator { - public: - CasEcmGenerator() = default; - virtual ~CasEcmGenerator() = default; - - virtual std::string GenerateEcm(const EcmParameters& params); - - // Query the state of this ECM Generator - bool initialized() { return initialized_; } - bool rotation_enabled() { return rotation_enabled_; } - - void set_ecm(std::unique_ptr ecm) { ecm_ = std::move(ecm); } - - private: - friend class CasEcmGeneratorTest; - - util::Status ProcessEcmParameters(const EcmParameters& ecm_params, - std::vector* keys); - - util::Status ProcessEcmParameters(const EcmParameters& ecm_params); - util::Status ValidateKeyId(const std::string& id); - util::Status ValidateKeyData(const std::string& key_data); - util::Status ValidateWrappedKeyIv(const std::string& iv); - util::Status ValidateIv(const std::string& iv, size_t required_size); - util::Status ValidateContentIv(const std::string& iv); - util::Status ValidateKeyParameters(const KeyParameters& key_params); - - bool initialized_ = false; - uint32_t generation_ = 0; - bool rotation_enabled_ = false; - uint32_t current_key_index_ = 0; - bool current_key_even_ = true; - uint32_t content_iv_size_ = 0; - std::unique_ptr ecm_; -}; - -} // namespace cas -} // namespace widevine - -#endif // MEDIA_CAS_PACKAGER_SDK_PUBLIC_ECM_GENERATOR_H_ diff --git a/media_cas_packager_sdk/public/simulcrypt_server b/media_cas_packager_sdk/public/simulcrypt_server new file mode 100644 index 0000000..298a45a Binary files /dev/null and b/media_cas_packager_sdk/public/simulcrypt_server differ diff --git a/media_cas_packager_sdk/public/simulcrypt_server.cc b/media_cas_packager_sdk/public/simulcrypt_server.cc new file mode 100644 index 0000000..96321e3 --- /dev/null +++ b/media_cas_packager_sdk/public/simulcrypt_server.cc @@ -0,0 +1,84 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2018 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 server that listens on a port for Simulcrypt API messages. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gflags/gflags.h" +#include "glog/logging.h" + +DEFINE_int32(port, 0, "Server port number"); + +constexpr uint32_t kBufferSize = 256; +constexpr uint32_t kLicenseBacklog = 5; +constexpr uint32_t kWriteChunkSize = 18; + +int main(int argc, char **argv) { + gflags::ParseCommandLineFlags(&argc, &argv, true); + CHECK(FLAGS_port != 0) << "need --port"; + + struct sockaddr_in server_address; + bzero(reinterpret_cast(&server_address), sizeof(server_address)); + server_address.sin_family = AF_INET; + server_address.sin_addr.s_addr = htonl(INADDR_ANY); + server_address.sin_port = htons(FLAGS_port); + + int listen_socket_fd = socket(AF_INET, SOCK_STREAM, /* protocol= */ 0); + CHECK(listen_socket_fd >= 0) << "failed to open socket"; + CHECK(bind(listen_socket_fd, (struct sockaddr *)&server_address, + sizeof(server_address)) >= 0) + << "error on binding"; + std::cout << "Server listening ..." << std::endl << std::flush; + int return_val = listen(listen_socket_fd, kLicenseBacklog); + switch (return_val) { + case EADDRINUSE: + LOG(FATAL) << "Another socket is already listening on the same port."; + break; + case EBADF: + LOG(FATAL) << "The argument sockfd is not a valid descriptor."; + break; + case ENOTSOCK: + LOG(FATAL) << "The argument sockfd is not a socket."; + break; + case EOPNOTSUPP: + LOG(FATAL) << "The socket is not of a type that supports the listen() " + "operation."; + default: + break; + } + + struct sockaddr_in client_address; + socklen_t clilet_address_size = sizeof(client_address); + int client_socket_fd = accept( + listen_socket_fd, reinterpret_cast(&client_address), + &clilet_address_size); + CHECK(client_socket_fd >= 0) << "error on accept"; + + char buffer[kBufferSize]; + bzero(buffer, kBufferSize); + if (read(client_socket_fd, buffer, kBufferSize - 1) < 0) { + LOG(FATAL) << "ERROR reading from socket"; + } + printf("Here is the message: %s", buffer); + if (write(client_socket_fd, "I got your message", kWriteChunkSize) < 0) { + LOG(FATAL) << "ERROR writing to socket"; + } + + close(client_socket_fd); + close(listen_socket_fd); + return 0; +}