Create some utility types/functions to help partners to create CasEncryptionRequest in JSON format and process CasEncryptionResponse in JSON format.
The idea is that partner can take the CasEncryptionRequest in JSON to construct a signed license request, send it to Widevine license service (using whatever tool they have); and once they have a response, they can use another utility here to parse and understand what is in the response JSON. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=229422648
This commit is contained in:
@@ -154,7 +154,11 @@ cc_library(
|
||||
srcs = ["wv_cas_types.cc"],
|
||||
hdrs = ["wv_cas_types.h"],
|
||||
copts = PUBLIC_COPTS,
|
||||
deps = ["//base"],
|
||||
deps = [
|
||||
"//base",
|
||||
"//protos/public:media_cas_encryption_proto",
|
||||
"//common:status",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
|
||||
@@ -10,6 +10,13 @@
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "base/macros.h"
|
||||
#include "google/protobuf/util/json_util.h"
|
||||
#include "common/status.h"
|
||||
#include "protos/public/media_cas_encryption.pb.h"
|
||||
|
||||
using google::protobuf::util::JsonPrintOptions;
|
||||
using google::protobuf::util::JsonStringToMessage;
|
||||
using google::protobuf::util::MessageToJsonString;
|
||||
|
||||
namespace widevine {
|
||||
namespace cas {
|
||||
@@ -72,5 +79,62 @@ bool StringToCryptoMode(const std::string& str, CryptoMode* mode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Status CreateWvCasEncryptionRequestJson(const WvCasEncryptionRequest& request,
|
||||
std::string* request_json) {
|
||||
CHECK(request_json);
|
||||
|
||||
CasEncryptionRequest request_proto;
|
||||
request_proto.set_content_id(request.content_id);
|
||||
request_proto.set_provider(request.provider);
|
||||
for (const std::string& track_type : request.track_types) {
|
||||
request_proto.add_track_types(track_type);
|
||||
}
|
||||
request_proto.set_key_rotation(request.key_rotation);
|
||||
|
||||
JsonPrintOptions print_options;
|
||||
// Set this option so that the json output is
|
||||
// {"content_id":"MjExNDA4NDQ=", ...
|
||||
// instead of
|
||||
// {"contentId":"MjExNDA4NDQ=", ...
|
||||
print_options.preserve_proto_field_names = true;
|
||||
// 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.");
|
||||
}
|
||||
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
Status 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.");
|
||||
}
|
||||
|
||||
response->status =
|
||||
static_cast<WvCasEncryptionResponse::Status>(response_proto.status());
|
||||
response->status_message = response_proto.status_message();
|
||||
response->content_id = response_proto.content_id();
|
||||
for (const auto& key_info_proto : response_proto.entitlement_keys()) {
|
||||
WvCasEncryptionResponse::KeyInfo key_info;
|
||||
key_info.key_id = key_info_proto.key_id();
|
||||
key_info.key = key_info_proto.key();
|
||||
key_info.track_type = key_info_proto.track_type();
|
||||
key_info.key_slot = static_cast<WvCasEncryptionResponse::KeyInfo::KeySlot>(
|
||||
key_info_proto.key_slot());
|
||||
response->entitlement_keys.push_back(key_info);
|
||||
}
|
||||
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
} // namespace cas
|
||||
} // namespace widevine
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
#define MEDIA_CAS_PACKAGER_SDK_PUBLIC_WV_CAS_TYPES_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/status.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace cas {
|
||||
@@ -64,6 +67,63 @@ bool CryptoModeToString(CryptoMode mode, std::string* str);
|
||||
// Returns false if str is not a valid CryptoMode.
|
||||
bool StringToCryptoMode(const std::string& str, CryptoMode* mode);
|
||||
|
||||
struct WvCasEncryptionRequest {
|
||||
std::string content_id;
|
||||
std::string provider;
|
||||
// Track types such as "AUDIO", SD" or "HD".
|
||||
std::vector<std::string> track_types;
|
||||
// Indicates if the client is using key rotation. If true, the server will
|
||||
// return one key for EVEN and one key for ODD, otherwise only a single key is
|
||||
// returned.
|
||||
bool key_rotation = true;
|
||||
};
|
||||
|
||||
struct WvCasEncryptionResponse {
|
||||
enum class Status {
|
||||
STATUS_UNSPECIFIED = 0,
|
||||
OK = 1,
|
||||
SIGNATURE_FAILED = 2,
|
||||
ACCESS_DENIED = 3,
|
||||
INTERNAL_ERROR = 4,
|
||||
INVALID_ARGUMENT = 5,
|
||||
PROVIDER_ID_MISSING = 6,
|
||||
CONTENT_ID_MISSING = 7,
|
||||
TRACK_TYPE_MISSING = 8
|
||||
};
|
||||
struct KeyInfo {
|
||||
enum class KeySlot {
|
||||
KEY_SLOT_UNSPECIFIED = 0,
|
||||
SINGLE = 1,
|
||||
EVEN = 2,
|
||||
ODD = 3
|
||||
};
|
||||
std::string key_id;
|
||||
std::string key;
|
||||
// Optional label used for the key.
|
||||
std::string track_type;
|
||||
KeySlot key_slot;
|
||||
};
|
||||
Status status;
|
||||
std::string status_message;
|
||||
std::string content_id;
|
||||
std::vector<KeyInfo> entitlement_keys;
|
||||
};
|
||||
|
||||
// Returns a WvCasEncryptionRequest in JSON format.
|
||||
// This request JSON can be later put into the 'request' field of a signed
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
|
||||
} // namespace cas
|
||||
} // namespace widevine
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "media_cas_packager_sdk/public/wv_cas_types.h"
|
||||
|
||||
#include "testing/gmock.h"
|
||||
#include "testing/gunit.h"
|
||||
|
||||
namespace widevine {
|
||||
@@ -46,5 +47,56 @@ TEST(WvCasTypesTest, StringToCryptoMode) {
|
||||
EXPECT_FALSE(StringToCryptoMode("invalid crypto mode", &crypto_mode));
|
||||
}
|
||||
|
||||
TEST(WvCasTypesTest, CreateWvCasEncryptionRequestJson) {
|
||||
WvCasEncryptionRequest request;
|
||||
request.content_id = "test_content_id";
|
||||
request.provider = "test_provider";
|
||||
request.track_types.push_back("SD");
|
||||
request.track_types.push_back("AUDIO");
|
||||
request.key_rotation = true;
|
||||
|
||||
std::string actual_request_json;
|
||||
EXPECT_OK(CreateWvCasEncryptionRequestJson(request, &actual_request_json));
|
||||
|
||||
// Content_id has been base64 encoded.
|
||||
std::string expected_request_json =
|
||||
"{\"content_id\":\"dGVzdF9jb250ZW50X2lk\",\"provider\":\"test_provider\","
|
||||
"\"track_types\":[\"SD\",\"AUDIO\"],\"key_rotation\":true}";
|
||||
EXPECT_EQ(expected_request_json, actual_request_json);
|
||||
}
|
||||
|
||||
TEST(WvCasTypesTest, ParseWvCasEncryptionResponseJson) {
|
||||
std::string response_json =
|
||||
"{\"status\":\"OK\",\"content_id\":\"MjExNDA4NDQ=\",\"entitlement_keys\":"
|
||||
"[{\"key_id\":\"ZmFrZV9rZXlfaWQxLi4uLg==\",\"key\":"
|
||||
"\"ZmFrZWZha2VmYWtlZmFrZWZha2VmYWtlZmFrZTEuLi4=\","
|
||||
"\"track_type\":\"SD\",\"key_slot\":\"EVEN\"},{\"key_id\":"
|
||||
"\"ZmFrZV9rZXlfaWQyLi4uLg==\",\"key\":"
|
||||
"\"ZmFrZWZha2VmYWtlZmFrZWZha2VmYWtlZmFrZTIuLi4=\",\"track_type\":\"SD\","
|
||||
"\"key_slot\":\"ODD\"}]}";
|
||||
|
||||
WvCasEncryptionResponse actual_response;
|
||||
EXPECT_OK(ParseWvCasEncryptionResponseJson(response_json, &actual_response));
|
||||
|
||||
EXPECT_EQ(WvCasEncryptionResponse::Status::OK, actual_response.status);
|
||||
// 21140844 is base64 decode of "MjExNDA4NDQ=".
|
||||
EXPECT_EQ("21140844", actual_response.content_id);
|
||||
ASSERT_EQ(2, actual_response.entitlement_keys.size());
|
||||
// Base64 decode of the key_id in the json.
|
||||
EXPECT_EQ("fake_key_id1....", actual_response.entitlement_keys.at(0).key_id);
|
||||
// Base64 decode of the key in the json.
|
||||
EXPECT_EQ("fakefakefakefakefakefakefake1...",
|
||||
actual_response.entitlement_keys.at(0).key);
|
||||
EXPECT_EQ("SD", actual_response.entitlement_keys.at(0).track_type);
|
||||
EXPECT_EQ(WvCasEncryptionResponse::KeyInfo::KeySlot::EVEN,
|
||||
actual_response.entitlement_keys.at(0).key_slot);
|
||||
EXPECT_EQ("fake_key_id2....", actual_response.entitlement_keys.at(1).key_id);
|
||||
EXPECT_EQ("fakefakefakefakefakefakefake2...",
|
||||
actual_response.entitlement_keys.at(1).key);
|
||||
EXPECT_EQ("SD", actual_response.entitlement_keys.at(1).track_type);
|
||||
EXPECT_EQ(WvCasEncryptionResponse::KeyInfo::KeySlot::ODD,
|
||||
actual_response.entitlement_keys.at(1).key_slot);
|
||||
}
|
||||
|
||||
} // namespace cas
|
||||
} // namespace widevine
|
||||
|
||||
Reference in New Issue
Block a user