diff --git a/common/default_device_security_profile_list.h b/common/default_device_security_profile_list.h deleted file mode 100644 index 33d96d9..0000000 --- a/common/default_device_security_profile_list.h +++ /dev/null @@ -1,39 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Copyright 2020 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. -//////////////////////////////////////////////////////////////////////////////// -// -// Description: -// Container of Widevine default security profiless. - -#ifndef COMMON_DEFAULT_DEVICE_SECURITY_PROFILE_LIST_H_ -#define COMMON_DEFAULT_DEVICE_SECURITY_PROFILE_LIST_H_ - -#include "common/security_profile_list.h" - -namespace widevine { - -class DefaultDeviceSecurityProfileList : public SecurityProfileList { - public: - DefaultDeviceSecurityProfileList(); - ~DefaultDeviceSecurityProfileList() override {} - - // Initialize the security profile list. The list is initially empty, this - // function will populate the list with default profiles. The size of the - // list is returned. - int Init() override; - - private: - // Initialize the list with Widevine default profiles. The size of the - // profile list after the additions is returned. - virtual int AddDefaultProfiles(); - virtual int GetDefaultProfileStrings( - std::vector* default_profile_strings) const; -}; - -} // namespace widevine - -#endif // COMMON_DEFAULT_DEVICE_SECURITY_PROFILE_LIST_H_ diff --git a/common/hash_algorithm.h b/common/hash_algorithm.h new file mode 100644 index 0000000..356b190 --- /dev/null +++ b/common/hash_algorithm.h @@ -0,0 +1,18 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2020 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 COMMON_HASH_ALGORITHM_H_ +#define COMMON_HASH_ALGORITHM_H_ + +namespace widevine { + +enum class HashAlgorithm { kUnspecified, kSha1, kSha256 }; + +} // namespace widevine + +#endif // COMMON_HASH_ALGORITHM_H_ diff --git a/common/security_profile_list.h b/common/security_profile_list.h index b6cdb96..37f4dfd 100644 --- a/common/security_profile_list.h +++ b/common/security_profile_list.h @@ -15,25 +15,32 @@ #define COMMON_SECURITY_PROFILE_LIST_H_ #include "absl/synchronization/mutex.h" +#include "common/hash_algorithm.h" +#include "common/status.h" #include "protos/public/client_identification.pb.h" #include "protos/public/device_security_profile_data.pb.h" +#include "protos/public/device_security_profile_list.pb.h" #include "protos/public/provisioned_device_info.pb.h" #include "protos/public/security_profile.pb.h" namespace widevine { using ClientCapabilities = ClientIdentification::ClientCapabilities; +const char kDefaultProfileOwnerName[] = "Widevine"; + // The SecurityProfileList will hold all security profiles. During license // acquisition, information from the client and information from the server are // combined to deternmine the device's security profile level. +// TODO(user): Clean up the virtual/protected functions once subclass +// default_device_security_profile_list gets removed. class SecurityProfileList { public: explicit SecurityProfileList(const std::string& profile_namespace); virtual ~SecurityProfileList() {} - // Initialize the security profile list. The size of the profile list is - // returned. + // Initialize the security profile list with Widevine default profiles. The + // size of the profile list is returned. virtual int Init(); // Add the specified profile to the existing list of profiles. Returns true @@ -45,7 +52,7 @@ class SecurityProfileList { // The number of profiles is returned. virtual int GetQualifiedProfilesFromSpecifiedProfiles( const std::vector& profiles_to_check, - const ClientIdentification& client_id, + const std::string& owner, const ClientIdentification& client_id, const ProvisionedDeviceInfo& device_info, std::vector* qualified_profiles) const; @@ -53,14 +60,28 @@ class SecurityProfileList { // requirements for the this device. The number of profiles is returned. virtual int GetQualifiedProfiles( const ClientIdentification& client_id, - const ProvisionedDeviceInfo& device_info, + const ProvisionedDeviceInfo& device_info, const std::string& owner, std::vector* qualified_profiles) const; - // Return true if a profile exist matching the specified |name|. - // |security_profile| is owned by the caller and is populated if a profile - // exist. - bool GetProfileByName(const std::string& name, - SecurityProfile* security_profile) const; + // Return true if a profile exist matching the specified parameters {|name|, + // |owner|}. |security_profiles| is owned by the caller and is populated if + // one or more profile exist. For default DSP, the output profiles should + // contain single record. For custom DSP, it may contain multiple records + // since active dsp and inactive dsp could share the same dsp_name under the + // same owner. + bool GetProfileByNameAndOwner( + const std::string& name, const std::string& owner, + std::vector* security_profiles) const; + + // Populates |security_profiles| owned by the content owner. + int GetProfilesByOwner(const std::string& owner, + std::vector* security_profiles) const; + + // Populates |owner_list| for security profiles. |is_default_dsp| boolean + // indicates the owner_list for default dsp or custom dsp. + int GetProfilesOwnerList(const bool is_default_dsp, + std::vector* owner_list) const; + // Return the device security capabilities. |drm_info| is populated with // data from |client_id| and |device_info|. |drm_info| must not be null and // is owned by the caller. @@ -74,10 +95,33 @@ class SecurityProfileList { // Return a list of profile names. virtual void GetProfileNames(std::vector* profile_names) const; + // Deserialized SignedDeviceSecurityProfiles for custom DSPs. + static Status DeserializeSignedDeviceSecurityProfiles( + const std::string& serialized_signed_device_security_profiles, + std::string* serialized_device_security_profiles, + HashAlgorithm* hash_algorithm, std::string* signature); + + // Validate signature and update security profile list for custom dsps. + Status ValidateAndUpdateProfileList( + const std::string& root_certificate_public_key, + const std::string& serialized_device_security_profiles, + HashAlgorithm hash_algorithm, const std::string& signature, + int* added_profile_num); + protected: void ClearAllProfiles(); private: + // Add Widevine default profiles into profile_list. The number of added + // default profiles will be returned. + virtual int AddDefaultProfiles(); + // Add Widevine custom profiles into profile_list. The number of added custom + // profiles will be returned. + virtual int AddCustomProfiles( + const DeviceSecurityProfileList& device_security_profile_list); + virtual int GetDefaultProfileStrings( + std::vector* default_profile_strings) const; + bool DoesProfileQualify(const SecurityProfile& profile, const ClientIdentification& client_id, const ProvisionedDeviceInfo& device_info) const; @@ -87,9 +131,19 @@ class SecurityProfileList { bool IsProfileActive(const SecurityProfile& profile, int64_t current_time_seconds) const; + bool InsertProfileLocked(const SecurityProfile& profile_to_insert) + ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + // Return true if a profile already exists in the profile_list. + bool DoesProfileExistLocked(const SecurityProfile& profile) const + ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + void ClearAllDefaultProfilesLocked() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + void ClearAllCustomProfilesLocked() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); mutable absl::Mutex mutex_; // Security profiles std::string profile_namespace_; + // TODO(user): Modify as Map. std::vector security_profiles_ ABSL_GUARDED_BY(mutex_); }; diff --git a/example/test_ecmg_messages.h b/example/test_ecmg_messages.h index 9adb9ba..3e98387 100644 --- a/example/test_ecmg_messages.h +++ b/example/test_ecmg_messages.h @@ -104,7 +104,7 @@ constexpr char kTestEcmgChannelStatus[] = { constexpr char kTestEcmgStreamSetupWithPrivateParameters[] = { '\x03', // protocol_version '\x01', '\x01', // message_type - Stream_setup - '\x00', '\xae', // message_length + '\x00', '\xa8', // message_length '\x00', '\x0e', // parameter_type - ECM_channel_id '\x00', '\x02', // parameter_length '\x00', '\x01', // parameter_value @@ -117,9 +117,6 @@ constexpr char kTestEcmgStreamSetupWithPrivateParameters[] = { '\x00', '\x10', // parameter_type - nominal_CP_duration '\x00', '\x02', // parameter_length '\x00', '\x64', // parameter_value - '\x80', '\x02', // parameter_type - STREAM_TRACK_TYPE - '\x00', '\x02', // parameter_length - 'S', 'D', // parameter_value '\x80', '\x03', // parameter_type - CONTENT_IV '\x00', '\x10', // parameter_length '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', // @@ -245,7 +242,7 @@ constexpr char kTestEcmgCwProvision[] = { constexpr char kTestEcmgCwProvisionWithAccessCriteria[] = { '\x03', // protocol_version '\x02', '\x01', // message_type - CW_provision - '\x00', '\xee', // message_length + '\x00', '\xe8', // message_length '\x00', '\x0e', // parameter_type - ECM_channel_id '\x00', '\x02', // parameter_length '\x00', '\x01', // parameter_value @@ -269,16 +266,13 @@ constexpr char kTestEcmgCwProvisionWithAccessCriteria[] = { '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', // '\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f', // '\x00', '\x0d', // parameter_type - access_criteria - '\x00', '\xa6', // parameter_length + '\x00', '\xa0', // parameter_length '\x80', '\x00', // access_criteria parameter_type - AGE_RESTRICTION '\x00', '\x01', // parameter_length '\x00', // parameter_value '\x80', '\x01', // access_criteria parameter_type - CRYPTO_MODE '\x00', '\x07', // parameter_length 'A', 'e', 's', 'S', 'c', 't', 'e', // parameter_value - '\x80', '\x02', // access_criteria parameter_type - STREAM_TRACK_TYPE - '\x00', '\x02', // parameter_length - 'S', 'D', // parameter_value '\x80', '\x03', // access_criteria parameter_type - CONTENT_IV '\x00', '\x10', // parameter_length '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', // diff --git a/example/wv_cas_ecm_example b/example/wv_cas_ecm_example index 1d6c2af..7925aa0 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 c59e00e..88b38d0 100644 --- a/example/wv_cas_ecm_example.cc +++ b/example/wv_cas_ecm_example.cc @@ -21,37 +21,60 @@ #include "media_cas_packager_sdk/public/wv_cas_ecm.h" #include "media_cas_packager_sdk/public/wv_cas_types.h" -const size_t kContentIvSize = 16; // 8 or 16 -const bool kKeyRotation = true; // whether key rotation is enabled -const char kCryptoMode[] = +namespace { + +constexpr int kEcmVersion = 3; // Choices are 2 and 3. + +constexpr size_t kContentIvSize = 16; // 8 or 16 +constexpr bool kKeyRotation = true; // whether key rotation is enabled +constexpr char kCryptoMode[] = "AesScte"; // "AesCbc", "AesCtr", "DvbCsa2", "DvbCsa3", "AesOfb", "AesScte" -const int kEcmPid = 149; // PID for the ECM packet -const int kAgeRestriction = 0; // Age restriction for the ECM -const char kOutputFile[] = - "/tmp/ecm.ts"; // ECM TS packet will be output to here -const uint8_t kTableId = 0x80; // 0x80 or 0x81 -const char kDefaultTrackTypeSd[] = "SD"; +constexpr int kEcmPid = 149; // PID for the ECM packet +constexpr int kAgeRestriction = 0; // Age restriction for the ECM +constexpr char kOutputFile[] = + "/tmp/ecm.ts"; // ECM TS packet will be output to here +constexpr uint8_t kTableId = 0x80; // 0x80 or 0x81 +constexpr char kDefaultTrackTypeSd[] = "SD"; -const char kEvenKey[] = "even_key........"; // 16 bytes -const char kEvenKeyId[] = "even_key_id....."; // 16 bytes -const char kEvenContentIv8Bytes[] = "even_iv."; // 8 bytes -const char kEvenContentIv16Bytes[] = "even_iv.even_iv."; // 16 bytes -const char kEvenEntitlementKeyId[] = "fake_key_id1...."; // 16 bytes -const char kEvenEntitlementKey[] = - "fakefakefakefakefakefakefake1..."; // 32 bytes -const char kEvenWrapIv[] = "even_warp_iv...."; // 16 bytes +constexpr char kEvenKey[] = "even_key........"; // 16 bytes +constexpr char kEvenKeyId[] = "even_key_id....."; // 16 bytes +constexpr char kEvenContentIv8Bytes[] = "even_iv."; // 8 bytes +constexpr char kEvenContentIv16Bytes[] = "even_iv.even_iv."; // 16 bytes +constexpr char kEvenEntitlementKeyId[] = "fake_key_id1...."; // 16 bytes +constexpr char kEvenEntitlementKey[] = + "fakefakefakefakefakefakefake1..."; // 32 bytes +constexpr char kEvenWrapIv[] = "even_warp_iv...."; // 16 bytes -const char kOddKey[] = "odd_key........."; // 16 bytes -const char kOddKeyId[] = "odd_key_id......"; // 16 bytes -const char kOddContentIv8Bytes[] = "odd_iv.."; // 8 bytes -const char kOddContentIv16Bytes[] = "odd_iv..odd_iv.."; // 16 bytes -const char kOddEntitlementKeyId[] = "fake_key_id2...."; // 16 bytes -const char kOddEntitlementKey[] = - "fakefakefakefakefakefakefake2..."; // 32 bytes -const char kOddWrapIv[] = "odd_warp_iv....."; // 16 bytes +constexpr char kOddKey[] = "odd_key........."; // 16 bytes +constexpr char kOddKeyId[] = "odd_key_id......"; // 16 bytes +constexpr char kOddContentIv8Bytes[] = "odd_iv.."; // 8 bytes +constexpr char kOddContentIv16Bytes[] = "odd_iv..odd_iv.."; // 16 bytes +constexpr char kOddEntitlementKeyId[] = "fake_key_id2...."; // 16 bytes +constexpr char kOddEntitlementKey[] = + "fakefakefakefakefakefakefake2..."; // 32 bytes +constexpr char kOddWrapIv[] = "odd_warp_iv....."; // 16 bytes + +// Fingerprinting and service blocking are only available with ECM v3+. +constexpr char kFingerprintingControl[] = "ctr"; +constexpr char kServiceBlockingDeviceGroup1[] = "group"; +constexpr char kServiceBlockingDeviceGroup2[] = "g2"; +constexpr unsigned char kTestECPrivateKey2Secp256r1[] = { + 0x30, 0x77, 0x02, 0x01, 0x01, 0x04, 0x20, 0x34, 0x9a, 0xf2, 0x95, + 0x94, 0xd4, 0xca, 0xb9, 0xa0, 0x81, 0xe4, 0x1c, 0xf5, 0xde, 0x8d, + 0x23, 0xf6, 0x79, 0xba, 0x3c, 0x6e, 0xc9, 0x0b, 0x56, 0x0f, 0x07, + 0x5e, 0x9f, 0xe9, 0x38, 0x18, 0xfc, 0xa0, 0x0a, 0x06, 0x08, 0x2a, + 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0xa1, 0x44, 0x03, 0x42, + 0x00, 0x04, 0x7b, 0x2a, 0x61, 0x59, 0xe5, 0x1b, 0xb6, 0x30, 0x7b, + 0x59, 0x98, 0x42, 0x59, 0x37, 0xfb, 0x46, 0xfe, 0x53, 0xfe, 0x32, + 0xa1, 0x5c, 0x93, 0x36, 0x11, 0xb0, 0x5a, 0xae, 0xa4, 0x48, 0xe3, + 0x20, 0x12, 0xce, 0x78, 0xa7, 0x7f, 0xfd, 0x73, 0x5e, 0x09, 0x77, + 0x53, 0x77, 0x8f, 0xd6, 0x1b, 0x26, 0xfa, 0xc4, 0x2c, 0xc4, 0x11, + 0xfa, 0x72, 0x6a, 0xbe, 0x94, 0x78, 0x4d, 0x74, 0x20, 0x27, 0x86}; const size_t kTsPacketSize = 188; +const size_t kMaxPossibleTsPacketsSizeBytes = 6 * kTsPacketSize; +using widevine::cas::EcmVersion; using widevine::cas::EntitlementKeyInfo; using widevine::cas::WvCasContentKeyInfo; using widevine::cas::WvCasEcmParameters; @@ -68,6 +91,9 @@ WvCasEcmParameters CreateWvCasEcmParameters(bool key_rotation, std::cerr << "Unsupported crypto mode " << kCryptoMode << std::endl; } params.age_restriction = kAgeRestriction; + params.ecm_version = kEcmVersion <= 2 ? EcmVersion::kV2 : EcmVersion::kV3; + params.ecc_private_signing_key.assign(std::begin(kTestECPrivateKey2Secp256r1), + std::end(kTestECPrivateKey2Secp256r1)); return params; } @@ -115,6 +141,8 @@ std::vector CreateContentKeyInfo(bool key_rotation, return content_keys; } +} // namespace + int main(int argc, char** argv) { WvCasEcmParameters params = CreateWvCasEcmParameters(kKeyRotation, kContentIvSize); @@ -122,6 +150,17 @@ int main(int argc, char** argv) { CreateInjectedEntitlements(kKeyRotation); widevine::cas::WvCasEcm wv_cas_ecm(params, entitlements); + if (params.ecm_version == EcmVersion::kV3) { + widevine::cas::EcmFingerprintingParams fingerprinting_params; + fingerprinting_params.control = kFingerprintingControl; + wv_cas_ecm.SetFingerprinting(&fingerprinting_params); + + widevine::cas::EcmServiceBlockingParams service_blocking_params; + service_blocking_params.device_groups = {kServiceBlockingDeviceGroup1, + kServiceBlockingDeviceGroup2}; + wv_cas_ecm.SetServiceBlocking(&service_blocking_params); + } + std::vector content_keys = CreateContentKeyInfo(kKeyRotation, kContentIvSize); std::string ecm; @@ -147,16 +186,17 @@ int main(int argc, char** argv) { std::cout << std::endl; } // Generate ECM TS Packet. - uint8_t packet[kTsPacketSize]; + uint8_t packet[kMaxPossibleTsPacketsSizeBytes]; + ssize_t packet_size = kMaxPossibleTsPacketsSizeBytes; uint8_t continuity_counter; // not used. - status = wv_cas_ecm.GenerateTsPacket(ecm, kEcmPid, kTableId, - &continuity_counter, packet); + status = wv_cas_ecm.GenerateTsPacket( + ecm, kEcmPid, kTableId, &continuity_counter, packet, &packet_size); if (!status.ok()) { std::cerr << "Failed to create ECM TS packet: " << status << std::endl; return -1; } else { std::cout << "TS packet bytes: "; - for (size_t i = 0; i < kTsPacketSize; i++) { + for (size_t i = 0; i < packet_size; i++) { printf("'\\x%02x', ", static_cast(packet[i])); } std::cout << std::endl; @@ -165,7 +205,7 @@ int main(int argc, char** argv) { std::ofstream file; file.open(kOutputFile, std::ios_base::binary); assert(file.is_open()); - file.write(reinterpret_cast(packet), kTsPacketSize); + file.write(reinterpret_cast(packet), packet_size); file.close(); return 0; diff --git a/example/wv_cas_emm_example b/example/wv_cas_emm_example index 7522f95..2e0b420 100644 Binary files a/example/wv_cas_emm_example and b/example/wv_cas_emm_example differ diff --git a/example/wv_cas_key_fetcher_example b/example/wv_cas_key_fetcher_example index 1bc6f43..7448112 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 e1ea4da..3717716 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 eefb85b..2137e54 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 f0cfd6f..409f15d 100644 --- a/example/wv_ecmg_example.cc +++ b/example/wv_ecmg_example.cc @@ -34,6 +34,18 @@ using widevine::cas::WvCasEcmgClientHandler; constexpr int kServerPortNumber = 1234; constexpr int kListenQueueSize = 20; constexpr int kBufferSizeBytes = 2048; +constexpr unsigned char kTestECPrivateKey2Secp256r1[] = { + 0x30, 0x77, 0x02, 0x01, 0x01, 0x04, 0x20, 0x34, 0x9a, 0xf2, 0x95, + 0x94, 0xd4, 0xca, 0xb9, 0xa0, 0x81, 0xe4, 0x1c, 0xf5, 0xde, 0x8d, + 0x23, 0xf6, 0x79, 0xba, 0x3c, 0x6e, 0xc9, 0x0b, 0x56, 0x0f, 0x07, + 0x5e, 0x9f, 0xe9, 0x38, 0x18, 0xfc, 0xa0, 0x0a, 0x06, 0x08, 0x2a, + 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0xa1, 0x44, 0x03, 0x42, + 0x00, 0x04, 0x7b, 0x2a, 0x61, 0x59, 0xe5, 0x1b, 0xb6, 0x30, 0x7b, + 0x59, 0x98, 0x42, 0x59, 0x37, 0xfb, 0x46, 0xfe, 0x53, 0xfe, 0x32, + 0xa1, 0x5c, 0x93, 0x36, 0x11, 0xb0, 0x5a, 0xae, 0xa4, 0x48, 0xe3, + 0x20, 0x12, 0xce, 0x78, 0xa7, 0x7f, 0xfd, 0x73, 0x5e, 0x09, 0x77, + 0x53, 0x77, 0x8f, 0xd6, 0x1b, 0x26, 0xfa, 0xc4, 0x2c, 0xc4, 0x11, + 0xfa, 0x72, 0x6a, 0xbe, 0x94, 0x78, 0x4d, 0x74, 0x20, 0x27, 0x86}; void BuildEcmgConfig(EcmgConfig* config) { config->delay_start = 200; // in milliseconds. @@ -43,6 +55,9 @@ void BuildEcmgConfig(EcmgConfig* config) { config->access_criteria_transfer_mode = 0; config->number_of_content_keys = 2; config->crypto_mode = widevine::cas::CryptoMode::kAesCtr; + config->ecc_private_signing_key.assign( + std::begin(kTestECPrivateKey2Secp256r1), + std::end(kTestECPrivateKey2Secp256r1)); } void PrintMessage(const std::string& description, const char* const message, diff --git a/libmedia_cas_packager_sdk.so b/libmedia_cas_packager_sdk.so index 1d44c2b..d068833 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_ecm.h b/media_cas_packager_sdk/public/wv_cas_ecm.h index 5aa2cb7..e191f08 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 @@ -19,6 +20,8 @@ namespace widevine { namespace cas { +class Ecm; + // Information needed to generate content key portion of ECM. // Fields: // |key_id| key ID for the content key, must be 16 bytes. @@ -44,11 +47,19 @@ struct WvCasContentKeyInfo { // |crypto_mode| the encryption mode used for the content stream. // A constant of type CryptoMode. // |age_restriction| minimum age required; the value represents actual age. +// |cas_id| CA system id that is in the ECM. Must be 0x4AD4 or 0x56C0~0x56C9 +// (all inclusive). +// |ecm_version| version of generated ECM. +// |ecc_private_signing_key| Private signing key used to sign ECM data. Must +// be an elliptic-curve cryptography key. struct WvCasEcmParameters { EcmIvSize content_iv_size = kIvSize8; bool key_rotation_enabled = true; CryptoMode crypto_mode = CryptoMode::kAesCtr; uint8_t age_restriction = 0; + uint16_t cas_id = 0x4AD4; + EcmVersion ecm_version = EcmVersion::kV2; + std::string ecc_private_signing_key; }; // Class for generating Widevine CAS ECMs. @@ -66,10 +77,23 @@ class WvCasEcm { const std::vector& injected_entitlements); WvCasEcm(const WvCasEcm&) = delete; WvCasEcm& operator=(const WvCasEcm&) = delete; - virtual ~WvCasEcm() = default; + virtual ~WvCasEcm(); - // Accept keys and IVs and construct an ECM that will fit into a Transport - // Stream packet payload (184 bytes). + // Set fingerprinting info that will be embedded into the generated ECM. The + // configuration will be used in all following ECMs generated by calling + // GenerateEcm() or GenerateSingleKeyEcm(). + // |fingerprinting| may be set to nullptr to clear the fingerprinting info. + virtual void SetFingerprinting(const EcmFingerprintingParams* fingerprinting); + + // Set service blocking info that will be embedded into the generated ECM. The + // configuration will be used in all following ECMs generated by calling + // GenerateEcm() or GenerateSingleKeyEcm(). + // |service_blocking| may be set to nullptr to clear the service blocking + // info. + virtual void SetServiceBlocking( + const EcmServiceBlockingParams* service_blocking); + + // Constructs a Widevine ECM using the provided key info. // Args: // |even_key| information for even key to be encoded into ECM. // |odd_key| information for odd key to be encoded into ECM. @@ -78,15 +102,14 @@ class WvCasEcm { // The |even_key| and |odd_key| contents (specifically IV sizes) must be // consistent with the initialized settings. // The even_key and odd_key will be wrapped using the appropriate - // entitlement key. Wrapping modifies the original structure. + // entitlement key. virtual Status GenerateEcm(const WvCasContentKeyInfo& even_key, const WvCasContentKeyInfo& odd_key, const std::string& track_type, std::string* serialized_ecm) const; - // Accept a key and IV and construct an ECM that will fit into a Transport - // Stream packet payload (184 bytes). This call is specifically for the case - // where key rotation is disabled. + // Constructs a Widevine ECM using the provided key info. This call is + // specifically for the case where key rotation is disabled. // Args: // |key| information for key to be encoded into ECM. // |track_type| the track that the key is being used to encrypt. @@ -112,16 +135,18 @@ class WvCasEcm { // it will be incremented, only last 4 bits are used // - |packet| a buffer of size at least 188 bytes to be used to return // the generated TS packet + // - |packet_size| is the size of the allocated |packet|. It will be updated + // as the number of bytes actually used. // // Returns: // - A status indicating whether there was any error during processing static Status GenerateTsPacket(const std::string& ecm, uint16_t pid, uint8_t table_id, uint8_t* continuity_counter, - uint8_t* packet); + uint8_t* packet, ssize_t* packet_size); private: + std::unique_ptr ecm_; WvCasEcmParameters ecm_param_; - std::vector injected_entitlements_; }; } // namespace cas 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 c326db8..083f356 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 @@ -36,6 +36,27 @@ class WvCasEcmgClientHandler { // Calling this function is optional. void SetCustomEntitlementKeyFetcherFunc(EntitlementKeyFetcherFunc fetcher); + // Sets the custom access criteria processing function used by ECMG to get + // information including entitlement keys, content iv, crypto mode, etc. + // Calling this function is optional: If the function is set, access criteria + // received in the CwProvision message from SCS will be processed by this + // callback function (SDK will no longer process the access criteria). If not + // set, access criteria will be processed by the SDK. + void SetCustomAccessCriteriaProcessFunc( + CustomAccessCriteriaProcessFunc ac_processor); + + // Sets the fingerprinting setting function, which will be called upon each + // CwProvisioning request. It controls the fingerprinting information carried + // in ECMs. + void SetFingerprintingSettingFunc( + FingerprintingSettingFunc fingerprinting_func); + + // Sets the service blocking setting function, which will be called upon each + // CwProvisioning request. It controls the service blocking information + // carried in ECMs. + void SetServiceBlockingSettingFunc( + ServiceBlockingSettingFunc service_blocking_func); + // Handles a |request| from the SCS client. If any response is generated, it // will return the response via |response_buffer| and |response_length|. // Args: diff --git a/media_cas_packager_sdk/public/wv_cas_types.h b/media_cas_packager_sdk/public/wv_cas_types.h index 52e378a..c907623 100644 --- a/media_cas_packager_sdk/public/wv_cas_types.h +++ b/media_cas_packager_sdk/public/wv_cas_types.h @@ -9,6 +9,7 @@ #ifndef MEDIA_CAS_PACKAGER_SDK_PUBLIC_WV_CAS_TYPES_H_ #define MEDIA_CAS_PACKAGER_SDK_PUBLIC_WV_CAS_TYPES_H_ +#include #include #include @@ -65,6 +66,8 @@ struct EntitlementKeyInfo { std::string key_value; // must be 32 bytes. }; +enum class EcmVersion : int { kV2 = 0, kV3 = 1 }; + // A struct that captures the Simulcrypt ECMG configurations. Most fields are // Simulcrypt standard fields (See ETSI TS 103 197 V1.5.1 (2008-10) // Section 5.3). @@ -109,6 +112,11 @@ struct EcmgConfig { // will be included in the ECM. If new crypto_mode is received from SCS, ECM // will use the new one. CryptoMode crypto_mode; + // Version of generated ECM. + EcmVersion ecm_version = EcmVersion::kV2; + // Private signing key used to sign ECM data. Must be an elliptic-curve + // cryptography key. + std::string ecc_private_signing_key; }; // A custom entitlement key fetching function used by ECMG to fetch entitlement @@ -127,6 +135,82 @@ struct EcmgConfig { using EntitlementKeyFetcherFunc = std::vector (*)( uint16_t channel_id, uint16_t stream_id, uint16_t ecm_id); +struct EcmgCustomParameters { + // Minimum age required for the content. The value represents actual age. + // A negative value means the field is not set. + int age_restriction = -1; + // The encryption mode used for the content stream. + std::string crypto_mode; + // Initial vector used in the content encryption, must be 8 (DvbCsa2) or 16 + // bytes (all other crypto modes). The size of the vector must not exceed 2. + // The first entry is for the even key slot, the second entry is for the odd + // key slot. It is suggested that the same IV used for both even and odd key + // slot for better decrypting performance. + std::vector content_ivs; + // Entitlement keys used to encrypt the content keys (control words). + // The size of the vector must not exceed 2. When the vector size is 2, one of + // them must be specified as even key and the other must be odd key (sequence + // does not matter). + std::vector entitlement_keys; + // Fingerprinting control that will be embedded in ECM. The control is opaque + // to Widevine. + std::string fingerprinting_control; + // Devices that should have service blocking enforced. The blocking starts + // when the ECM is received, and stops util the device is no longer in + // |device_groups|. + std::vector service_blocking_groups; +}; + +// A custom access control processing function used by ECMG to get information +// including entitlement keys, content iv, crypto mode, etc. If set, the +// function will be called when an access criteria is received in the +// CwProvision message from SCS. The access criteria will no longer be processed +// by the SDK. +// Args: +// |channel_id| is the channel id received at ECMG from SCS when setting up +// the channel. +// |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 + CustomAccessCriteriaProcessFunc; + +struct EcmFingerprintingParams { + // The |control| is opaque to Widevine. + std::string control; +}; + +// Function that will be called upon each CwProvisioning request. It controls +// the fingerprinting information carried in ECMs. +// Args: +// |channel_id| is the channel id received at ECMG from SCS when setting up +// the channel. +// |stream_id| is the stream id received at ECMG from SCS when setting up +// the stream. +typedef std::function + FingerprintingSettingFunc; + +struct EcmServiceBlockingParams { + // Devices that should have service blocking enforced. The blocking starts + // when the ECM is received, and stops util the device is no longer in + // |device_groups|. + std::vector device_groups; +}; + +// Function that will be called upon each CwProvisioning request. It controls +// the service blocking information carried in ECMs. +// Args: +// |channel_id| is the channel id received at ECMG from SCS when setting up +// the channel. +// |stream_id| is the stream id received at ECMG from SCS when setting up +// the stream. +typedef std::function + ServiceBlockingSettingFunc; + struct WvCasEncryptionRequest { std::string content_id; std::string provider; diff --git a/media_cas_packager_sdk/public/wv_ecmg b/media_cas_packager_sdk/public/wv_ecmg index 5e3e575..57f30ad 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 9bf3ade..352873f 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/media_cas.proto b/protos/public/media_cas.proto index e9aea06..405ecac 100644 --- a/protos/public/media_cas.proto +++ b/protos/public/media_cas.proto @@ -50,3 +50,62 @@ message EmmPayload { repeated Fingerprinting fingerprinting = 1; repeated ServiceBlocking service_blocking = 2; } + +message EcmMetaData { + enum CipherMode { + UNSPECIFIED = 0; + AES_CBC = 1; + AES_CTR = 2; + DVB_CSA2 = 3; + DVB_CSA3 = 4; + AES_OFB = 5; + AES_SCTE52 = 6; + } + // Required. The cipher mode used to encrypt/decrypt the content. + optional CipherMode cipher_mode = 1; + // Optional. The minimum age required to watch the content. The value + // represents actual age, with 0 means no restriction. + optional uint32 age_restriction = 2 [default = 0]; +} + +message EcmKeyData { + // The wrapped content key data (aka control word). + // Required. + optional bytes wrapped_key_data = 1; + // The ID of the entitlement key used to wrap the content key. The secure key + // data associated with this ID is held by the license server. The client gets + // the key from the license server through a license request. + // Required for the even key data, optional for the odd key data if it is the + // same as the even key data. + optional bytes entitlement_key_id = 2; + // IV for decrypting the wrapped_key_data. + // Required for the even key data, optional for the odd key data if it is the + // same as the even key data. + optional bytes wrapped_key_iv = 3; + // IV for decrypting the content stream. + // Optional. If not specified in the even key data, 8 bytes 0x00 will be used; + // If not specified in the odd key data, the same content iv in the even key + // data will be used. + optional bytes content_iv = 4; +} + +message EcmPayload { + // Required. Meta info carried by the ECM. + optional EcmMetaData meta_data = 1; + // Required. The key data for the even slot. + optional EcmKeyData even_key_data = 2; + // Optional. The key data for the odd slot if key rotation is enabled. + optional EcmKeyData odd_key_data = 3; + // Optional. Widevine fingerprinting information. + optional Fingerprinting fingerprinting = 4; + // Optional. Widevine service blocking information. + optional ServiceBlocking service_blocking = 5; +} + +// The payload field for an ECM with signature. +message SignedEcmPayload { + // Serialized EcmPayload. + optional bytes serialized_payload = 1; + // ECC (Elliptic Curve Cryptography) signature of |serialized_payload|. + optional bytes signature = 2; +}