diff --git a/common/hash_algorithm.h b/common/hash_algorithm.h index 356b190..9c4b6d3 100644 --- a/common/hash_algorithm.h +++ b/common/hash_algorithm.h @@ -11,7 +11,7 @@ namespace widevine { -enum class HashAlgorithm { kUnspecified, kSha1, kSha256 }; +enum class HashAlgorithm { kUnspecified, kSha1, kSha256, kSha384 }; } // namespace widevine diff --git a/common/security_profile_list.h b/common/security_profile_list.h index 37f4dfd..e1c91c1 100644 --- a/common/security_profile_list.h +++ b/common/security_profile_list.h @@ -14,6 +14,8 @@ #ifndef COMMON_SECURITY_PROFILE_LIST_H_ #define COMMON_SECURITY_PROFILE_LIST_H_ +#include + #include "absl/synchronization/mutex.h" #include "common/hash_algorithm.h" #include "common/status.h" diff --git a/example/wv_cas_ecm_example b/example/wv_cas_ecm_example index 7925aa0..3df75a7 100644 Binary files a/example/wv_cas_ecm_example and b/example/wv_cas_ecm_example differ diff --git a/example/wv_cas_ecm_example.cc b/example/wv_cas_ecm_example.cc index 88b38d0..fe7bb40 100644 --- a/example/wv_cas_ecm_example.cc +++ b/example/wv_cas_ecm_example.cc @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -166,11 +167,12 @@ int main(int argc, char** argv) { std::string ecm; widevine::Status status; if (kKeyRotation) { - status = wv_cas_ecm.GenerateEcm(content_keys[0], content_keys[1], - kDefaultTrackTypeSd, &ecm); + status = + wv_cas_ecm.GenerateEcm(content_keys[0], content_keys[1], + kDefaultTrackTypeSd, /*group_ids=*/{}, &ecm); } else { - status = wv_cas_ecm.GenerateSingleKeyEcm(content_keys[0], - kDefaultTrackTypeSd, &ecm); + status = wv_cas_ecm.GenerateSingleKeyEcm( + content_keys[0], kDefaultTrackTypeSd, /*group_ids=*/{}, &ecm); } if (!status.ok()) { diff --git a/example/wv_cas_emm_example b/example/wv_cas_emm_example index 2e0b420..7a04de4 100644 Binary files a/example/wv_cas_emm_example and b/example/wv_cas_emm_example differ diff --git a/example/wv_cas_emm_example.cc b/example/wv_cas_emm_example.cc index a88457f..defbc89 100644 --- a/example/wv_cas_emm_example.cc +++ b/example/wv_cas_emm_example.cc @@ -8,6 +8,7 @@ // Example of how to use the wv_cas_emm library. +#include #include #include #include diff --git a/example/wv_cas_key_fetcher_example b/example/wv_cas_key_fetcher_example index 7448112..1322776 100644 Binary files a/example/wv_cas_key_fetcher_example and b/example/wv_cas_key_fetcher_example differ diff --git a/example/wv_cas_types_example b/example/wv_cas_types_example index 3717716..dc132f4 100644 Binary files a/example/wv_cas_types_example and b/example/wv_cas_types_example differ diff --git a/example/wv_ecmg_example b/example/wv_ecmg_example index 2137e54..6a324a6 100644 Binary files a/example/wv_ecmg_example and b/example/wv_ecmg_example differ diff --git a/example/wv_ecmg_example.cc b/example/wv_ecmg_example.cc index 409f15d..17f7f11 100644 --- a/example/wv_ecmg_example.cc +++ b/example/wv_ecmg_example.cc @@ -9,12 +9,14 @@ // Example of Simulcrypt ECMG with custom entitlement key fetcher. #include +#include #include #include #include #include #include +#include #include #include #include @@ -76,7 +78,6 @@ void ServeClient(int socket_fd, WvCasEcmgClientHandler* ecmg) { char response[kBufferSizeBytes]; while (true) { bzero(request, kBufferSizeBytes); - bzero(response, kBufferSizeBytes); size_t response_length = 0; size_t request_length = recv(socket_fd, request, kBufferSizeBytes, 0); if (request_length == 0) { @@ -88,12 +89,23 @@ void ServeClient(int socket_fd, WvCasEcmgClientHandler* ecmg) { return; } PrintMessage("Request", request, request_length); - ecmg->HandleRequest(kBufferSizeBytes, request, kBufferSizeBytes, response, - &response_length); - PrintMessage("Response", response, response_length); - if (send(socket_fd, response, response_length, 0) < 0) { - std::cerr << "Failed to send response to client." << std::endl; - return; + size_t processed_total_length = 0; + while (processed_total_length < request_length) { + bzero(response, kBufferSizeBytes); + size_t processed_length = 0; + ecmg->HandleRequest(kBufferSizeBytes - processed_total_length, + request + processed_total_length, kBufferSizeBytes, + response, response_length, processed_length); + if (processed_length == 0) { + std::cerr << "Failed to process the request" << std::endl; + return; + } + processed_total_length += processed_length; + PrintMessage("Response", response, response_length); + if (send(socket_fd, response, response_length, 0) < 0) { + std::cerr << "Failed to send response to client" << std::endl; + return; + } } } } @@ -118,10 +130,10 @@ std::vector MyEntitlementKeyFetcherFun(uint16_t channel_id, constexpr char entitlement_key_id_odd[] = "fake_key_id2...."; constexpr char entitlement_key_value_odd[] = "fakefakefakefakefakefakefake2..."; - return {{track_types_hd, /*is_even_key=*/true, entitlement_key_id_even, - entitlement_key_value_even}, - {track_types_hd, /*is_even_key=*/false, entitlement_key_id_odd, - entitlement_key_value_odd}}; + return {{track_types_hd, /*group_id=*/"", /*is_even_key=*/true, + entitlement_key_id_even, entitlement_key_value_even}, + {track_types_hd, /*group_id=*/"", /*is_even_key=*/false, + entitlement_key_id_odd, entitlement_key_value_odd}}; } int create_listen_socket_fd() { @@ -181,6 +193,9 @@ int main(int argc, char** argv) { break; } + // Ignoring SIGCHLD signal to prevent Zombie processes. + signal(SIGCHLD, SIG_IGN); + // While loop to serve different client connections. while (true) { struct sockaddr_in client_address; @@ -190,7 +205,7 @@ int main(int argc, char** argv) { &client_address_size); std::cout << "\nTCP connection " << client_socket_fd << " start" << std::endl; - if (client_socket_fd < 0) { + if (client_socket_fd < 0 && errno != EINTR) { std::cerr << "Failed to accept connection request from client" << std::endl; } else { diff --git a/libmedia_cas_packager_sdk.so b/libmedia_cas_packager_sdk.so index d068833..823f616 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/wv_cas_ca_descriptor.h b/media_cas_packager_sdk/public/wv_cas_ca_descriptor.h index 428457c..540a763 100644 --- a/media_cas_packager_sdk/public/wv_cas_ca_descriptor.h +++ b/media_cas_packager_sdk/public/wv_cas_ca_descriptor.h @@ -11,6 +11,7 @@ #include +#include #include #include @@ -60,7 +61,8 @@ class WvCasCaDescriptor { // ECM stream). The descriptor will be 6 bytes plus any bytes added as // (user-defined) private data. virtual Status GenerateCaDescriptor( - uint16_t ca_pid, const std::string& provider, const std::string& content_id, + uint16_t ca_pid, const std::string& provider, + const std::string& content_id, const std::vector& entitlement_key_ids, std::string* serialized_ca_desc) const; diff --git a/media_cas_packager_sdk/public/wv_cas_curl_key_fetcher.cc b/media_cas_packager_sdk/public/wv_cas_curl_key_fetcher.cc index 432a9cb..5b9ed83 100644 --- a/media_cas_packager_sdk/public/wv_cas_curl_key_fetcher.cc +++ b/media_cas_packager_sdk/public/wv_cas_curl_key_fetcher.cc @@ -8,6 +8,8 @@ #include "media_cas_packager_sdk/public/wv_cas_curl_key_fetcher.h" +#include + #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" #include "curl/curl.h" diff --git a/media_cas_packager_sdk/public/wv_cas_ecm.h b/media_cas_packager_sdk/public/wv_cas_ecm.h index e191f08..35a5342 100644 --- a/media_cas_packager_sdk/public/wv_cas_ecm.h +++ b/media_cas_packager_sdk/public/wv_cas_ecm.h @@ -9,6 +9,7 @@ #ifndef MEDIA_CAS_PACKAGER_SDK_PUBLIC_WV_CAS_ECM_H_ #define MEDIA_CAS_PACKAGER_SDK_PUBLIC_WV_CAS_ECM_H_ +#include #include #include #include @@ -98,6 +99,8 @@ class WvCasEcm { // |even_key| information for even key to be encoded into ECM. // |odd_key| information for odd key to be encoded into ECM. // |track_type| the track that the keys are being used to encrypt. + // |group_ids| If specified, the content keys will be additionally encrypted + // by group entitlement keys with specified |group_ids|. // |serialized_ecm| caller-supplied std::string pointer to receive the ECM. // The |even_key| and |odd_key| contents (specifically IV sizes) must be // consistent with the initialized settings. @@ -106,6 +109,7 @@ class WvCasEcm { virtual Status GenerateEcm(const WvCasContentKeyInfo& even_key, const WvCasContentKeyInfo& odd_key, const std::string& track_type, + const std::vector& group_ids, std::string* serialized_ecm) const; // Constructs a Widevine ECM using the provided key info. This call is @@ -113,11 +117,14 @@ class WvCasEcm { // Args: // |key| information for key to be encoded into ECM. // |track_type| the track that the key is being used to encrypt. + // |group_ids| If specified, the content keys will be additionally encrypted + // by group entitlement keys with specified |group_ids|. // |serialized_ecm| caller-supplied std::string pointer to receive the ECM. // The |key| contents (specifically IV sizes) must be consistent // with the initialized settings. virtual Status GenerateSingleKeyEcm(const WvCasContentKeyInfo& key, const std::string& track_type, + const std::vector& group_ids, std::string* serialized_ecm) const; // Generate a TS packet with the given |ecm| as payload. diff --git a/media_cas_packager_sdk/public/wv_cas_ecmg_client_handler.h b/media_cas_packager_sdk/public/wv_cas_ecmg_client_handler.h index 083f356..df27548 100644 --- a/media_cas_packager_sdk/public/wv_cas_ecmg_client_handler.h +++ b/media_cas_packager_sdk/public/wv_cas_ecmg_client_handler.h @@ -66,10 +66,13 @@ class WvCasEcmgClientHandler { // enough to hold the max possible response message (at least 2048 bytes). // - |response_buffer| is the buffer that holds the response message. // - |response_length| is the actual length of the response message. + // - |processed_request_length| is the actual length of |request_buffer| that + // has been processed. Status HandleRequest(size_t request_buffer_size, const char* const request_buffer, size_t response_buffer_size, char* response_buffer, - size_t* response_length); + size_t& response_length, + size_t& processed_request_length); protected: // For unit test only. diff --git a/media_cas_packager_sdk/public/wv_cas_emm.h b/media_cas_packager_sdk/public/wv_cas_emm.h index 60f9a80..79a9a61 100644 --- a/media_cas_packager_sdk/public/wv_cas_emm.h +++ b/media_cas_packager_sdk/public/wv_cas_emm.h @@ -9,6 +9,7 @@ #ifndef MEDIA_CAS_PACKAGER_SDK_PUBLIC_WV_CAS_EMM_H_ #define MEDIA_CAS_PACKAGER_SDK_PUBLIC_WV_CAS_EMM_H_ +#include #include #include #include diff --git a/media_cas_packager_sdk/public/wv_cas_types.h b/media_cas_packager_sdk/public/wv_cas_types.h index c907623..41d439a 100644 --- a/media_cas_packager_sdk/public/wv_cas_types.h +++ b/media_cas_packager_sdk/public/wv_cas_types.h @@ -9,8 +9,10 @@ #ifndef MEDIA_CAS_PACKAGER_SDK_PUBLIC_WV_CAS_TYPES_H_ #define MEDIA_CAS_PACKAGER_SDK_PUBLIC_WV_CAS_TYPES_H_ +#include #include #include +#include #include #include "common/status.h" @@ -52,18 +54,34 @@ enum EcmIvSize { kIvSize8 = 0, kIvSize16 = 1 }; // Information needed for the injected entitlement keys. Used for Ecm // initialization. -// Fields: -// |track_type| the track that the key is being used to encrypt. -// |is_even_key| if true, this entitlement is an even key. For the single key -// case (no key rotation), is_even_key is always true. -// |key_id| key ID for this entitlement key, must be 16 bytes. -// |key_value| entitlement key value, must be 32 bytes. Used to decrypt -// content keys. struct EntitlementKeyInfo { + // The track that the key is being used to encrypt. It used as the identifier + // to select which enetitlement keys to use when generating ECM. Must be + // non-empty. std::string track_type; + // Used only if this is a group entitlement key (leave empty in case of non + // group key). If specified, it used as the secondary (to |track_type|) + // identifier to select which group enetitlement keys to use when generating + // ECM. + std::string group_id; + // If this entitlement key is even key or not. bool is_even_key; - std::string key_id; // must be 16 bytes. - std::string key_value; // must be 32 bytes. + // Key ID of the entitlement key, must be 16 bytes. + std::string key_id; + // Key value of the entitlement key, must be 32 bytes. Used to encrypt content + // keys in the generated ECMs. + std::string key_value; + + bool operator==(const EntitlementKeyInfo& other) const { + return std::forward_as_tuple(track_type, group_id, is_even_key, key_id, + key_value) == + std::forward_as_tuple(other.track_type, other.group_id, + other.is_even_key, other.key_id, + other.key_value); + } + bool operator!=(const EntitlementKeyInfo& other) const { + return !(*this == other); + } }; enum class EcmVersion : int { kV2 = 0, kV3 = 1 }; @@ -172,9 +190,13 @@ struct EcmgCustomParameters { // |stream_id| is the stream id received at ECMG from SCS when setting up // the stream. // |access_criteria| the received access criteria from SCS. -// Returns EcmgCustomParameters. Negative or empty fields will be ignored. -typedef std::function +// |ecmg_params| is the output parameters by processing |access_criteria|. +// Negative or empty fields will be ignored. +// Returns a Status. If status is not OK, error INVALID_MESSAGE with error info +// from status will be sent to SCS. +typedef std::function CustomAccessCriteriaProcessFunc; struct EcmFingerprintingParams { diff --git a/media_cas_packager_sdk/public/wv_ecmg b/media_cas_packager_sdk/public/wv_ecmg index 57f30ad..2a88928 100644 Binary files a/media_cas_packager_sdk/public/wv_ecmg and b/media_cas_packager_sdk/public/wv_ecmg differ diff --git a/media_cas_packager_sdk/public/wv_emmg b/media_cas_packager_sdk/public/wv_emmg index 352873f..ce17209 100644 Binary files a/media_cas_packager_sdk/public/wv_emmg and b/media_cas_packager_sdk/public/wv_emmg differ diff --git a/protos/public/hash_algorithm.proto b/protos/public/hash_algorithm.proto index bcb0751..91677b9 100644 --- a/protos/public/hash_algorithm.proto +++ b/protos/public/hash_algorithm.proto @@ -17,4 +17,5 @@ enum HashAlgorithmProto { HASH_ALGORITHM_UNSPECIFIED = 0; HASH_ALGORITHM_SHA_1 = 1; HASH_ALGORITHM_SHA_256 = 2; + HASH_ALGORITHM_SHA_384 = 3; } diff --git a/protos/public/media_cas.proto b/protos/public/media_cas.proto index 405ecac..33dfbb3 100644 --- a/protos/public/media_cas.proto +++ b/protos/public/media_cas.proto @@ -22,6 +22,9 @@ message CaDescriptorPrivateData { // Entitlement key IDs for current content per track. Each track will allow up // to 2 entitlement key ids (odd and even entitlement keys). repeated bytes entitlement_key_ids = 3; + + // The groups ids this channel belongs to. + repeated bytes group_ids = 4; } // Widevine fingerprinting. @@ -49,6 +52,15 @@ message ServiceBlocking { message EmmPayload { repeated Fingerprinting fingerprinting = 1; repeated ServiceBlocking service_blocking = 2; + // Epoch time in seconds. The time when the EMM is generated. + optional int64 timestamp_secs = 3; +} + +message SignedEmmPayload { + // Serialized EmmPayload. + optional bytes serialized_payload = 1; + // ECC (Elliptic Curve Cryptography) signature of |serialized_payload|. + optional bytes signature = 2; } message EcmMetaData { @@ -89,6 +101,18 @@ message EcmKeyData { optional bytes content_iv = 4; } +message EcmGroupKeyData { + // Group id of this key data. + optional bytes group_id = 1; + // Required. The key data for the even slot. Fields wrapped_key_iv and + // content_iv may be omitted if it is the same as EcmPayload.even_key_data. + optional EcmKeyData even_key_data = 2; + // Optional. The key data for the odd slot if key rotation is enabled. Fields + // wrapped_key_iv and content_iv may be omitted if it is the same as + // EcmPayload.odd_key_data. + optional EcmKeyData odd_key_data = 3; +} + message EcmPayload { // Required. Meta info carried by the ECM. optional EcmMetaData meta_data = 1; @@ -100,6 +124,9 @@ message EcmPayload { optional Fingerprinting fingerprinting = 4; // Optional. Widevine service blocking information. optional ServiceBlocking service_blocking = 5; + // If a channel belongs to a group, the content keys can additionally be + // encrypted by the group entitlement keys. + repeated EcmGroupKeyData group_key_data = 6; } // The payload field for an ECM with signature. diff --git a/protos/public/media_cas_encryption.proto b/protos/public/media_cas_encryption.proto index c40712a..12c3b27 100644 --- a/protos/public/media_cas_encryption.proto +++ b/protos/public/media_cas_encryption.proto @@ -27,9 +27,14 @@ message CasEncryptionRequest { // returned. optional bool key_rotation = 4; // Optional value which can be used to indicate a group. - // If present the CasEncryptionResponse will return key based on the group - // id. + // If present, the CasEncryptionResponse will return keys based on this group + // id, instead of |content_id|. optional bytes group_id = 5; + // Entitlement period index for media using entitlement key rotation. It + // always corresponds to the entitlement key period. If present, the + // entitlement keys returned will corresponds to the specified entitlement + // period index. + optional uint32 entitlement_period_index = 6; } message CasEncryptionResponse {