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:
Fang Yu
2019-01-15 12:50:30 -08:00
parent 4f5c02fb39
commit 6e1f377329
4 changed files with 181 additions and 1 deletions

View File

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

View File

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

View File

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

View File

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