Support for group license

Content keys in ECM v3 can now additionally be encrypted by group
entitlement keys.
This commit is contained in:
Widevine Buildbot
2021-03-04 22:51:24 +00:00
parent 810ceaf1a1
commit b215264c6d
22 changed files with 123 additions and 33 deletions

View File

@@ -11,7 +11,7 @@
namespace widevine {
enum class HashAlgorithm { kUnspecified, kSha1, kSha256 };
enum class HashAlgorithm { kUnspecified, kSha1, kSha256, kSha384 };
} // namespace widevine

View File

@@ -14,6 +14,8 @@
#ifndef COMMON_SECURITY_PROFILE_LIST_H_
#define COMMON_SECURITY_PROFILE_LIST_H_
#include <cstdint>
#include "absl/synchronization/mutex.h"
#include "common/hash_algorithm.h"
#include "common/status.h"

Binary file not shown.

View File

@@ -12,6 +12,7 @@
#include <stdio.h>
#include <cassert>
#include <cstdint>
#include <fstream>
#include <iostream>
#include <string>
@@ -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()) {

Binary file not shown.

View File

@@ -8,6 +8,7 @@
// Example of how to use the wv_cas_emm library.
#include <cstdint>
#include <iostream>
#include <memory>
#include <string>

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -9,12 +9,14 @@
// Example of Simulcrypt ECMG with custom entitlement key fetcher.
#include <netinet/in.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <unistd.h>
#include <cerrno>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <iostream>
@@ -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,14 +89,25 @@ void ServeClient(int socket_fd, WvCasEcmgClientHandler* ecmg) {
return;
}
PrintMessage("Request", request, request_length);
ecmg->HandleRequest(kBufferSizeBytes, request, kBufferSizeBytes, response,
&response_length);
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;
std::cerr << "Failed to send response to client" << std::endl;
return;
}
}
}
}
std::vector<EntitlementKeyInfo> MyEntitlementKeyFetcherFun(uint16_t channel_id,
@@ -118,10 +130,10 @@ std::vector<EntitlementKeyInfo> 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 {

Binary file not shown.

View File

@@ -11,6 +11,7 @@
#include <stddef.h>
#include <cstdint>
#include <string>
#include <vector>
@@ -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<std::string>& entitlement_key_ids,
std::string* serialized_ca_desc) const;

View File

@@ -8,6 +8,8 @@
#include "media_cas_packager_sdk/public/wv_cas_curl_key_fetcher.h"
#include <cstdint>
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "curl/curl.h"

View File

@@ -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 <cstdint>
#include <memory>
#include <string>
#include <vector>
@@ -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<std::string>& 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<std::string>& group_ids,
std::string* serialized_ecm) const;
// Generate a TS packet with the given |ecm| as payload.

View File

@@ -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.

View File

@@ -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 <cstdint>
#include <memory>
#include <string>
#include <vector>

View File

@@ -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 <cstdint>
#include <functional>
#include <string>
#include <tuple>
#include <vector>
#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<EcmgCustomParameters(uint16_t channel_id, uint16_t stream_id,
const std::string& access_criteria)>
// |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<Status(uint16_t channel_id, uint16_t stream_id,
const std::string& access_criteria,
EcmgCustomParameters& ecmg_params)>
CustomAccessCriteriaProcessFunc;
struct EcmFingerprintingParams {

View File

@@ -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;
}

View File

@@ -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.

View File

@@ -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 {