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"],
|
srcs = ["wv_cas_types.cc"],
|
||||||
hdrs = ["wv_cas_types.h"],
|
hdrs = ["wv_cas_types.h"],
|
||||||
copts = PUBLIC_COPTS,
|
copts = PUBLIC_COPTS,
|
||||||
deps = ["//base"],
|
deps = [
|
||||||
|
"//base",
|
||||||
|
"//protos/public:media_cas_encryption_proto",
|
||||||
|
"//common:status",
|
||||||
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
cc_test(
|
cc_test(
|
||||||
|
|||||||
@@ -10,6 +10,13 @@
|
|||||||
|
|
||||||
#include "glog/logging.h"
|
#include "glog/logging.h"
|
||||||
#include "base/macros.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 widevine {
|
||||||
namespace cas {
|
namespace cas {
|
||||||
@@ -72,5 +79,62 @@ bool StringToCryptoMode(const std::string& str, CryptoMode* mode) {
|
|||||||
return false;
|
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 cas
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|||||||
@@ -10,6 +10,9 @@
|
|||||||
#define MEDIA_CAS_PACKAGER_SDK_PUBLIC_WV_CAS_TYPES_H_
|
#define MEDIA_CAS_PACKAGER_SDK_PUBLIC_WV_CAS_TYPES_H_
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "common/status.h"
|
||||||
|
|
||||||
namespace widevine {
|
namespace widevine {
|
||||||
namespace cas {
|
namespace cas {
|
||||||
@@ -64,6 +67,63 @@ bool CryptoModeToString(CryptoMode mode, std::string* str);
|
|||||||
// Returns false if str is not a valid CryptoMode.
|
// Returns false if str is not a valid CryptoMode.
|
||||||
bool StringToCryptoMode(const std::string& str, CryptoMode* mode);
|
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 cas
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include "media_cas_packager_sdk/public/wv_cas_types.h"
|
#include "media_cas_packager_sdk/public/wv_cas_types.h"
|
||||||
|
|
||||||
|
#include "testing/gmock.h"
|
||||||
#include "testing/gunit.h"
|
#include "testing/gunit.h"
|
||||||
|
|
||||||
namespace widevine {
|
namespace widevine {
|
||||||
@@ -46,5 +47,56 @@ TEST(WvCasTypesTest, StringToCryptoMode) {
|
|||||||
EXPECT_FALSE(StringToCryptoMode("invalid crypto mode", &crypto_mode));
|
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 cas
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|||||||
Reference in New Issue
Block a user