WvPL License SDK release: 17.0.1

This commit is contained in:
Buildbot
2022-04-22 02:33:19 +00:00
parent 0b20fb7737
commit 743f4faefe
254 changed files with 12886 additions and 3492 deletions

View File

@@ -0,0 +1,246 @@
// Copyright 2018 Google LLC. All rights reserved.
#include <cstdint>
#include <iostream>
#include <map>
#include <string>
#include "absl/strings/escaping.h"
#include "sdk/external/cpp/wvpl/license_server_sdk/wvpl_environment.h"
#include "sdk/external/cpp/wvpl/license_server_sdk/wvpl_license_counter.h"
#include "sdk/external/cpp/wvpl/license_server_sdk/wvpl_session.h"
using video_widevine_server::wv_pl_sdk::HDCP_V1;
using video_widevine_server::wv_pl_sdk::HDCP_V2;
using video_widevine_server::wv_pl_sdk::kDeviceCertificateExpiration;
using video_widevine_server::wv_pl_sdk::kDrmCertificateType;
using video_widevine_server::wv_pl_sdk::VIDEO_HD;
using video_widevine_server::wv_pl_sdk::VIDEO_SD;
using video_widevine_server::wv_pl_sdk::WvPLEnvironment;
using video_widevine_server::wv_pl_sdk::WvPLKey;
using video_widevine_server::wv_pl_sdk::WvPLPlaybackPolicy;
using video_widevine_server::wv_pl_sdk::WvPLSession;
using video_widevine_server::wv_pl_sdk::WvPLStatus;
const uint32_t kSystemId = 0x112;
const char kKeyBytes1[] =
"\x69\xea\xa8\x02\xa6\x76\x3a\xf9\x79\xe8\xd1\x94\x0f\xb8\x83\x92";
const char kKeyId1[] =
"\xab\xba\x27\x1e\x8b\xcf\x55\x2b\xbd\x2e\x86\xa4\x34\xa9\xa5\xd9";
const char kKeyBytes2[] =
"\xa4\x63\x1a\x15\x3a\x44\x3d\xf9\xee\xd0\x59\x30\x43\xdb\x75\x19";
const char kKeyId2[] =
"\xf3\xc5\xe0\x36\x1e\x66\x54\xb2\x8f\x80\x49\xc7\x78\xb2\x39\x46";
const char kPreProvisioningKey[] = "f7538b38acc78ec68c732ac665c55c65";
const char kValidCertList[] =
"{"
"\"kind\": \"certificateprovisioning#certificateStatusListResponse\","
"\"signedList\": \""
"CisIhdC/"
"9gUSIwoNc2VyaWFsLW51bWJlciIQCJICGgRtYWtlIgVtb2RlbDAeEoADMI21pWCKSrmyeUAiCT"
"gkYwrZsXXHKsDQ7J9pkNbZTYj5kqpa/WaXQDWV3KNsEAf0a9FERSFr1V/"
"Vr3oEwOVZ8pA2uHY7KA/ghY7FByzDYA8GQyofi8XV/m27QL/o2iu1YQXkO87l37/"
"RdWY1gHzqiwbiCXJsPqD4+S2g9o5/hwlDOzHToqylUS2rj5iBO/"
"0fkuxKnWbxU0+PCcV2cuUhBv+jqOg8wDAw3/9HJEiUAzH7la/"
"f8nZQMUj1AOBZuKbgdobIPNs9tj3cCRKDIY8DomfceZgJO+"
"DHtxdggeZoBfq8jiFS5NynMx7OcINT3LRPmJ6opTwMhF5uurXRmRDXxJ/E+Z5bTCEbxCZ/"
"6rhr+faGam9tDZHxHuetDPx5l2mfQnF7lY7Te+FChv+"
"xIc93FvqNYF3TCZui3LYoqqPylijzLNZ7dPaVZ7hN/cFPJVudA/"
"eHq6ElmJquoKd5022d6Rin6oFymJUKF7FKRKfIkepkgncT5C/kI3/tX6nilNb6"
"\" }";
/** Example of how to use WvPL API. */
int main(int argc, char** argv) {
// -- Set up WvPLEnvironment.
std::map<std::string, std::string> config;
// Set device certificate expiration time to 10 years (10 * 365 * 24 * 3600).
// Note that in practice, the expiration should not be 10 years long.
// Certificate status list should be updated periodically.
config[kDeviceCertificateExpiration] = "315360000";
// The DCSL and service certificate used in this example are for "development"
// and will not work with Widevine's production servers. Replace "dev" with
// "prod" when using production certificates.
config[kDrmCertificateType] = "dev";
// Set parameters on the environment.
WvPLEnvironment wvpl_env(config);
WvPLStatus status = wvpl_env.Initialize();
if (!status.ok()) {
std::cerr << "Failed to Initialize environment: " << status.error_message()
<< std::endl;
return status.error_code();
}
// The preProvKeys map takes a device systemID as the key and an associated
// preprovisioning key as value.
std::map<uint32_t, std::string> prev_prov_keys;
prev_prov_keys[kSystemId] = kPreProvisioningKey;
wvpl_env.SetPreProvisioningKeys(prev_prov_keys);
std::string b64DrmServiceCertificate =
"CsMCCAMSENU2hMYAMrDf+"
"Z9TlWepj1UY7eXE9gUijgIwggEKAoIBAQCqBNtJ830093pLL7h0daCvCUY7WQ8nrNfyYa5NI"
"TMR8V6JxHUtykxzMP2FR6cgJb0NPXQXg3U6kTqHClUYu2OOHjoSi68LtbTL4S+7d/"
"b1NqOhLLLQqiRXUOfZE4BrSJx0W+bUrzLOFgl+xmMv5jlNpdFLhn1+/"
"QOs0u2dxen+yCDoXIOVbFtQtA3RB+sRxvjaOwW4u5dqMj2Xzd/"
"JjWiAwgDAMe3g256M2s+5ZzZ1CnZv6KWPwKzQKXzO/O/"
"bSGrMz9K9T1kRVPPnq8FAiXh2IY+"
"JZ5EcPwrbcdEmiMWxf7bWv3Pk9vSSC8ZDUUI4oBdocf6TCEFKFq1uZBXP0VGbAgMBAAE6Emh"
"hbWlkLXd2cGwtc2RrLXVhdEABSAESgAO0xjYXyZAJ7UNU0N5duOxRkSjlZW7DcqYE5DH8T9n"
"QzCZ/PHPraiWcOaHxyG1sR6Vn6zW7EFV4izXWPI0BTI6Kys/3fQmbstX71om2h8lm3+zOh/"
"baLl605RaIpTCHdQj5hfoxs/nn6CI+VxxxD8L1JiGXbcg0/"
"noSHMJorOq+sDJDK6aYqZDEtrJaY6DfZa+IAK7Nn/"
"WDWVpefrVJwP2h2chjKpYSbOVxRnpqWqWVxT30ROyJwF1BA9F/"
"dMeqTyeyzk1fMfVRQuHaaFqYz6HazSg7dqoXGEKTonN9kHWXTzdPY5jhdXO3vOu4U1k9LzSM"
"4Gnb/"
"8K2F2vQQECskoGkGTF85NB2vyXA95FJa5ExwCXrTnCuBZOLOsR6wwazn5H0gDAmqbTxm2I0r"
"7Ar7IrU8z9v83k1171RDk9xHO+yJD6eFQSyk9stqQH/dNU1DPjCaMylk8ysQ/"
"8XYRj9O0LBEsT2oC4Y7lFd17/NqJeOGvOwVgUmQ/lIVNMmZajEqz0=";
std::string b64PrivateKey =
"MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQI4UNtEZV1vrkCAggAMBQGCCqG"
"SIb3DQMHBAhSpl2tcdfcVwSCBMi7K5Ne/XsAB49mm0gIQ1BCTiIfdzuS/OO0Z3xoUry9/"
"Z50vE6aAkqLbvP7XbWltDU+jzjkjoAnkEUJ56Njifd7EhTSN7M6W6x2mC7c+"
"qgdVW7XqR8iKIpULYaU9txNadwl1p1iP0MoKN0cORXKHzhgUPaTkUKiseCMVdCsImPrDlBrm"
"s+jeyft8YwPpUlCxFLTQo+F9HmxKBKxYcEs1g9EByrXCrIx1oHAJBgCihFOi+"
"qFC2HQJex54tx1QEf/"
"bb5G7xW3vD4T3fZU9by9GNKq9MOVsARmy7ozvDeark6f+"
"MxCuUhVvRqRK4EC3YiaVj9GB6kqWA1UjldF+"
"HFHsqs6qV2J6HNs42JL7N9PQTJJx6Xdxp7f7PjgsJUslZeiyW+uZ8wIpMsBg4NtOkcbgb+"
"ijGiPGYCPMv7mVFWqyMeSM+BQN3NF48P/"
"VHfHQ9CmX41uE8+"
"IhXdi1b4fjenUIKcJ95yoB1yauZGBRBdvjjrZtduczYnoJd9BYCbEYErRC5cvNU+"
"FzYEUYKqUBLc25RWd8hg/"
"NGiaC7YwCJjI59o7SGgY1BFuPuEaehJ60OvbgJiyBofAGOMulrF+"
"Quf6QFgltUFnOIpcXhll9/"
"C+"
"RWW6uLXfxZICt3F6Z4dq9xZi5ZSUzZVsdLtbYAP4QLXTqGTgPA7FdFvHrhf3GJTIppE2a58x"
"uEkVkXl2cqYSuat4QGRxNrleJ55gWvZKIzIN2j85Jk2E9UljNURso/9FIsEF/"
"S2Js77nvn5t40VNdzhUvSjDkD8S75hlyHMTFbc/"
"9yOlrUWT2AP+veMnzgR5wltQ32qgkujBj7ogHz+0/"
"RvpBKyvmHI+mvmqm6aS22yEjQ6jhyEN8mKZNPpgz94TFzYwP9s/qV+/"
"AllJNQ0ruMue9gUKYu381u/"
"F0eLG4ZORinxnxMtWQhpB7cR71nBmt2SYicg2moqKc9hQ7JRJ7g0V07AjWByRRHaOwgJ/"
"YBsCaVeyMl8ynKpWB9r8AW9qKaf79JC+brcWrel8S+YKHX8akmTrs0gSzI+M0hBGqp3f/"
"xnRiAuBXLGZ8Vm2YqsJpFYGyFKBoPUinCuoDLWJnbWt3GBAvHp8OWry67Ldz6vcQmh0eEWmi"
"qIzHGGw2eti2BNpJwATXZ7CfETB9jlHfk4vPZlTWzuzBaQtExLNGXJbgXDJeaTxVdZ7JU2ul"
"S4VgmBAUZnXXJYBgNc1qCCnijFX/R06hN3LrG/"
"d5odxQCfhIMuSieaTrWuoqBYxxi5dLzarRRp9DySURt9JepyOps9sPBfvLh5iDy2LQ2dWbmS"
"A/"
"rqsaV+"
"8z4v4Y1gDJCmKFMa2GPNSlJDqjASw8tDDMXL4dwYb2RcWLCHM5CXJAnxSE4ZpHvf0dwAyDqg"
"hikih9D7CvOwGdv7oVW6/"
"tTdoOo+WkrQtgmRfboxX9Spin+vL1ia2jW2VugSMQbEhTkKH+"
"NE0TC6VGsvPJt0wLLhzRMzTEeT05Zl4QpeCKUmHFM4LIXWcACQ8L1Q0YjOXg733q/"
"fs2kmXsHV9TG5kqHemGIf7t0baF7ibzwnl7F+GGWmg/"
"dQ1RdM7T0p5wRtqHK0wAKxG8hxZkejHPBnV9GYK8QUOlxnE7tVM2Sv6QmTOKGXGKDRBsoacm"
"BaGLiE=";
std::string b64DecodedDrmServiceCertificate;
std::string b64DecodedPrivateKey;
if (!absl::Base64Unescape(b64DrmServiceCertificate,
&b64DecodedDrmServiceCertificate)) {
std::cerr << "Failed to decode ServiceCertificate." << std::endl;
return 1;
}
if (!absl::Base64Unescape(b64PrivateKey, &b64DecodedPrivateKey)) {
std::cerr << "Failed to decode PrivateKey." << std::endl;
return 1;
}
// Must set a valid drm service certificate, otherwise it will failed to
// create session.
std::string passphrase = "encryptallthekitties";
status = wvpl_env.SetDrmServiceCertificate(b64DecodedDrmServiceCertificate,
b64DecodedPrivateKey, passphrase);
if (!status.ok()) {
std::cerr << "Failed to set DrmServiceCertificate: "
<< status.error_message() << std::endl;
return status.error_code();
}
// Set a valid device certificate status list.
status = wvpl_env.SetDeviceCertificateStatusList(kValidCertList);
if (!status.ok()) {
std::cerr << "Failed to update the certificate status list: "
<< status.error_message() << std::endl;
return status.error_code();
}
// -- Create the DRM session.
WvPLSession* session = nullptr;
// Set the license request.
const char kLicenseRequestExample[] =
"\x12\x90\x01\x0a\x58\x08\x00\x12\x48\x00\x00\x00\x02\x00\x00\x01\x12\x8e"
"\x1e\xbf\xe0\x37\x82\x80\x96\xca\x65\x38\xb4\xf6\xf4\xbc\xb5\x1c\x2b\x71"
"\x91\xcf\x03\x7e\x98\xbe\xaa\x24\x92\x49\x07\xe1\x28\xf9\xff\x49\xb5\x4a"
"\x16\x5c\xd9\xc3\x3e\x65\x47\x53\x7e\xb4\xd2\x9f\xb7\xe8\xdf\x3c\x2c\x1c"
"\xd9\x25\x17\xa1\x2f\x49\x22\x95\x3e\x1a\x0a\x0a\x03\x66\x6f\x6f\x12\x03"
"\x62\x61\x72\x12\x2c\x12\x2a\x0a\x10\x30\x31\x32\x33\x34\x35\x36\x37\x38"
"\x39\x41\x42\x43\x44\x45\x46\x10\x01\x1a\x14\x6d\x79\x43\x6f\x6f\x6c\x52"
"\x65\x71\x75\x65\x73\x74\x2c\x20\x44\x75\x64\x65\x21\x18\x01\x20\x9d\x88"
"\xf7\x8a\x05\x1a\x20\xac\xc0\x12\xac\xcd\x1a\xac\x9f\x11\xaa\x84\xbf\xd1"
"\x6f\x01\x44\x8a\xd5\x00\xb8\x60\x2e\x26\x9d\xd2\x71\x7e\x0b\x26\x55\x31"
"\x83";
status =
wvpl_env.CreateSession(std::string(std::begin(kLicenseRequestExample),
std::end(kLicenseRequestExample) - 1),
&session);
if (!status.ok()) {
std::cerr << "Failed to create session: " << status.error_message()
<< std::endl;
return status.error_code();
}
// -- Inspect the request.
// Set parameters on the created session.
// Set policy parameters
WvPLPlaybackPolicy policy;
policy.set_license_duration_seconds(604800);
policy.set_playback_duration_seconds(86400);
session->set_policy(policy);
// Set key parameters
WvPLKey wvpl_key1;
// Key bytes and key id must be in bytes.
wvpl_key1.set_key_id(kKeyId1);
wvpl_key1.set_key_bytes(kKeyBytes1);
wvpl_key1.set_track_type(VIDEO_HD);
wvpl_key1.mutable_output_protection()->set_hdcp(HDCP_V1);
wvpl_key1.mutable_output_protection()->set_secure_data_path(true);
status = session->AddKey(wvpl_key1);
if (!status.ok()) {
std::cerr << "Failed to add key: " << status.error_message() << std::endl;
return status.error_code();
}
WvPLKey wvpl_key2;
wvpl_key2.set_key_id(kKeyId2);
wvpl_key2.set_key_bytes(kKeyBytes2);
wvpl_key2.set_track_type(VIDEO_SD);
wvpl_key2.mutable_requested_output_protection()->set_hdcp(HDCP_V2);
wvpl_key2.mutable_requested_output_protection()->set_secure_data_path(false);
status = session->AddKey(wvpl_key2);
if (!status.ok()) {
std::cerr << "Failed to add key: " << status.error_message() << std::endl;
return status.error_code();
}
// -- Generate the license
std::string license_response;
status = session->GenerateLicense(&license_response);
if (!status.ok()) {
std::cerr << "Failed to generate license: " << status.error_message()
<< std::endl;
return status.error_code();
}
std::cout << "Generated license. " << absl::BytesToHexString(license_response)
<< std::endl;
return 0;
}

View File

@@ -0,0 +1,383 @@
// Copyright 2021 Google LLC. All rights reserved.
#include <algorithm>
#include <cstdint>
#include <iostream>
#include <map>
#include <string>
#include "absl/strings/escaping.h"
#include "sdk/external/cpp/wvpl/license_server_sdk/wvpl_environment.h"
#include "sdk/external/cpp/wvpl/license_server_sdk/wvpl_license_counter.h"
#include "sdk/external/cpp/wvpl/license_server_sdk/wvpl_session.h"
using video_widevine::kDefaultProfileOwnerName;
using video_widevine_server::wv_pl_sdk::HDCP_NONE;
using video_widevine_server::wv_pl_sdk::HDCP_V1;
using video_widevine_server::wv_pl_sdk::kDeviceCertificateExpiration;
using video_widevine_server::wv_pl_sdk::kDrmCertificateType;
using video_widevine_server::wv_pl_sdk::VIDEO_SD;
using video_widevine_server::wv_pl_sdk::WvPLEnvironment;
using video_widevine_server::wv_pl_sdk::WvPLKey;
using video_widevine_server::wv_pl_sdk::WvPLPlaybackPolicy;
using video_widevine_server::wv_pl_sdk::WvPLSession;
using video_widevine_server::wv_pl_sdk::WvPLStatus;
const uint32_t kSystemId = 0x112;
const char kKeyBytes1[] =
"\x69\xea\xa8\x02\xa6\x76\x3a\xf9\x79\xe8\xd1\x94\x0f\xb8\x83\x92";
const char kKeyId1[] =
"\xab\xba\x27\x1e\x8b\xcf\x55\x2b\xbd\x2e\x86\xa4\x34\xa9\xa5\xd9";
const char kKeyBytes2[] =
"\xa4\x63\x1a\x15\x3a\x44\x3d\xf9\xee\xd0\x59\x30\x43\xdb\x75\x19";
const char kKeyId2[] =
"\xf3\xc5\xe0\x36\x1e\x66\x54\xb2\x8f\x80\x49\xc7\x78\xb2\x39\x46";
const char kPreProvisioningKey[] = "f7538b38acc78ec68c732ac665c55c65";
const char kValidCertList[] =
"{"
"\"kind\": \"certificateprovisioning#certificateStatusListResponse\","
"\"signedList\": \""
"CisIhdC/"
"9gUSIwoNc2VyaWFsLW51bWJlciIQCJICGgRtYWtlIgVtb2RlbDAeEoADMI21pWCKSrmyeUAiCT"
"gkYwrZsXXHKsDQ7J9pkNbZTYj5kqpa/WaXQDWV3KNsEAf0a9FERSFr1V/"
"Vr3oEwOVZ8pA2uHY7KA/ghY7FByzDYA8GQyofi8XV/m27QL/o2iu1YQXkO87l37/"
"RdWY1gHzqiwbiCXJsPqD4+S2g9o5/hwlDOzHToqylUS2rj5iBO/"
"0fkuxKnWbxU0+PCcV2cuUhBv+jqOg8wDAw3/9HJEiUAzH7la/"
"f8nZQMUj1AOBZuKbgdobIPNs9tj3cCRKDIY8DomfceZgJO+"
"DHtxdggeZoBfq8jiFS5NynMx7OcINT3LRPmJ6opTwMhF5uurXRmRDXxJ/E+Z5bTCEbxCZ/"
"6rhr+faGam9tDZHxHuetDPx5l2mfQnF7lY7Te+FChv+"
"xIc93FvqNYF3TCZui3LYoqqPylijzLNZ7dPaVZ7hN/cFPJVudA/"
"eHq6ElmJquoKd5022d6Rin6oFymJUKF7FKRKfIkepkgncT5C/kI3/tX6nilNb6"
"\" }";
const char b64DrmServiceCertificate[] =
"CsMCCAMSENU2hMYAMrDf+"
"Z9TlWepj1UY7eXE9gUijgIwggEKAoIBAQCqBNtJ830093pLL7h0daCvCUY7WQ8nrNfyYa5NI"
"TMR8V6JxHUtykxzMP2FR6cgJb0NPXQXg3U6kTqHClUYu2OOHjoSi68LtbTL4S+7d/"
"b1NqOhLLLQqiRXUOfZE4BrSJx0W+bUrzLOFgl+xmMv5jlNpdFLhn1+/"
"QOs0u2dxen+yCDoXIOVbFtQtA3RB+sRxvjaOwW4u5dqMj2Xzd/"
"JjWiAwgDAMe3g256M2s+5ZzZ1CnZv6KWPwKzQKXzO/O/"
"bSGrMz9K9T1kRVPPnq8FAiXh2IY+"
"JZ5EcPwrbcdEmiMWxf7bWv3Pk9vSSC8ZDUUI4oBdocf6TCEFKFq1uZBXP0VGbAgMBAAE6Emh"
"hbWlkLXd2cGwtc2RrLXVhdEABSAESgAO0xjYXyZAJ7UNU0N5duOxRkSjlZW7DcqYE5DH8T9n"
"QzCZ/PHPraiWcOaHxyG1sR6Vn6zW7EFV4izXWPI0BTI6Kys/3fQmbstX71om2h8lm3+zOh/"
"baLl605RaIpTCHdQj5hfoxs/nn6CI+VxxxD8L1JiGXbcg0/"
"noSHMJorOq+sDJDK6aYqZDEtrJaY6DfZa+IAK7Nn/"
"WDWVpefrVJwP2h2chjKpYSbOVxRnpqWqWVxT30ROyJwF1BA9F/"
"dMeqTyeyzk1fMfVRQuHaaFqYz6HazSg7dqoXGEKTonN9kHWXTzdPY5jhdXO3vOu4U1k9LzSM"
"4Gnb/"
"8K2F2vQQECskoGkGTF85NB2vyXA95FJa5ExwCXrTnCuBZOLOsR6wwazn5H0gDAmqbTxm2I0r"
"7Ar7IrU8z9v83k1171RDk9xHO+yJD6eFQSyk9stqQH/dNU1DPjCaMylk8ysQ/"
"8XYRj9O0LBEsT2oC4Y7lFd17/NqJeOGvOwVgUmQ/lIVNMmZajEqz0=";
const char b64PrivateKey[] =
"MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQI4UNtEZV1vrkCAggAMBQGCCqG"
"SIb3DQMHBAhSpl2tcdfcVwSCBMi7K5Ne/XsAB49mm0gIQ1BCTiIfdzuS/OO0Z3xoUry9/"
"Z50vE6aAkqLbvP7XbWltDU+jzjkjoAnkEUJ56Njifd7EhTSN7M6W6x2mC7c+"
"qgdVW7XqR8iKIpULYaU9txNadwl1p1iP0MoKN0cORXKHzhgUPaTkUKiseCMVdCsImPrDlBrm"
"s+jeyft8YwPpUlCxFLTQo+F9HmxKBKxYcEs1g9EByrXCrIx1oHAJBgCihFOi+"
"qFC2HQJex54tx1QEf/"
"bb5G7xW3vD4T3fZU9by9GNKq9MOVsARmy7ozvDeark6f+"
"MxCuUhVvRqRK4EC3YiaVj9GB6kqWA1UjldF+"
"HFHsqs6qV2J6HNs42JL7N9PQTJJx6Xdxp7f7PjgsJUslZeiyW+uZ8wIpMsBg4NtOkcbgb+"
"ijGiPGYCPMv7mVFWqyMeSM+BQN3NF48P/"
"VHfHQ9CmX41uE8+"
"IhXdi1b4fjenUIKcJ95yoB1yauZGBRBdvjjrZtduczYnoJd9BYCbEYErRC5cvNU+"
"FzYEUYKqUBLc25RWd8hg/"
"NGiaC7YwCJjI59o7SGgY1BFuPuEaehJ60OvbgJiyBofAGOMulrF+"
"Quf6QFgltUFnOIpcXhll9/"
"C+"
"RWW6uLXfxZICt3F6Z4dq9xZi5ZSUzZVsdLtbYAP4QLXTqGTgPA7FdFvHrhf3GJTIppE2a58x"
"uEkVkXl2cqYSuat4QGRxNrleJ55gWvZKIzIN2j85Jk2E9UljNURso/9FIsEF/"
"S2Js77nvn5t40VNdzhUvSjDkD8S75hlyHMTFbc/"
"9yOlrUWT2AP+veMnzgR5wltQ32qgkujBj7ogHz+0/"
"RvpBKyvmHI+mvmqm6aS22yEjQ6jhyEN8mKZNPpgz94TFzYwP9s/qV+/"
"AllJNQ0ruMue9gUKYu381u/"
"F0eLG4ZORinxnxMtWQhpB7cR71nBmt2SYicg2moqKc9hQ7JRJ7g0V07AjWByRRHaOwgJ/"
"YBsCaVeyMl8ynKpWB9r8AW9qKaf79JC+brcWrel8S+YKHX8akmTrs0gSzI+M0hBGqp3f/"
"xnRiAuBXLGZ8Vm2YqsJpFYGyFKBoPUinCuoDLWJnbWt3GBAvHp8OWry67Ldz6vcQmh0eEWmi"
"qIzHGGw2eti2BNpJwATXZ7CfETB9jlHfk4vPZlTWzuzBaQtExLNGXJbgXDJeaTxVdZ7JU2ul"
"S4VgmBAUZnXXJYBgNc1qCCnijFX/R06hN3LrG/"
"d5odxQCfhIMuSieaTrWuoqBYxxi5dLzarRRp9DySURt9JepyOps9sPBfvLh5iDy2LQ2dWbmS"
"A/"
"rqsaV+"
"8z4v4Y1gDJCmKFMa2GPNSlJDqjASw8tDDMXL4dwYb2RcWLCHM5CXJAnxSE4ZpHvf0dwAyDqg"
"hikih9D7CvOwGdv7oVW6/"
"tTdoOo+WkrQtgmRfboxX9Spin+vL1ia2jW2VugSMQbEhTkKH+"
"NE0TC6VGsvPJt0wLLhzRMzTEeT05Zl4QpeCKUmHFM4LIXWcACQ8L1Q0YjOXg733q/"
"fs2kmXsHV9TG5kqHemGIf7t0baF7ibzwnl7F+GGWmg/"
"dQ1RdM7T0p5wRtqHK0wAKxG8hxZkejHPBnV9GYK8QUOlxnE7tVM2Sv6QmTOKGXGKDRBsoacm"
"BaGLiE=";
const char kLicenseRequestExample[] =
"\x12\x90\x01\x0a\x58\x08\x00\x12\x48\x00\x00\x00\x02\x00\x00\x01\x12\x8e"
"\x1e\xbf\xe0\x37\x82\x80\x96\xca\x65\x38\xb4\xf6\xf4\xbc\xb5\x1c\x2b\x71"
"\x91\xcf\x03\x7e\x98\xbe\xaa\x24\x92\x49\x07\xe1\x28\xf9\xff\x49\xb5\x4a"
"\x16\x5c\xd9\xc3\x3e\x65\x47\x53\x7e\xb4\xd2\x9f\xb7\xe8\xdf\x3c\x2c\x1c"
"\xd9\x25\x17\xa1\x2f\x49\x22\x95\x3e\x1a\x0a\x0a\x03\x66\x6f\x6f\x12\x03"
"\x62\x61\x72\x12\x2c\x12\x2a\x0a\x10\x30\x31\x32\x33\x34\x35\x36\x37\x38"
"\x39\x41\x42\x43\x44\x45\x46\x10\x01\x1a\x14\x6d\x79\x43\x6f\x6f\x6c\x52"
"\x65\x71\x75\x65\x73\x74\x2c\x20\x44\x75\x64\x65\x21\x18\x01\x20\x9d\x88"
"\xf7\x8a\x05\x1a\x20\xac\xc0\x12\xac\xcd\x1a\xac\x9f\x11\xaa\x84\xbf\xd1"
"\x6f\x01\x44\x8a\xd5\x00\xb8\x60\x2e\x26\x9d\xd2\x71\x7e\x0b\x26\x55\x31"
"\x83";
const char b64SerializedSignedDeviceSecurityProfiles[] =
"Cm0I45_P_"
"AUSLwoIZHNwX3Rlc3QQAhoECAAQACIGCAAQAyACKg13aWRldmluZV90ZXN0OgQIABAAEjQKC"
"GxpdmVkZW1vEAIaAggEIgIQASoNd2lkZXZpbmVfdGVzdDIFCNcIEAI6CAiDwrX6BRAAEoADb"
"oYJI5ouzml8W63DmiTBA7thzSl5OaKbyc1dAKGpHrMw-lTuUyrzULPm5nb_"
"st4YJk2w3F0k4Ly0lsRY_QweMSIechtcII5jO_9N2scy0y5V-lBRH_"
"7AtZFdfwD2wrseVCCarSyWDZN84-"
"6Wu3ekKbNO8vehiP8Tx2VWmkcIypJ09nPuTDxPT5CutLkgzlIS2q9HjwE5IK4nfC-"
"rx7seGgMxouyEb-QsTBtJ-Lx9O8D1GFMvOtHmfG-KfoD1L-"
"9lUT6j8W3TfyrUesnIDQy2bCCQzsbsNg60M9B-YZNvl6DX_-"
"PqE1CT2Lal7rp5TsZluOtwEj8TMBzyNl5kC5MAXZ3ETEFDag_9qEZrMJMIEsw194_lo_"
"o2ivjkiPNwCC00rJ91RI9gc2ctDlXUqtbqfz5-lx_v04VsI8AEYfuXmO8OBhFZwz4__"
"ZVwpI8fOtedm1KI46uCWZUwIi3yigYNJvgxoynzxVF8uMLTJi3iqiG5BAtrRImEt1iRVD8Ow"
"h4-GAI=";
int GenerateLicenseUsingProfiles(
WvPLSession& session, const std::string& content_owner,
const std::string& content_id,
const std::vector<std::string>& qualified_profile_names,
std::string* license_response) {
// Set parameters on the created session.
// Set policy parameters.
WvPLPlaybackPolicy policy;
policy.set_license_duration_seconds(604800);
policy.set_playback_duration_seconds(86400);
session.set_policy(policy);
// Set key parameters.
// Content keys are generated based on the info from qualifying profiles.
// The qualified profiles can be used to determine the content keys allowed
// in the license.
// In this example case, qualified custom profiles for owner_name
// "widevine_test" is non-empty, so the following keys are generated based on
// qualified custom profiles. Those keys would support SD content.
WvPLKey wvpl_key1;
// Key bytes and key id must be in bytes.
wvpl_key1.set_key_id(kKeyId1);
wvpl_key1.set_key_bytes(kKeyBytes1);
wvpl_key1.set_track_type(VIDEO_SD);
wvpl_key1.mutable_output_protection()->set_hdcp(HDCP_NONE);
wvpl_key1.mutable_output_protection()->set_secure_data_path(true);
WvPLStatus status = session.AddKey(wvpl_key1);
if (!status.ok()) {
std::cerr << "Failed to add key: " << status.error_message() << std::endl;
return status.error_code();
}
WvPLKey wvpl_key2;
wvpl_key2.set_key_id(kKeyId2);
wvpl_key2.set_key_bytes(kKeyBytes2);
wvpl_key2.set_track_type(VIDEO_SD);
wvpl_key2.mutable_requested_output_protection()->set_hdcp(HDCP_V1);
wvpl_key2.mutable_requested_output_protection()->set_secure_data_path(false);
status = session.AddKey(wvpl_key2);
if (!status.ok()) {
std::cerr << "Failed to add key: " << status.error_message() << std::endl;
return status.error_code();
}
// Generate the license
status = session.GenerateLicense(license_response);
if (!status.ok()) {
std::cerr << "Failed to generate license: " << status.error_message()
<< std::endl;
return status.error_code();
}
std::cout << "Generated license. "
<< absl::BytesToHexString(*license_response) << std::endl;
return 0;
}
int GenerateLicense(WvPLSession& session, const std::string& content_owner,
const std::vector<std::string>& profile_owners,
std::string* license_response) {
std::vector<std::string> qualified_profile_names;
WvPLStatus status;
std::string content_id;
status = session.GetContentId(&content_id);
// Check if the input content owner has qualified custom profiles. If it
// exists, those profiles will be used to decide what quality to play. If it
// doesn't exist, will use default profiles instead.
if (std::find(profile_owners.begin(), profile_owners.end(), content_owner) !=
profile_owners.end()) {
status = session.GetQualifiedCustomDeviceSecurityProfiles(
content_owner, &qualified_profile_names);
if (!status.ok()) {
std::cerr << "Failed to get qualifying custom profiles: "
<< status.error_message() << " for owner: <" << content_owner
<< ">" << std::endl;
return status.error_code();
} else {
if (!qualified_profile_names.empty()) {
std::cout << "DRM qualifying custom profiles for owner <"
<< content_owner << ">: ";
for (const auto& profile_name : qualified_profile_names) {
std::cout << "<" << profile_name << "> ";
}
std::cout << std::endl;
// Generate license using custom profiles.
return GenerateLicenseUsingProfiles(session, content_owner, content_id,
qualified_profile_names,
license_response);
} else {
// Print out error.
std::cerr << "There is no qualified custom DSP for the current device. "
"Reject the license request."
<< std::endl;
return 1;
}
}
} else {
status = session.GetQualifiedDefaultDeviceSecurityProfiles(
&qualified_profile_names);
if (!status.ok()) {
std::cerr << "Failed to get qualifying default profiles: "
<< status.error_message() << std::endl;
return status.error_code();
} else {
if (!qualified_profile_names.empty()) {
std::cout << "DRM qualifying default profiles: " << std::endl;
for (const auto& profile_name : qualified_profile_names) {
std::cout << "<" << profile_name << "> ";
}
std::cout << std::endl;
// Generate license using default profiles.
return GenerateLicenseUsingProfiles(
session, video_widevine::kDefaultProfileOwnerName, content_id,
qualified_profile_names, license_response);
} else {
// Print out error.
std::cerr << "There is no qualified default DSP for the current "
"device. Reject the license request."
<< std::endl;
return 1;
}
}
}
}
/** This example is used when content providers want to use DSP API for license
* generation. The qualified profiles can be used to determine the content keys
* allowed in the license.
*
* If content provider doesn't want to use DSP for license generation, they
* could follow the example shown in "wvpl_license_sdk_example.cc".
*/
int main(int argc, char** argv) {
// Set up WvPLEnvironment.
std::map<std::string, std::string> config;
// Set device certificate expiration time to 10 years (10 * 365 * 24 * 3600).
// Note that in practice, the expiration should not be 10 years long.
// Certificate status list should be updated periodically.
config[kDeviceCertificateExpiration] = "315360000";
// The DCSL and service certificate used in this example are for "development"
// and will not work with Widevine's production servers. Replace "dev" with
// "prod" when using production certificates.
config[kDrmCertificateType] = "dev";
// Set parameters on the environment.
WvPLEnvironment wvpl_env(config);
WvPLStatus status = wvpl_env.Initialize();
if (!status.ok()) {
std::cerr << "Failed to Initialize environment: " << status.error_message()
<< std::endl;
return status.error_code();
}
// The preProvKeys map takes a device systemID as the key and an associated
// preprovisioning key as value.
std::map<uint32_t, std::string> prev_prov_keys;
prev_prov_keys[kSystemId] = kPreProvisioningKey;
wvpl_env.SetPreProvisioningKeys(prev_prov_keys);
std::string b64DecodedDrmServiceCertificate;
std::string b64DecodedPrivateKey;
if (!absl::Base64Unescape(b64DrmServiceCertificate,
&b64DecodedDrmServiceCertificate)) {
std::cerr << "Failed to decode ServiceCertificate." << std::endl;
return 1;
}
if (!absl::Base64Unescape(b64PrivateKey, &b64DecodedPrivateKey)) {
std::cerr << "Failed to decode PrivateKey." << std::endl;
return 1;
}
// Must set a valid drm service certificate, otherwise it will failed to
// create session.
std::string passphrase = "encryptallthekitties";
status = wvpl_env.SetDrmServiceCertificate(b64DecodedDrmServiceCertificate,
b64DecodedPrivateKey, passphrase);
if (!status.ok()) {
std::cerr << "Failed to set DrmServiceCertificate: "
<< status.error_message() << std::endl;
return status.error_code();
}
// Set a valid device certificate status list.
status = wvpl_env.SetDeviceCertificateStatusList(kValidCertList);
if (!status.ok()) {
std::cerr << "Failed to update the certificate status list: "
<< status.error_message() << std::endl;
return status.error_code();
}
// Set custom device security profile list.
// One time setup each time new DSPs are loaded.
std::string serialized_signed_dsps;
if (!absl::WebSafeBase64Unescape(b64SerializedSignedDeviceSecurityProfiles,
&serialized_signed_dsps)) {
std::cerr << "Failed to decode serialized_signed_dsps." << std::endl;
return 1;
}
status = wvpl_env.SetCustomDeviceSecurityProfiles(serialized_signed_dsps);
if (!status.ok()) {
std::cerr << "Failed to set custom device security profiles: "
<< status.error_message()
<< ". wvpl_env will continue to use the previously loaded DSPs "
"if there is any."
<< std::endl;
return status.error_code();
}
// Optional sannity check.
// Verify the custom profile owners match with list of expected content
// owners.
std::vector<std::string> custom_profile_owners;
status =
wvpl_env.GetCustomDeviceSecurityProfileOwners(&custom_profile_owners);
if (!status.ok() || custom_profile_owners.empty()) {
std::cerr
<< "Failed to get owner list for custom device security profiles: "
<< status.error_message() << std::endl;
return status.error_code();
}
std::cout << "DRM custom profile owner list: ";
for (const auto& owner_name : custom_profile_owners) {
std::cout << "<" << owner_name << "> ";
}
std::cout << std::endl;
// Create the DRM session.
WvPLSession* session = nullptr;
// Set the license request.
status =
wvpl_env.CreateSession(std::string(std::begin(kLicenseRequestExample),
std::end(kLicenseRequestExample) - 1),
&session);
if (!status.ok()) {
std::cerr << "Failed to create session: " << status.error_message()
<< std::endl;
return status.error_code();
}
// Generate the license for |content_owner| after successfully creating a
// WvPL session. Assume license is generated for content_owner =
// "widevine_test".
std::string content_owner = "widevine_test";
std::string license_response;
return GenerateLicense(*session, content_owner, custom_profile_owners,
&license_response);
}

View File

@@ -0,0 +1,457 @@
// Copyright 2018 Google LLC. All rights reserved.
package com.google.video.widevine.sdk.wvpl;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.video.widevine.protos.LicenseProtocol.License;
import com.google.video.widevine.protos.LicenseProtocol.SignedMessage;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Base64;
import java.util.Map;
/** Example of how to use WvPL API. */
public class WvPLExample {
/* Creates WvTestRunner to run wvpl test */
static class WvTestRunner extends Thread {
private final int loops;
private final String threadName;
private final WvPLEnvironment env;
public WvTestRunner(WvPLEnvironment env, ThreadGroup group, String threadName, int loops) {
super(group, threadName);
this.loops = loops;
this.threadName = threadName;
this.env = env;
}
@Override
public void run() {
System.out.println("Thread Started: " + threadName);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
System.out.println("InterruptedException in thread.");
}
String b64LicenseRequest =
"CAES9AwKlQwIARLtCQquAggCEhDEmqLqtzGkARd0kTEo0I1IGJSf2qkFIo4CMIIBCgKCAQEA0NXBS5-DGcEt_vnp"
+ "kWgiYUrrlKjddGnSLPuloU5fNHN-TTtF2HwCbKHxv-71EeYwl4omrd36XZ7N6cRPE78G1BQoJy57z2wgCJZ8_3"
+ "jPsMnQSadynMxDdvVBqQde87T66-KqqBMTBOI3dC_LtX1Ydj04OR3yIKSlmTGGPwW37BfvsmNWGAHOHa0i7kP3"
+ "C1HkUyBixNJLArnNmQSfLi5jf8EyRu6-yJwFRyY1EcXXcDouUgJbWFGey6oK26-HI87rs4bEJyjxk3fr0BJcfh"
+ "QSVm-Awmyp4TU0J_A_PLoatG0yzjvPLT0ymsujLta0ZT6xHLSSeD3JfDyrlyS196GS2QIDAQABKIchEoACUqrZ"
+ "8CzIzCe-j4BI0cqjvQ7C_spcMjwdlO9E5pb-FIcP5fDdOSa8Op6gpkB7HwEv-HC8ITXjdQ3MRl5BcLB8elBDBw"
+ "Tl-nfw4AUqVRwSyPKfpOTU9w7MloiFBD8j2zP0Fbb3gub_G-JCMBYlpi8ObTiE5a3olWIl__trUgj-huLSKkIs"
+ "dH7dtaK55vvHIM7h7UcwomnxjJN1phHxB0c4iyHBAQfGaVSIUz3N7nXqsW__QO7qRIa7XCA-uiXpYF17wwTc4w"
+ "_X_fOKoYJ8mly85Sunm-XvxKZGzzcjyJuOsO_cK_KUiqt-TzeVmiohpmrEDK68ozm6LMFIFO3yBFpNgxq2BQqw"
+ "AggBEhDbPKzP3nYDjtzFHkw5JPr6GMLSg44FIo4CMIIBCgKCAQEAuVISqq1KZKOCyoJdYCxsMcBmzUvOotSUG2"
+ "c3VlJz_7Kq4MY65zff4_8AXtnsT4q4WWiREgiewPWe0n8x8_FI8z3r5cLKzwh_wRUwHlV5Sn2dw4I8VTzdPMH5"
+ "bfmgtCpM1NV5NhzT-YagPth3xrcwCnIP54oCLuKCDxG_rKcmKcHiyMVZnqdYHDNO5b8Tn21EV3JHZsLjpEfo_J"
+ "Og6mv1Z0r-vP6VJQT43KSJns5VSKx-RAHoff2ZTBuDWU2YOqFZpi_oeupPiHT_prwF9bIZiGhbEFNK-2T_t2BG"
+ "SzBpGNkQPb3VLek7WcctcqZTZ-eFQrzboRV0L5O8pho2cjVTswIDAQABKIchMAESgAMB8IopgGaBVAPSQnh6PI"
+ "VhD9nt9H_BgEMsW_pXJ5ij4PMltfdr8y03yF2GHJ_ZTeRYJpL_Mn8Mh5sLCaNF44S3nxSw5dT7bH35ZIFiB-uC"
+ "OH60js6UKadq099NZkPUtLCJAX5U_ggkSq2UMAMeRCyTo7XghUEaf_JyECh6rI5fKAjzIX7MfTLU32yHeiGkjN"
+ "Aj2_SoUnZH91Kb_wbStm6GSQdnw7G2kyKp6I6MJclgQZn7fJVCmRJugC57fDiBVv-sfl3Zp2SYQ9hwRBR9byMn"
+ "Vp5KLedB_v-9nnHniMLgSiDK-HASpZA-5C0OO27jHcdvdMgVjXoyJY05YYW1BcKKnGHit82fMY5zEDVG5mfmgq"
+ "d8ZYVlLAxJLWHZ7XCs1pIcAG1LjEy5rKzQtlT2DdwBzsF5mdybWKCLr-fbEqFo_ftp9HIc6EL70pZ07ofHbl0e"
+ "-PCkn5mJJYPbTNv7UxZgUU_2IqUJTxtwZD2GannSnwAPjd6D7u0W6CwFk-rhBe4aFAoMY29tcGFueV9uYW1lEg"
+ "Rhc3VzGhUKCm1vZGVsX25hbWUSB05leHVzIDcaIAoRYXJjaGl0ZWN0dXJlX25hbWUSC2FybWVhYmktdjdhGhIK"
+ "C2RldmljZV9uYW1lEgNkZWIaFgoMcHJvZHVjdF9uYW1lEgZyYXpvcmcaRQoKYnVpbGRfaW5mbxI3Z29vZ2xlL3"
+ "Jhem9yZy9kZWI6TU5DL01BU1RFUi8xODU1NjM3OnVzZXJkZWJ1Zy9kZXYta2V5cxotCglkZXZpY2VfaWQSIEpR"
+ "NVpJWkVMVUpBQlBVQzNLMkg1Q0NXNklVAAAAAAAAGiYKFHdpZGV2aW5lX2NkbV92ZXJzaW9uEg52My4wLjAtYW"
+ "5kcm9pZDIIEAEgBCgJMAASSgpICiIIARoNd2lkZXZpbmVfdGVzdCIPc3RyZWFtaW5nX2NsaXAxEAEaIDE3NEQ4"
+ "MkUyRjA0QzZGQkEwNjAwMDAwMDAwMDAwMDAwGAEgmZ_aqQUwFTij79WWBRqAAsguK-RkA18-zOzwX38JDpT877"
+ "woGiL-kUhQ9w2NuDxolgFovFqp-VNEESpG9GG-dp8sFXAABy4Xk3YJnyVvAtI6MtiSPjDLt87QPSie6WCLgqvL"
+ "zHoGSGuo4ij4HBX23QM0jZNDz_u_1rdZLKfV-nIWQ1sVI2u-QYnTYCztRmpMd1Gp1mZzLpcOL9PNGIYbuJSmQe"
+ "fi3FDGdwfmKLRYg8peJXj2OPHKNyyJCfRMZ42PmBvg4bxI36Ets_TU_6m2RMKARnTLYoGq0bw0fl9GeV_27mDU"
+ "Kvl0SIWXh0Jup8xxCO_iFQzw_7rCyW2FtrzXqq2qEmPRK5HjY-_c_0XVWh0=";
String testSessionId = "TestSessionId";
String testPurchaseId = "TestPurchaseId";
String testProviderClientToken = "TestProviderClientToken";
boolean testOverrideProviderClientToken = true;
byte[] decodedLicenseRequest = Base64.getUrlDecoder().decode(b64LicenseRequest);
byte[] testMasterSigningKeyBytes = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
for (int i = 0; i < loops; i++) {
try {
byte[] bad = {1, 2, 3, 4};
env.createSession(bad);
} catch (WvPLStatusException e) {
System.out.println("Message for bad session: " + e.getMessage());
System.out.println("Status message for bad session: " + e.getStatus().getMessage());
}
WvPLSession session = null;
try {
session = env.createSession(decodedLicenseRequest);
} catch (WvPLStatusException e) {
System.out.println("Message: " + e.getMessage());
System.out.println("Status message: " + e.getStatus().getMessage());
}
byte[] contentId = null;
try {
contentId = session.getContentId();
System.out.println("ContentId:" + new String(contentId, UTF_8));
} catch (WvPLStatusException e) {
System.out.println("Message: " + e.getMessage());
System.out.println("Status message: " + e.getStatus().getMessage());
}
System.out.println("Version: " + WvPLSession.getVersionString());
// VMP Status
try {
System.out.println(
"VMP Status: " + session.verifyPlatform().getPlatformVerificationStatus());
} catch (WvPLStatusException e) {
System.out.println("Message: " + e.getMessage());
System.out.println("Status message: " + e.getStatus().getMessage());
}
// Set parameters on the created session.
WvPLPlaybackPolicy policy = new WvPLPlaybackPolicy();
policy.setLicenseDurationSeconds(10000000L);
session.setPolicy(policy);
WvPLKey key1 = new WvPLKey();
byte[] data1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
key1.setKeyId(data1);
key1.setKeyBytes(data1);
key1.setTrackType(WvPLTrackType.TrackType.AUDIO);
session.addKey(key1);
WvPLKey key2 = new WvPLKey();
byte[] data2 = {10, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
key2.setKeyBytes(data2);
key2.setTrackType(WvPLTrackType.TrackType.VIDEO_SD);
session.addKey(key2);
WvPLKey key3 = new WvPLKey();
byte[] data3 = {10, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
key3.setKeyId(data3);
key3.setKeyBytes(data3);
key3.setTrackType(WvPLTrackType.TrackType.VIDEO_HD);
WvPLOutputProtection outputProtection = new WvPLOutputProtection();
outputProtection.setHdcp(WvPLHdcp.HDCP.HDCP_V2_2);
outputProtection.setSecurityLevel(WvPLSecurityLevel.SecurityLevel.SW_SECURE_DECODE);
outputProtection.setDisableAnalogOutput(true);
outputProtection.setCgms(WvPLCgms.Cgms.COPY_FREE);
System.out.println(
"SecurityLevel for outputProtection = " + outputProtection.getSecurityLevel());
WvPLVideoResolutionConstraint videoResolutionConstraint1 =
new WvPLVideoResolutionConstraint();
videoResolutionConstraint1.setMinResolutionPixels(300);
videoResolutionConstraint1.setMaxResolutionPixels(600);
videoResolutionConstraint1.setHdcp(WvPLHdcp.HDCP.HDCP_V2);
WvPLVideoResolutionConstraint videoResolutionConstraint2 =
new WvPLVideoResolutionConstraint();
videoResolutionConstraint2.setMinResolutionPixels(3000);
videoResolutionConstraint2.setMaxResolutionPixels(6000);
videoResolutionConstraint2.setHdcp(WvPLHdcp.HDCP.HDCP_V1);
key3.addVideoResolutionConstraint(videoResolutionConstraint1);
key3.addVideoResolutionConstraint(videoResolutionConstraint2);
System.out.println(
"Number of video resolutions in key3 = " + key3.getVideoResolutionConstraint().size());
for (WvPLVideoResolutionConstraint vrc : key3.getVideoResolutionConstraint()) {
System.out.println("min resolution in pixels = " + vrc.getMinResolutionPixels()
+ ", max resolution in pixels = " + vrc.getMaxResolutionPixels()
+ ", hdcp = " + vrc.getHdcp());
}
key3.setOutputProtection(outputProtection);
WvPLOutputProtection requestedOutputProtection = new WvPLOutputProtection();
requestedOutputProtection.setHdcp(WvPLHdcp.HDCP.HDCP_V2);
key3.setRequestedOutputProtection(requestedOutputProtection);
session.addKey(key3);
WvPLKey key4 = new WvPLKey();
byte[] data4 = {10, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
key4.setKeyBytes(data4);
key4.setTrackType(WvPLTrackType.TrackType.VIDEO_UHD1);
session.addKey(key4);
System.out.println("Is device ChromeCDM = " + session.isChromeCDM());
WvPLSessionInit sessionInit = new WvPLSessionInit();
sessionInit.setSessionId(testSessionId);
sessionInit.setPurchaseId(testPurchaseId);
sessionInit.setMasterSigningKey(testMasterSigningKeyBytes);
sessionInit.setProviderClientToken(testProviderClientToken);
sessionInit.setOverrideProviderClientToken(testOverrideProviderClientToken);
session.setSessionInit(sessionInit);
try {
byte[] license = session.generateLicense();
System.out.println(
"License Duration Seconds = " + session.getPolicy().getLicenseDurationSeconds());
System.out.print("License Response = ");
if (license.length > 0) {
printSignedMessage(license);
} else {
System.out.println("Empty");
}
} catch (WvPLStatusException e) {
System.out.println("Message: " + e.getMessage());
System.out.println("Status message: " + e.getStatus().getMessage());
}
try {
// GetProvisionedDeviceInfo is deprecated, use getDeviceInfo instead.
WvPLDeviceInfo deviceInfo = session.getDeviceInfo();
System.out.println("DeviceInfo = " + deviceInfo);
WvPLDeviceSecurityLevel.DeviceSecurityLevel deviceSecurityLevel =
deviceInfo.getDeviceSecurityLevel();
// Expect a L1 device for the license request in use.
System.out.println(
"Device Security level = " + deviceSecurityLevel.getDeviceSecurityLevel());
} catch (WvPLStatusException e) {
System.out.println("Failed to get deviceInfo.");
System.out.println("Message: " + e.getMessage());
System.out.println("Status message: " + e.getStatus().getMessage());
}
try {
WvPLClientInfo clientInfo = session.getClientInfo();
WvPLHdcp.HDCP maxHdcpVersion = clientInfo.getMaxHdcpVersion();
System.out.println("max hdcp version = " + maxHdcpVersion.getHDCP()
+ ", oem crypto api version = " + clientInfo.getOemCryptoApiVersion()
+ ", provider client token = " + clientInfo.getProviderClientToken());
Map<String, String> namesValues = clientInfo.getNamesValues();
for (Map.Entry<String, String> nameValue : namesValues.entrySet()) {
System.out.println("Key = " + nameValue.getKey() + ", Value = " + nameValue.getValue());
}
} catch (WvPLStatusException e) {
System.out.println("Failed to get clientInfo from license request.");
System.out.println("Message: " + e.getMessage());
System.out.println("Status message: " + e.getStatus().getMessage());
}
WvPLWidevinePsshData widevinePsshData = null;
try {
widevinePsshData = session.getPsshData();
} catch (WvPLStatusException e) {
System.out.println("Failed to get psshData from license request.");
System.out.println("Message: " + e.getMessage());
System.out.println("Status message: " + e.getStatus().getMessage());
}
if (widevinePsshData.getContentId().length > 0) {
System.out.println(
"ContentId from Widevine Pssh = " + Arrays.toString(widevinePsshData.getContentId()));
} else {
System.out.println("ContentId from Widevine Pssh is empty");
}
if (!widevinePsshData.getKeyIds().isEmpty()) {
for (byte[] keyId : widevinePsshData.getKeyIds()) {
System.out.println("keyId from Widevine Pssh = " + Arrays.toString(keyId));
}
} else {
System.out.println("KeyIds from Widevine Pssh is empty");
}
WvPLStatus errorStatus =
new WvPLStatus(WvPLStatus.StatusCode.SERVICE_CERTIFICATE_REQUEST_MESSAGE, "");
byte[] error = env.generateErrorResponse(errorStatus);
System.out.print("Service Certificate Response = ");
if (error.length > 0) {
printSignedMessage(error);
} else {
System.out.println("Empty");
}
// license counter with flushData set
boolean flushData = true;
byte[] statsBytes = env.getStatsAsBytes(flushData);
System.out.print("stats as bytes with flush set = ");
if (statsBytes.length > 0) {
System.out.println(Arrays.toString(statsBytes));
} else {
System.out.println("Empty");
}
String statsString = env.getStatsAsString(flushData);
System.out.print("stats as std::string with flush set = ");
if (statsString.length() > 0) {
System.out.println(statsString);
} else {
System.out.println("Empty");
}
// license counter with flushData unset
flushData = false;
statsBytes = env.getStatsAsBytes(flushData);
System.out.print("stats as bytes with flush unset = ");
if (statsBytes.length > 0) {
System.out.println(Arrays.toString(statsBytes));
} else {
System.out.println("Empty");
}
statsString = env.getStatsAsString(flushData);
System.out.print("stats as String with flush unset = ");
if (statsString.length() > 0) {
System.out.println(statsString);
} else {
System.out.println("Empty");
}
WvPLSessionInit retrievedSessionInit = session.getSessionInit();
System.out.println(
"retrieved session init fields: sessionId = " + retrievedSessionInit.getSessionId()
+ ", purchaseId = " + retrievedSessionInit.getPurchaseId()
+ ", masterSigningKey = " + Arrays.toString(retrievedSessionInit.getMasterSigningKey())
+ ", providerClientToken = " + retrievedSessionInit.getProviderClientToken()
+ ", overrideProviderClientToken = "
+ retrievedSessionInit.getOverrideProviderClientToken());
try {
policy.close();
key1.close();
key2.close();
outputProtection.close();
key3.close();
session.close();
} catch (IOException e) {
System.out.println("IOException when closing: " + e.getMessage());
}
System.out.println("Thread Name: " + threadName + ", Loop Complete: " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
System.out.println("InterruptedException in thread.");
}
}
System.out.println("Thread Ended: " + threadName);
}
}
public static void printSignedMessage(byte[] bytes) {
try {
SignedMessage licenseMessage = SignedMessage.parseFrom(bytes);
System.out.println(licenseMessage);
if (licenseMessage.getType() == SignedMessage.MessageType.LICENSE
|| licenseMessage.getType() == SignedMessage.MessageType.SERVICE_CERTIFICATE) {
License licenseResponse = License.parseFrom(licenseMessage.getMsg());
System.out.println(licenseResponse);
}
} catch (Exception e) {
System.out.print(" FAILED");
}
}
public static String loadCertificateStatusListFromFile(String certificateStatusListFile)
throws IOException {
BufferedReader br = Files.newBufferedReader(Paths.get(certificateStatusListFile), UTF_8);
StringBuilder sb = new StringBuilder();
String line = br.readLine();
while (line != null) {
sb.append(line);
sb.append(System.lineSeparator());
line = br.readLine();
}
br.close();
return sb.toString();
}
public static void main(String[] argv) throws Exception {
int numberOfRunners = 10;
int numberOfLoops = 100;
// TODO(yawenyu): Remove loadCertificateStatusListFromFile function. Add three flags to the
// main function for certificateStatusListFile,b64DrmServiceCertificate and b64PrivateKey.
String certificateStatusListFile =
"sdk/testing/sampleProdCertificateStatusList.json";
String b64DrmServiceCertificate =
"CsYCCAMSEIrKPCL3PObh7uUsPogH1bwY-PDksQUijgIwggEKAoIBAQDP6G_52lmYxLv2m2qL3B2L"
+ "tUUjCcHWvy4cxBe4_9TJLHkqaZe8InB4mmn_V0IV0f8-1jXu9cwxHycp0c2FzUe40KcK5Y1Tjqp2"
+ "EgFzC_e7z_KnTv0_ar1E7HANAAJbfOZEsTPDiLqRyD0kS85lCCxHICPrruORnKvgogDm4Gr4ACNO"
+ "NMG8WeH1mgVeIUNPpg1FVeeWYr1zMAmJmSHjPz6ymHW9lMNFLZayJZNjA9L-g4Ncv8mI03Qt0fmi"
+ "hJLa5p9EHrqDuhbEgmD3fmEvxwyitR111NbXPAzJPk_4jfMqztIiqDoEXqBEpnFgdTBPwx9FmC3C"
+ "en_0SGMMtpmC8sfJAgMBAAE6GWF0cy53di5zYW5kYm94Lmdvb2dsZS5jb20SgAN1Hma3aCJos4A_"
+ "aa-JyanEIZ12SpZpkQissZxSI0JTwRgW8bnYMgMFqJAdxKQ60kDjwzlqi30bpqXgFa6dvfWjswlR"
+ "cRQovHhR0UAnI04LmmXuH4LN1Exclh9TxKnMtYW9tkCKD-Ykrl1gbaBSYkTLTvn9zV8z7sHePDzz"
+ "tNMZH0MFR2YWLc0X5TSPJETftB_iGgm0SoQy7CpFYEswBV4kTlTEtEbdH3D1tPvp2VT9--sT45b2"
+ "rH2smhm-iiOi20sr42Dhcsl6TkIrmvuKcXv7oVjNHEUo4F25qmjFaNTVmBjxwfYPxTANqnnNJVIO"
+ "bAqfQVMCJT0sUd5C0LWcYIaUOxRI2hJCp3rpEyIWPjexk8hRwZ1S2Sdiiiwlh_ms3PlSkyoMWIHa"
+ "VllH3OV_8S1LFMy3ZgCOgx0rnFjNVpFayCSLWFhtE0waV2dQ1bhtYhGDxn17fO0EXUyNJhPa2rfy"
+ "r1VDonJVG7CyyzwF88_0KEZISCQykw3oCF82PDKF2tU=";
String b64PrivateKey =
"MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQItfwZJLNOj2MCAggAMBQGCCqGSIb3"
+ "DQMHBAiwSBxfbxJvkwSCBMi6EkXgJBbGDayLfDbm1EFCucR6OiGAJoH_1ePza9mGREhI_r4YqCZx"
+ "opY8D5aFJIeKjR4KCiOoGZnn9xHu5JBhs_asGFWuSFry95fXFzfgYCFqzI2Za9TZkDpqDrh2d8on"
+ "CGBIYgz9PaxXKrxAQxhLfKaZ8lB-pRi5SRTV-6nxG37ZfDMRIrHFQcxhlnjTSQ97Ix2iaK66UYDJ"
+ "sqjapH3cYwk_B_xaxpbYUDlmuJBh14SzSpjG_HXlBWRxCKBRljXo6wjBoBFyKviu4p3Otcpfa7Rf"
+ "1nF4gQHHW6HvMvQTaY0lC6D5NzyV7KWosBdYX0am7uvQKs9MqulhL0NpBFMywyb9CTDvccoDyanO"
+ "U4nkSsax6ILS2trA7JKrb_4CX5W6qkmFgCqzgx8cNWI-1NABCJzBtF85BGoArQKW_sQqh96ulfTb"
+ "MpyQKQ2Uvkqtfv3G7Q5Hz3A6qAJqf6A6a9ADsk4ry9-ZZshlxFw4iNa6kNQKKSO4EElIHXe47TAF"
+ "658nb9aA2cSlLQnr7d9h-AyGghP6cztTazwgjiS7lXo4GwOQQ2jWuVJ4RI0750WdC5t1yS1p-qIC"
+ "2Ckh71OF8d3DSDQYTMYjn5iOODQqqM9mx_VMIairNfrCsX9GvOHSw0My3OshKCuPXhdt3TkThp2T"
+ "Mr4TlunHiPG5C7S1tSr0Brvy_c6XpejpLxmnUd-_J53GfbHLXeZiifMVCu7c7BKSkoDSLQwfk7lz"
+ "Idu0BE9GtIrB1NmQasYaeE9L31TL4KegvROFksyK7mZnC6NEXZKsZdsfV3TcB0OZ52BE5L2X7CMO"
+ "X-n_BQeH-cB-pbIuFqKtbjEcqRUag-m4MYZ2GkMwBJP3Sh_jewjh2TKPlkwvJI9FX5n7VGGuBvN5"
+ "CmUqqgQAqu9ieVuz-VVylSIOwdEdSyPGQia45OlYd9HsTD7NdGPyseBjlVLWq2BI1lG5KqJqCH7w"
+ "NxrIYl0PWOo6WrClWJW1CkJXRmoKzl5PTx-PL8W0KO3pZg94RMHVsZ8sRE62MPPu9Ltnl6x0Zjc8"
+ "YQItP7eR-aF0wlQQ-8__TBTBah3hLMBsAEKV6xD2bTvHBW-3vd-WCxX3hM7FACULlxgbT5wkXb0K"
+ "hUOWwim3zLRjbFhOI-MynVt6aM83bWrSRdbr52eyT4Ybx3EZ8tLVtzumgLVMWbJXcUnOU6I5y6W-"
+ "tzzUHsDba5AtG4-u9UitUQlDd4Hh4GlUaRdCqX8PwQLl-efjJysBuNu5xobxp3df9SqRgxCdaC-J"
+ "CS2Hi_hciIrcJNwxTJFUjilGOMDAqM0GdH8QMkbFjPAsAvz_fMYHoB4vyRYeURbwyiFb8hVOjgdB"
+ "8M_oRlmeYIcS8DzR84WdV-yUV_hsakjIqr5KznmGqhDSgrv609vjxKLO3PUnXq1J5TcTH5hN-bHr"
+ "WS-9zV0iA81aCVxk7HbCAj_ODuQjFYu_B31gvpkgICvgb-PMxCU-GJ5-bcYLWm-8apHdQ15SMbma"
+ "TrW7k7JZLMMqSjc7aJ25NyIbAtBg70EjebAnJI1JqmnPV-lUzfeqW0ulRv1fQZH057BXCyaPhNDY"
+ "rFf_lzyz-aX21HuL59l1B6rEJPU3qxdctT6kmi6L78fTvSP0V5aNd0box0Y=";
String passphrase = "encryptallthekitties";
byte[] passphraseBytes = passphrase.getBytes(UTF_8);
byte[] decodedDrmServiceCertificate = Base64.getUrlDecoder().decode(b64DrmServiceCertificate);
byte[] decodedPrivateKey = Base64.getUrlDecoder().decode(b64PrivateKey);
String certList = "";
try {
certList = loadCertificateStatusListFromFile(certificateStatusListFile);
} catch (FileNotFoundException e) {
System.out.println("FileNotFoundException in reading input cert list.");
} catch (IOException e) {
System.out.println("IOException in reading input cert list.");
}
java.util.Map<String, String> configValues = new java.util.HashMap<>();
String providerIv = "d58ce954203b7c9a9a9d467f59839249";
String providerKey = "1ae8ccd0e7985cc0b6203a55855a1034afc252980e970ca90e5202689f947ab9";
// Define the configuration that is to be used for WvPLEnvironment.
configValues.put("allow_unknown_device", "1");
configValues.put("provider", "widevine_test");
configValues.put("provider_iv", providerIv);
configValues.put("provider_key", providerKey);
// Set the device certificate expiration to 10 years (10 * 365 * 24 * 3600).
// Note that in practice, the expiration should not be 10 years long. Certificate status list
// should be updated periodically.
configValues.put("device_certificate_expiration", "315360000");
java.util.Map<Integer, String> preProvKeys = new java.util.HashMap<>();
preProvKeys.put(100, "f7008b38acc00ec68c732ac665c55c65");
try (WvPLEnvironment env = new WvPLEnvironment(configValues)) {
env.setPreProvisioningKeys(preProvKeys);
WvPLStatus status;
try {
status = env.setDeviceCertificateStatusList(certList.getBytes(UTF_8));
} catch (WvPLStatusException e) {
status = e.getStatus();
System.out.println("setDeviceCertificateStatusList exception Message: "
+ e.getMessage() + ", exception status message: " + e.getStatus().getMessage()
+ ", numeric code = " + e.getStatus().getNumericCode());
}
status = env.setServiceCertificate(
decodedDrmServiceCertificate, decodedPrivateKey, passphraseBytes);
if (status.getStatusCode() != WvPLStatus.StatusCode.OK) {
System.out.println("setServiceCertificate status = " + status.getStatusCode()
+ ", message = " + status.getMessage());
} else {
System.out.println("Successfully loaded service certificate");
}
WvTestRunner[] runner = new WvTestRunner[numberOfRunners];
ThreadGroup group = new ThreadGroup("Test Runner");
for (int i = 0; i < runner.length; i++) {
runner[i] = new WvTestRunner(env, group, "thread_runner_" + i, numberOfLoops);
runner[i].start();
}
for (WvPLExample.WvTestRunner element : runner) {
element.join();
}
env.close();
}
}
}

View File

@@ -0,0 +1,563 @@
// Copyright 2021 Google LLC. All rights reserved.
package com.google.video.widevine.sdk.wvpl;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.video.widevine.protos.LicenseProtocol.License;
import com.google.video.widevine.protos.LicenseProtocol.SignedMessage;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class WvPLUsingDSPExample {
/** Example of how to use WvPL API including DSP functions.
*
* This example is used when content providers want to use DSP API for license
* generation. The qualified profiles can be used to determine the content keys
* allowed in the license.
*
* If content provider doesn't want to use DSP for license generation, they
* could follow the example shown in "wvpl_license_sdk_example.cc".
*/
/* Creates WvTestRunner to run wvpl test */
static class WvTestRunner extends Thread {
private final int loops;
private final String threadName;
private final WvPLEnvironment env;
private List<String> customProfileOwners;
private String contentOwner;
public WvTestRunner(WvPLEnvironment env, ThreadGroup group, String threadName, int loops) {
super(group, threadName);
this.loops = loops;
this.threadName = threadName;
this.env = env;
}
public void setCustomProfileOwners(List<String> customProfileOwners) {
this.customProfileOwners = customProfileOwners;
}
public void setContentOwner(String contentOwner) {
this.contentOwner = contentOwner;
}
@Override
public void run() {
System.out.println("Thread Started: " + threadName);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
System.out.println("InterruptedException in thread.");
}
String b64LicenseRequest =
"CAES9AwKlQwIARLtCQquAggCEhDEmqLqtzGkARd0kTEo0I1IGJSf2qkFIo4CMIIBCgKCAQEA0NXBS5-DGcEt_vnp"
+ "kWgiYUrrlKjddGnSLPuloU5fNHN-TTtF2HwCbKHxv-71EeYwl4omrd36XZ7N6cRPE78G1BQoJy57z2wgCJZ8_3"
+ "jPsMnQSadynMxDdvVBqQde87T66-KqqBMTBOI3dC_LtX1Ydj04OR3yIKSlmTGGPwW37BfvsmNWGAHOHa0i7kP3"
+ "C1HkUyBixNJLArnNmQSfLi5jf8EyRu6-yJwFRyY1EcXXcDouUgJbWFGey6oK26-HI87rs4bEJyjxk3fr0BJcfh"
+ "QSVm-Awmyp4TU0J_A_PLoatG0yzjvPLT0ymsujLta0ZT6xHLSSeD3JfDyrlyS196GS2QIDAQABKIchEoACUqrZ"
+ "8CzIzCe-j4BI0cqjvQ7C_spcMjwdlO9E5pb-FIcP5fDdOSa8Op6gpkB7HwEv-HC8ITXjdQ3MRl5BcLB8elBDBw"
+ "Tl-nfw4AUqVRwSyPKfpOTU9w7MloiFBD8j2zP0Fbb3gub_G-JCMBYlpi8ObTiE5a3olWIl__trUgj-huLSKkIs"
+ "dH7dtaK55vvHIM7h7UcwomnxjJN1phHxB0c4iyHBAQfGaVSIUz3N7nXqsW__QO7qRIa7XCA-uiXpYF17wwTc4w"
+ "_X_fOKoYJ8mly85Sunm-XvxKZGzzcjyJuOsO_cK_KUiqt-TzeVmiohpmrEDK68ozm6LMFIFO3yBFpNgxq2BQqw"
+ "AggBEhDbPKzP3nYDjtzFHkw5JPr6GMLSg44FIo4CMIIBCgKCAQEAuVISqq1KZKOCyoJdYCxsMcBmzUvOotSUG2"
+ "c3VlJz_7Kq4MY65zff4_8AXtnsT4q4WWiREgiewPWe0n8x8_FI8z3r5cLKzwh_wRUwHlV5Sn2dw4I8VTzdPMH5"
+ "bfmgtCpM1NV5NhzT-YagPth3xrcwCnIP54oCLuKCDxG_rKcmKcHiyMVZnqdYHDNO5b8Tn21EV3JHZsLjpEfo_J"
+ "Og6mv1Z0r-vP6VJQT43KSJns5VSKx-RAHoff2ZTBuDWU2YOqFZpi_oeupPiHT_prwF9bIZiGhbEFNK-2T_t2BG"
+ "SzBpGNkQPb3VLek7WcctcqZTZ-eFQrzboRV0L5O8pho2cjVTswIDAQABKIchMAESgAMB8IopgGaBVAPSQnh6PI"
+ "VhD9nt9H_BgEMsW_pXJ5ij4PMltfdr8y03yF2GHJ_ZTeRYJpL_Mn8Mh5sLCaNF44S3nxSw5dT7bH35ZIFiB-uC"
+ "OH60js6UKadq099NZkPUtLCJAX5U_ggkSq2UMAMeRCyTo7XghUEaf_JyECh6rI5fKAjzIX7MfTLU32yHeiGkjN"
+ "Aj2_SoUnZH91Kb_wbStm6GSQdnw7G2kyKp6I6MJclgQZn7fJVCmRJugC57fDiBVv-sfl3Zp2SYQ9hwRBR9byMn"
+ "Vp5KLedB_v-9nnHniMLgSiDK-HASpZA-5C0OO27jHcdvdMgVjXoyJY05YYW1BcKKnGHit82fMY5zEDVG5mfmgq"
+ "d8ZYVlLAxJLWHZ7XCs1pIcAG1LjEy5rKzQtlT2DdwBzsF5mdybWKCLr-fbEqFo_ftp9HIc6EL70pZ07ofHbl0e"
+ "-PCkn5mJJYPbTNv7UxZgUU_2IqUJTxtwZD2GannSnwAPjd6D7u0W6CwFk-rhBe4aFAoMY29tcGFueV9uYW1lEg"
+ "Rhc3VzGhUKCm1vZGVsX25hbWUSB05leHVzIDcaIAoRYXJjaGl0ZWN0dXJlX25hbWUSC2FybWVhYmktdjdhGhIK"
+ "C2RldmljZV9uYW1lEgNkZWIaFgoMcHJvZHVjdF9uYW1lEgZyYXpvcmcaRQoKYnVpbGRfaW5mbxI3Z29vZ2xlL3"
+ "Jhem9yZy9kZWI6TU5DL01BU1RFUi8xODU1NjM3OnVzZXJkZWJ1Zy9kZXYta2V5cxotCglkZXZpY2VfaWQSIEpR"
+ "NVpJWkVMVUpBQlBVQzNLMkg1Q0NXNklVAAAAAAAAGiYKFHdpZGV2aW5lX2NkbV92ZXJzaW9uEg52My4wLjAtYW"
+ "5kcm9pZDIIEAEgBCgJMAASSgpICiIIARoNd2lkZXZpbmVfdGVzdCIPc3RyZWFtaW5nX2NsaXAxEAEaIDE3NEQ4"
+ "MkUyRjA0QzZGQkEwNjAwMDAwMDAwMDAwMDAwGAEgmZ_aqQUwFTij79WWBRqAAsguK-RkA18-zOzwX38JDpT877"
+ "woGiL-kUhQ9w2NuDxolgFovFqp-VNEESpG9GG-dp8sFXAABy4Xk3YJnyVvAtI6MtiSPjDLt87QPSie6WCLgqvL"
+ "zHoGSGuo4ij4HBX23QM0jZNDz_u_1rdZLKfV-nIWQ1sVI2u-QYnTYCztRmpMd1Gp1mZzLpcOL9PNGIYbuJSmQe"
+ "fi3FDGdwfmKLRYg8peJXj2OPHKNyyJCfRMZ42PmBvg4bxI36Ets_TU_6m2RMKARnTLYoGq0bw0fl9GeV_27mDU"
+ "Kvl0SIWXh0Jup8xxCO_iFQzw_7rCyW2FtrzXqq2qEmPRK5HjY-_c_0XVWh0=";
String testSessionId = "TestSessionId";
String testPurchaseId = "TestPurchaseId";
String testProviderClientToken = "TestProviderClientToken";
boolean testOverrideProviderClientToken = true;
byte[] decodedLicenseRequest = Base64.getUrlDecoder().decode(b64LicenseRequest);
byte[] testMasterSigningKeyBytes = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
for (int i = 0; i < loops; i++) {
try {
byte[] bad = {1, 2, 3, 4};
env.createSession(bad);
} catch (WvPLStatusException e) {
System.out.println("Message for bad session: " + e.getMessage());
System.out.println("Status message for bad session: " + e.getStatus().getMessage());
}
WvPLSession session = null;
try {
session = env.createSession(decodedLicenseRequest);
} catch (WvPLStatusException e) {
System.out.println("Message: " + e.getMessage());
System.out.println("Status message: " + e.getStatus().getMessage());
}
byte[] contentId = null;
try {
contentId = session.getContentId();
System.out.println("ContentId:" + new String(contentId, UTF_8));
} catch (WvPLStatusException e) {
System.out.println("Message: " + e.getMessage());
System.out.println("Status message: " + e.getStatus().getMessage());
}
System.out.println("Version: " + WvPLSession.getVersionString());
// VMP Status
try {
System.out.println(
"VMP Status: " + session.verifyPlatform().getPlatformVerificationStatus());
} catch (WvPLStatusException e) {
System.out.println("Message: " + e.getMessage());
System.out.println("Status message: " + e.getStatus().getMessage());
}
// Check if the input content owner has qualified custom profiles. If it exists, those
// profiles will be used to decide what quality to play. If it doesn't exist,
// will use default profiles instead.
List<String> qualifiedProfileNames;
WvPLStatus status;
if (customProfileOwners.contains(contentOwner)) {
try {
qualifiedProfileNames = session.getQualifiedCustomDeviceSecurityProfiles(contentOwner);
} catch (WvPLStatusException e) {
status = e.getStatus();
System.out.println("Failed to get qualifying custom profiles: " + status.getStatusCode()
+ " for owner: <" + contentOwner + ">");
return;
}
if (!qualifiedProfileNames.isEmpty()) {
System.out.println("DRM qualifying custom profiles for owner <" + contentOwner + ">:");
for (String profileName : qualifiedProfileNames) {
System.out.printf("%s ", profileName);
}
System.out.println();
} else {
// Print out error and exit.
System.out.println("There is no qualified custom DSP for the current device. "
+ "Reject the license request.");
return;
}
} else {
try {
qualifiedProfileNames = session.getQualifiedDefaultDeviceSecurityProfiles();
} catch (WvPLStatusException e) {
status = e.getStatus();
System.out.println("Failed to get qualifying default profiles: "
+ status.getStatusCode());
return;
}
if (!qualifiedProfileNames.isEmpty()) {
System.out.println("DRM qualifying default profiles:");
for (String profileName : qualifiedProfileNames) {
System.out.printf("%s ", profileName);
}
System.out.println();
} else {
// Print out error and exit.
System.out.println("There is no qualified default DSP for the current "
+ "device. Reject the license request.");
return;
}
}
// Content keys are generated based on the info from qualifying profiles.
// The qualified profiles can be used to determine the content keys allowed
// in the license.
// In this example, the following keys are generated based on
// qualified custom profiles. Those keys would support SD and HD content.
WvPLPlaybackPolicy policy = new WvPLPlaybackPolicy();
policy.setLicenseDurationSeconds(10000000L);
session.setPolicy(policy);
WvPLKey key1 = new WvPLKey();
byte[] data1 = {10, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
key1.setKeyBytes(data1);
key1.setTrackType(WvPLTrackType.TrackType.VIDEO_SD);
session.addKey(key1);
WvPLKey key2 = new WvPLKey();
byte[] data2 = {10, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
key2.setKeyId(data2);
key2.setKeyBytes(data2);
key2.setTrackType(WvPLTrackType.TrackType.VIDEO_HD);
WvPLOutputProtection outputProtection = new WvPLOutputProtection();
outputProtection.setHdcp(WvPLHdcp.HDCP.HDCP_V2_2);
outputProtection.setSecurityLevel(WvPLSecurityLevel.SecurityLevel.SW_SECURE_DECODE);
outputProtection.setDisableAnalogOutput(true);
outputProtection.setCgms(WvPLCgms.Cgms.COPY_FREE);
System.out.println(
"SecurityLevel for outputProtection = " + outputProtection.getSecurityLevel());
WvPLVideoResolutionConstraint videoResolutionConstraint1 =
new WvPLVideoResolutionConstraint();
videoResolutionConstraint1.setMinResolutionPixels(300);
videoResolutionConstraint1.setMaxResolutionPixels(600);
videoResolutionConstraint1.setHdcp(WvPLHdcp.HDCP.HDCP_V2);
WvPLVideoResolutionConstraint videoResolutionConstraint2 =
new WvPLVideoResolutionConstraint();
videoResolutionConstraint2.setMinResolutionPixels(3000);
videoResolutionConstraint2.setMaxResolutionPixels(6000);
videoResolutionConstraint2.setHdcp(WvPLHdcp.HDCP.HDCP_V1);
key2.addVideoResolutionConstraint(videoResolutionConstraint1);
key2.addVideoResolutionConstraint(videoResolutionConstraint2);
System.out.println(
"Number of video resolutions in key2 = " + key2.getVideoResolutionConstraint().size());
for (WvPLVideoResolutionConstraint vrc : key2.getVideoResolutionConstraint()) {
System.out.println("min resolution in pixels = " + vrc.getMinResolutionPixels()
+ ", max resolution in pixels = " + vrc.getMaxResolutionPixels()
+ ", hdcp = " + vrc.getHdcp());
}
key2.setOutputProtection(outputProtection);
WvPLOutputProtection requestedOutputProtection = new WvPLOutputProtection();
requestedOutputProtection.setHdcp(WvPLHdcp.HDCP.HDCP_V2);
key2.setRequestedOutputProtection(requestedOutputProtection);
session.addKey(key2);
System.out.println("Is device ChromeCDM = " + session.isChromeCDM());
WvPLSessionInit sessionInit = new WvPLSessionInit();
sessionInit.setSessionId(testSessionId);
sessionInit.setPurchaseId(testPurchaseId);
sessionInit.setMasterSigningKey(testMasterSigningKeyBytes);
sessionInit.setProviderClientToken(testProviderClientToken);
sessionInit.setOverrideProviderClientToken(testOverrideProviderClientToken);
session.setSessionInit(sessionInit);
try {
byte[] license = session.generateLicense();
System.out.println(
"License Duration Seconds = " + session.getPolicy().getLicenseDurationSeconds());
System.out.print("License Response = ");
if (license.length > 0) {
printSignedMessage(license);
} else {
System.out.println("Empty");
}
} catch (WvPLStatusException e) {
System.out.println("Message: " + e.getMessage());
System.out.println("Status message: " + e.getStatus().getMessage());
}
try {
// GetProvisionedDeviceInfo is deprecated, use getDeviceInfo instead.
WvPLDeviceInfo deviceInfo = session.getDeviceInfo();
System.out.println("DeviceInfo = " + deviceInfo);
WvPLDeviceSecurityLevel.DeviceSecurityLevel deviceSecurityLevel =
deviceInfo.getDeviceSecurityLevel();
// Expect a L1 device for the license request in use.
System.out.println(
"Device Security level = " + deviceSecurityLevel.getDeviceSecurityLevel());
} catch (WvPLStatusException e) {
System.out.println("Failed to get deviceInfo.");
System.out.println("Message: " + e.getMessage());
System.out.println("Status message: " + e.getStatus().getMessage());
}
try {
WvPLClientInfo clientInfo = session.getClientInfo();
WvPLHdcp.HDCP maxHdcpVersion = clientInfo.getMaxHdcpVersion();
System.out.println("max hdcp version = " + maxHdcpVersion.getHDCP()
+ ", oem crypto api version = " + clientInfo.getOemCryptoApiVersion()
+ ", provider client token = " + clientInfo.getProviderClientToken());
Map<String, String> namesValues = clientInfo.getNamesValues();
for (Map.Entry<String, String> nameValue : namesValues.entrySet()) {
System.out.println("Key = " + nameValue.getKey() + ", Value = " + nameValue.getValue());
}
} catch (WvPLStatusException e) {
System.out.println("Failed to get clientInfo from license request.");
System.out.println("Message: " + e.getMessage());
System.out.println("Status message: " + e.getStatus().getMessage());
}
WvPLWidevinePsshData widevinePsshData = null;
try {
widevinePsshData = session.getPsshData();
} catch (WvPLStatusException e) {
System.out.println("Failed to get psshData from license request.");
System.out.println("Message: " + e.getMessage());
System.out.println("Status message: " + e.getStatus().getMessage());
}
if (widevinePsshData.getContentId().length > 0) {
System.out.println(
"ContentId from Widevine Pssh = " + Arrays.toString(widevinePsshData.getContentId()));
} else {
System.out.println("ContentId from Widevine Pssh is empty");
}
if (!widevinePsshData.getKeyIds().isEmpty()) {
for (byte[] keyId : widevinePsshData.getKeyIds()) {
System.out.println("keyId from Widevine Pssh = " + Arrays.toString(keyId));
}
} else {
System.out.println("KeyIds from Widevine Pssh is empty");
}
WvPLStatus errorStatus =
new WvPLStatus(WvPLStatus.StatusCode.SERVICE_CERTIFICATE_REQUEST_MESSAGE, "");
byte[] error = env.generateErrorResponse(errorStatus);
System.out.print("Service Certificate Response = ");
if (error.length > 0) {
printSignedMessage(error);
} else {
System.out.println("Empty");
}
// license counter with flushData set
boolean flushData = true;
byte[] statsBytes = env.getStatsAsBytes(flushData);
System.out.print("stats as bytes with flush set = ");
if (statsBytes.length > 0) {
System.out.println(Arrays.toString(statsBytes));
} else {
System.out.println("Empty");
}
String statsString = env.getStatsAsString(flushData);
System.out.print("stats as std::string with flush set = ");
if (statsString.length() > 0) {
System.out.println(statsString);
} else {
System.out.println("Empty");
}
// license counter with flushData unset
flushData = false;
statsBytes = env.getStatsAsBytes(flushData);
System.out.print("stats as bytes with flush unset = ");
if (statsBytes.length > 0) {
System.out.println(Arrays.toString(statsBytes));
} else {
System.out.println("Empty");
}
statsString = env.getStatsAsString(flushData);
System.out.print("stats as String with flush unset = ");
if (statsString.length() > 0) {
System.out.println(statsString);
} else {
System.out.println("Empty");
}
WvPLSessionInit retrievedSessionInit = session.getSessionInit();
System.out.println(
"retrieved session init fields: sessionId = " + retrievedSessionInit.getSessionId()
+ ", purchaseId = " + retrievedSessionInit.getPurchaseId()
+ ", masterSigningKey = " + Arrays.toString(retrievedSessionInit.getMasterSigningKey())
+ ", providerClientToken = " + retrievedSessionInit.getProviderClientToken()
+ ", overrideProviderClientToken = "
+ retrievedSessionInit.getOverrideProviderClientToken());
try {
policy.close();
key1.close();
key2.close();
outputProtection.close();
session.close();
} catch (IOException e) {
System.out.println("IOException when closing: " + e.getMessage());
}
System.out.println("Thread Name: " + threadName + ", Loop Complete: " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
System.out.println("InterruptedException in thread.");
}
}
System.out.println("Thread Ended: " + threadName);
}
}
public static void printSignedMessage(byte[] bytes) {
try {
SignedMessage licenseMessage = SignedMessage.parseFrom(bytes);
System.out.println(licenseMessage);
if (licenseMessage.getType() == SignedMessage.MessageType.LICENSE
|| licenseMessage.getType() == SignedMessage.MessageType.SERVICE_CERTIFICATE) {
License licenseResponse = License.parseFrom(licenseMessage.getMsg());
System.out.println(licenseResponse);
}
} catch (Exception e) {
System.out.print(" FAILED");
}
}
public static String loadCertificateStatusListFromFile(String certificateStatusListFile)
throws IOException {
BufferedReader br = Files.newBufferedReader(Paths.get(certificateStatusListFile), UTF_8);
StringBuilder sb = new StringBuilder();
String line = br.readLine();
while (line != null) {
sb.append(line);
sb.append(System.lineSeparator());
line = br.readLine();
}
br.close();
return sb.toString();
}
public static void main(String[] argv) throws Exception {
int numberOfRunners = 10;
int numberOfLoops = 100;
// TODO(yawenyu): Remove loadCertificateStatusListFromFile function. Add three flags to the
// main function for certificateStatusListFile,b64DrmServiceCertificate and b64PrivateKey.
String certificateStatusListFile =
"sdk/testing/sampleProdCertificateStatusList.json";
String b64DrmServiceCertificate =
"CsYCCAMSEIrKPCL3PObh7uUsPogH1bwY-PDksQUijgIwggEKAoIBAQDP6G_52lmYxLv2m2qL3B2L"
+ "tUUjCcHWvy4cxBe4_9TJLHkqaZe8InB4mmn_V0IV0f8-1jXu9cwxHycp0c2FzUe40KcK5Y1Tjqp2"
+ "EgFzC_e7z_KnTv0_ar1E7HANAAJbfOZEsTPDiLqRyD0kS85lCCxHICPrruORnKvgogDm4Gr4ACNO"
+ "NMG8WeH1mgVeIUNPpg1FVeeWYr1zMAmJmSHjPz6ymHW9lMNFLZayJZNjA9L-g4Ncv8mI03Qt0fmi"
+ "hJLa5p9EHrqDuhbEgmD3fmEvxwyitR111NbXPAzJPk_4jfMqztIiqDoEXqBEpnFgdTBPwx9FmC3C"
+ "en_0SGMMtpmC8sfJAgMBAAE6GWF0cy53di5zYW5kYm94Lmdvb2dsZS5jb20SgAN1Hma3aCJos4A_"
+ "aa-JyanEIZ12SpZpkQissZxSI0JTwRgW8bnYMgMFqJAdxKQ60kDjwzlqi30bpqXgFa6dvfWjswlR"
+ "cRQovHhR0UAnI04LmmXuH4LN1Exclh9TxKnMtYW9tkCKD-Ykrl1gbaBSYkTLTvn9zV8z7sHePDzz"
+ "tNMZH0MFR2YWLc0X5TSPJETftB_iGgm0SoQy7CpFYEswBV4kTlTEtEbdH3D1tPvp2VT9--sT45b2"
+ "rH2smhm-iiOi20sr42Dhcsl6TkIrmvuKcXv7oVjNHEUo4F25qmjFaNTVmBjxwfYPxTANqnnNJVIO"
+ "bAqfQVMCJT0sUd5C0LWcYIaUOxRI2hJCp3rpEyIWPjexk8hRwZ1S2Sdiiiwlh_ms3PlSkyoMWIHa"
+ "VllH3OV_8S1LFMy3ZgCOgx0rnFjNVpFayCSLWFhtE0waV2dQ1bhtYhGDxn17fO0EXUyNJhPa2rfy"
+ "r1VDonJVG7CyyzwF88_0KEZISCQykw3oCF82PDKF2tU=";
String b64PrivateKey =
"MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQItfwZJLNOj2MCAggAMBQGCCqGSIb3"
+ "DQMHBAiwSBxfbxJvkwSCBMi6EkXgJBbGDayLfDbm1EFCucR6OiGAJoH_1ePza9mGREhI_r4YqCZx"
+ "opY8D5aFJIeKjR4KCiOoGZnn9xHu5JBhs_asGFWuSFry95fXFzfgYCFqzI2Za9TZkDpqDrh2d8on"
+ "CGBIYgz9PaxXKrxAQxhLfKaZ8lB-pRi5SRTV-6nxG37ZfDMRIrHFQcxhlnjTSQ97Ix2iaK66UYDJ"
+ "sqjapH3cYwk_B_xaxpbYUDlmuJBh14SzSpjG_HXlBWRxCKBRljXo6wjBoBFyKviu4p3Otcpfa7Rf"
+ "1nF4gQHHW6HvMvQTaY0lC6D5NzyV7KWosBdYX0am7uvQKs9MqulhL0NpBFMywyb9CTDvccoDyanO"
+ "U4nkSsax6ILS2trA7JKrb_4CX5W6qkmFgCqzgx8cNWI-1NABCJzBtF85BGoArQKW_sQqh96ulfTb"
+ "MpyQKQ2Uvkqtfv3G7Q5Hz3A6qAJqf6A6a9ADsk4ry9-ZZshlxFw4iNa6kNQKKSO4EElIHXe47TAF"
+ "658nb9aA2cSlLQnr7d9h-AyGghP6cztTazwgjiS7lXo4GwOQQ2jWuVJ4RI0750WdC5t1yS1p-qIC"
+ "2Ckh71OF8d3DSDQYTMYjn5iOODQqqM9mx_VMIairNfrCsX9GvOHSw0My3OshKCuPXhdt3TkThp2T"
+ "Mr4TlunHiPG5C7S1tSr0Brvy_c6XpejpLxmnUd-_J53GfbHLXeZiifMVCu7c7BKSkoDSLQwfk7lz"
+ "Idu0BE9GtIrB1NmQasYaeE9L31TL4KegvROFksyK7mZnC6NEXZKsZdsfV3TcB0OZ52BE5L2X7CMO"
+ "X-n_BQeH-cB-pbIuFqKtbjEcqRUag-m4MYZ2GkMwBJP3Sh_jewjh2TKPlkwvJI9FX5n7VGGuBvN5"
+ "CmUqqgQAqu9ieVuz-VVylSIOwdEdSyPGQia45OlYd9HsTD7NdGPyseBjlVLWq2BI1lG5KqJqCH7w"
+ "NxrIYl0PWOo6WrClWJW1CkJXRmoKzl5PTx-PL8W0KO3pZg94RMHVsZ8sRE62MPPu9Ltnl6x0Zjc8"
+ "YQItP7eR-aF0wlQQ-8__TBTBah3hLMBsAEKV6xD2bTvHBW-3vd-WCxX3hM7FACULlxgbT5wkXb0K"
+ "hUOWwim3zLRjbFhOI-MynVt6aM83bWrSRdbr52eyT4Ybx3EZ8tLVtzumgLVMWbJXcUnOU6I5y6W-"
+ "tzzUHsDba5AtG4-u9UitUQlDd4Hh4GlUaRdCqX8PwQLl-efjJysBuNu5xobxp3df9SqRgxCdaC-J"
+ "CS2Hi_hciIrcJNwxTJFUjilGOMDAqM0GdH8QMkbFjPAsAvz_fMYHoB4vyRYeURbwyiFb8hVOjgdB"
+ "8M_oRlmeYIcS8DzR84WdV-yUV_hsakjIqr5KznmGqhDSgrv609vjxKLO3PUnXq1J5TcTH5hN-bHr"
+ "WS-9zV0iA81aCVxk7HbCAj_ODuQjFYu_B31gvpkgICvgb-PMxCU-GJ5-bcYLWm-8apHdQ15SMbma"
+ "TrW7k7JZLMMqSjc7aJ25NyIbAtBg70EjebAnJI1JqmnPV-lUzfeqW0ulRv1fQZH057BXCyaPhNDY"
+ "rFf_lzyz-aX21HuL59l1B6rEJPU3qxdctT6kmi6L78fTvSP0V5aNd0box0Y=";
String b64SerializedSignedDSPs =
"CtcBCKW6m4UGEioKAkwxEAAaACICEAEqDXdpZGV2aW5lX3Rlc3Q6DQiq_tKDBhD_gtH_rwcSLQoFTDMtVjIQAxoAI"
+ "gIQAyoNd2lkZXZpbmVfdGVzdDoNCKr-0oMGEP-C0f-vBxIsCgJMMxADGgAiBAgPEAMqDXdpZGV2aW5lX3Rlc3Q6"
+ "DQiq_tKDBhD_gtH_rwcSRgoIVGVzdCBEU1AQAhoECAMQAyIGCAsQASADKg13aWRldmluZV90ZXN0MgUI6AcQAjI"
+ "FCOkHEAE6DQiq_tKDBhD_gtH_rwcSgANriJGUB9Gx1JwP1IIKmPsThy7k2P2GKCI6t0CNIuQ48Z54hz4HUC8lqV"
+ "Ykae1qoVH6PD7oJJ4Wzc5VKWSs6iLCiS-CB2z4LwS2a7om93hhXZ1DRYBdHOsvmItYPwZwNcM5lZBYGNOCi36jp"
+ "IVLqesIlnS4W3zjYUDEsiHhgbTftvZMRaSnbsDyvT5Hzc2zW6dyJbMLQfT-Yvt3oj4RmzxnDoc406Z01HVv3sPO"
+ "gYefTTK9z348Qvcaol4D9EllBI2t9DODhKrbV9rOZbNN8R3SAHHSKy0r8abNoyGNLEDluhIxjsUX1t7m4S20Bh7"
+ "-Ss5GwfysrU3tPXzJHIP5TY0pWeAW46UI3cDmPeJXfSEh85jl64GviY26l5tP2o_wxJ1WYOdP44hSs0QwNadG6E"
+ "kaEoZ8Kr0LSVISinZ8rMjNoiIu7K8JSt92av3_422wg88yfnfO398kgpOAr6Z2AiC7z4p0R7IOSR0W3JKGL0QcO"
+ "LddlYoqbLK5RvGOeFOh_QcYAg==";
String passphrase = "encryptallthekitties";
byte[] passphraseBytes = passphrase.getBytes(UTF_8);
byte[] decodedDrmServiceCertificate = Base64.getUrlDecoder().decode(b64DrmServiceCertificate);
byte[] decodedPrivateKey = Base64.getUrlDecoder().decode(b64PrivateKey);
String certList = "";
try {
certList = loadCertificateStatusListFromFile(certificateStatusListFile);
} catch (FileNotFoundException e) {
System.out.println("FileNotFoundException in reading input cert list.");
} catch (IOException e) {
System.out.println("IOException in reading input cert list.");
}
java.util.Map<String, String> configValues = new HashMap<>();
String providerIv = "d58ce954203b7c9a9a9d467f59839249";
String providerKey = "1ae8ccd0e7985cc0b6203a55855a1034afc252980e970ca90e5202689f947ab9";
// Define the configuration that is to be used for WvPLEnvironment.
configValues.put("allow_unknown_device", "1");
configValues.put("provider", "widevine_test");
configValues.put("provider_iv", providerIv);
configValues.put("provider_key", providerKey);
// Set the device certificate expiration to 10 years (10 * 365 * 24 * 3600).
// Note that in practice, the expiration should not be 10 years long. Certificate status list
// should be updated periodically.
configValues.put("device_certificate_expiration", "315360000");
java.util.Map<Integer, String> preProvKeys = new java.util.HashMap<>();
preProvKeys.put(100, "f7008b38acc00ec68c732ac665c55c65");
try (WvPLEnvironment env = new WvPLEnvironment(configValues)) {
env.setPreProvisioningKeys(preProvKeys);
WvPLStatus status;
try {
status = env.setDeviceCertificateStatusList(certList.getBytes(UTF_8));
} catch (WvPLStatusException e) {
status = e.getStatus();
System.out.println("setDeviceCertificateStatusList exception Message: "
+ e.getMessage() + ", exception status message: " + e.getStatus().getMessage()
+ ", numeric code = " + e.getStatus().getNumericCode());
}
status = env.setServiceCertificate(
decodedDrmServiceCertificate, decodedPrivateKey, passphraseBytes);
if (status.getStatusCode() != WvPLStatus.StatusCode.OK) {
System.out.println("setServiceCertificate status = " + status.getStatusCode()
+ ", message = " + status.getMessage());
} else {
System.out.println("Successfully loaded service certificate");
}
// Set custom device security profile list.
// One time setup each time new DSPs are loaded.
byte[] signedDSP = Base64.getUrlDecoder().decode(b64SerializedSignedDSPs);
status = env.setCustomDeviceSecurityProfiles(signedDSP);
if (status.getStatusCode() != WvPLStatus.StatusCode.OK) {
System.out.println("setCustomDeviceSecurityProfiles status = " + status.getStatusCode()
+ ", message = " + status.getMessage());
return;
}
// Optional sannity check.
// Verify the custom profile owners match with list of expected content
// owners.
List<String> customProfileOwners = new ArrayList<>();
try {
customProfileOwners = env.getCustomDeviceSecurityProfileOwners();
} catch (WvPLStatusException e) {
status = e.getStatus();
System.out.println("getCustomDeviceSecurityProfileOwners exception Message: "
+ e.getMessage() + ", exception status message: " + e.getStatus().getMessage()
+ ", numeric code = " + e.getStatus().getNumericCode());
}
if (customProfileOwners.isEmpty()) {
System.out.println("Owner list is empty for custom device security profiles.");
return;
}
System.out.printf("Custom profile owners are: ");
for (String owner : customProfileOwners) {
System.out.printf("%s, ", owner);
}
System.out.println();
String contentOwner = "widevine_test";
WvTestRunner[] runner = new WvTestRunner[numberOfRunners];
ThreadGroup group = new ThreadGroup("Test Runner");
for (int i = 0; i < runner.length; i++) {
runner[i] = new WvTestRunner(env, group, "thread_runner_" + i, numberOfLoops);
runner[i].setCustomProfileOwners(customProfileOwners);
runner[i].setContentOwner(contentOwner);
runner[i].start();
}
for (WvPLUsingDSPExample.WvTestRunner element : runner) {
element.join();
}
env.close();
}
}
}

View File

@@ -0,0 +1,260 @@
// Copyright 2020 Google LLC. All rights reserved.
#ifndef VIDEO_WIDEVINE_EXPORT_SDK_EXTERNAL_CPP_WVDRM_LICENSE_SERVER_SDK_ENVIRONMENT_H_
#define VIDEO_WIDEVINE_EXPORT_SDK_EXTERNAL_CPP_WVDRM_LICENSE_SERVER_SDK_ENVIRONMENT_H_
#include <memory>
#include "absl/strings/string_view.h"
#include "absl/synchronization/mutex.h"
#include "common/drm_root_certificate.h"
#include "common/security_profile_list.h"
#include "common/status.h"
#include "sdk/external/cpp/wvdrm/license_server_sdk/session.h"
#include "sdk/internal/environment_impl.h"
namespace video_widevine {
constexpr char kWrappingKeyLabel[] = "ENCRYPTION";
constexpr uint32_t kWrappingKeySizeBits = 128;
constexpr char kSigningKeyLabel[] = "AUTHENTICATION";
constexpr uint32_t kSigningKeySizeBits = 256;
class Environment {
public:
Environment(absl::string_view provider, const DrmRootCertificate* root_cert);
virtual ~Environment();
// Add a service certificate system-wide.
// |service_certificate| is a Google-generated certificate used to
// authenticate the service provider for purposes of device privacy;
// |service_private_key| is the encrypted PKCS#8 private RSA key corresponding
// to the service certificate; and |service_private_key_passphrase| is the
// password required to decrypt |service_private_key|.
Status AddDrmServiceCertificate(
const std::string& service_certificate,
const std::string& service_private_key,
const std::string& service_private_key_passphrase);
// Returns true if service certificate is loaded.
bool is_service_certificate_loaded();
// Specify a comma separated list of system Ids that can support having
// OEMCrypto version, as specified in the license request, reflected back in
// the Key Control Block which is used by partner. Otherwise, only 'kctl' or
// 'kc09' is returned in KCB.
void SetDevicesToHandleOEMCryptoVersionInKCB(
const std::string& system_id_list);
// Set pre-provisioning keys system-wide. Map key is system_id, value.
// Value should be human-readable hex digits encoded bytes, e.g.
// 'preProvKeys.put(100, "f7008b38acc00ec68c732ac665c55c65")'. Must be called
// before any other calls to this class. Calls are thread-safe, so the keys
// can be updated at any time.
void SetPreProvisioningKeys(const std::map<uint32_t, std::string>& keys);
void SetPreProvisioningKeys(const std::multimap<uint32_t, std::string>& keys);
// Set the certificate status list system-wide.
// |expiration_period| is the number of seconds until the
// certificate_status_list expires after its creation time
// (creation_time_seconds). If |allow_unknown_devices| is false, an error is
// returned if the device does not appear in the certificate_status_list.
Status SetCertificateStatusList(const std::string& certificate_status_list,
uint32_t expiration_period_seconds,
bool allow_unknown_devices);
// Enable delivery of licenses to client devices. This includes devices with
// TEST_ONLY status, and development platform verification certificates.
// Defaults to false.
void AllowDevelopmentClients(bool enable);
// Enable delivery of licenses to TEST_ONLY client devices.
// |device_list_make| is a comma separated list of devices to allow even
// if the device is in a TEST_ONLY state. This list wil be used only if
// AllowDevelopmentClient(false) is in use.
void AllowTestOnlyDevicesByMake(const std::string& device_list_make);
// Enable delivery of licenses to TEST_ONLY client devices.
// |device_list_provider| is a comma separated list of provider to allow
// even if the device is in a TEST_ONLY state. This list wil be used only if
// AllowDevelopmentClient(false) is in use.
void AllowTestOnlyDevicesByProvider(const std::string& device_list_provider);
// Enable delivery of licenses to revoked client devices. |system_id_list| is
// a comma separated list of systems Ids to allow even if revoked.
void AllowRevokedDevices(const std::string& system_id_list);
// A comma separated list of DRM Certificate Serial Numbers that are revoked.
void RevokedDrmCertificateSerialNumbers(
const std::string& drm_certificate_serial_numbers);
// Restriction on core message features. If this is an empty string, the
// default feature set is used. If it is an integer, that is the ODK version
// supported. This restricts the features that the server will support in an
// oemcrypto core message. For example, we may restrict the server to never
// send a v17 message by setting the std::string to "16". For details, please see
// common/oemcrypto_core_message/odk/include/core_message_features.h
void SetCoreMessageFeatures(const std::string& core_message_features);
// Creates a Session object.
// |root_cert| is the root certificate to be used to validate client
// credentials.
// |signed_license_request| is the serialized SignedMessage received from the
// client. |session| points to a Session*, which must be initialized to NULL
// on entry, but |session| itself may not be NULL. The new Session object will
// be owned by the caller. This method returns Status::OK if successful,
// or an appropriate error status, in which case
// Environment::GenerateErrorResponse should be invoked.
// Example usage:
// Environment env = absl::make_unique<Environment>(kProvider,
// drm_root_cert);
// Session* session = NULL;
// Status status = env->CreateSession(request_from_client,&session);
// if (!status.ok()) {
// std::string error_license;
// if (env->GenerateErrorResponse(status, &error_license)) {
// // Send error_license to the client.
// } else {
// // Handle error
// }
// return ...
// }
// // Create license, invoke GenerateSignedLicense, etc.
Status CreateSession(const std::string& signed_license_request,
Session** session);
// Create a session for generating a license. This variation of Create takes
// options to allow for the creation of the session to succeed even if the
// device is revoked.
Status CreateSessionWithOptions(const std::string& signed_license_request,
const SessionCreateOptions& options,
Session** session);
// Variation of Environment::CreateSession which also fills in the parsed
// LicenseRequest, for use in logging or debugging.
Status CreateSession(const std::string& signed_license_request,
const SessionCreateOptions& options, Session** session,
LicenseRequest* parsed_request_out);
// Same as CreateSession(), but caller can specify the ClientIdentification
// message and/or PlatformVerificationStatus. If ClientIdentification is
// specified, this variation of Create() will use the specified |client_id|
// instead of what is specified in |signed_license_request|. If
// PlatformVerificationStatus is specified, this method will use the specified
// |platform_verification_status| instead of attempting to determine it.
// Background for this function is to support cases where the client
// identification is encrypted with the provider's service certificate in
// which case we won't be able to decrypt OR when the provider determines
// platform verification. The provider will specify the
// clear client identification in |client_id| and the platform verification
// in |platform_verification_status|.
Status CreateSessionForProxy(
const std::string& signed_license_request,
const PlatformVerificationStatus platform_verification_status,
const ClientIdentification* client_id,
const SessionCreateOptions& options, Session** session,
LicenseRequest* parsed_request_out);
// Generates a SignedMessage containing a message generated in response to
// an error condition. |status| is a previous error status returned by the
// Session or Status(error::UNAVAILABLE, ...) to indicate that the
// backend is unavailable, |signed_message| points to a std::string to contain the
// serialized SignedMessage, and may not be NULL. This method returns true if
// there is an error license to be sent to the client, or false otherwise.
// Example usage in the Environment::Create comments above.
bool GenerateErrorResponse(const Status& status,
std::string* signed_message_bytes);
// DeriveKey uses the NIST 800-108 KDF recommendation, using AES-CMAC PRF.
// NIST 800-108:
// http://csrc.nist.gov/publications/nistpubs/800-108/sp800-108.pdf
// AES-CMAC:
// http://tools.ietf.org/html/rfc4493
std::string DeriveKey(const std::string& key, const std::string& label,
const std::string& context, const uint32_t size_bits);
// Returns a std::string containing the Widevine License Server SDK version in the
// form <major_version>.<minor_version>.<release> <build date> <build time> .
std::string GetSdkVersionString();
// If set to true, adds SDK and server version information to the license
// response.
void SetIncludeVersionInfoInLicense(bool include_version_info);
// Sets the service version information which can be included with the license
// response. If SetIncludeVersionInfoInLicense() is set to true and the server
// version is not empty, then the server version will be included in the
// license response. The host_version must be <= 32 characters and limited to
// alphanumeric and '_', '-', ':', ';', ' ', '/' and '.'.
void SetHostServerVersion(const std::string& host_version);
// Generate a signed request to be sent to Widevine Certificate Provisioning
// Server to retrieve 'DeviceCertificateStatusList'.
Status GenerateDeviceStatusListRequest(
std::string* signed_device_certificate_status_list_request);
// Set the custom device security profile list which is returned, from a call
// to Widevine PublishedDevicesService.
Status SetCustomDeviceSecurityProfiles(
absl::string_view serialized_signed_device_security_profiles);
// Return a list of the default profile names.
Status GetDefaultDeviceSecurityProfileNames(
std::vector<std::string>* profile_names) const;
// Return the default profile associated with |profile_name|.
Status GetDefaultDeviceSecurityProfile(
absl::string_view profile_name,
SecurityProfile* device_security_profile) const;
// Obtain the owner list for custom profiles.
Status GetCustomDeviceSecurityProfileOwners(
std::vector<std::string>* custom_profile_owners) const;
// Return a list of custom profile names associated with |owner_name|.
Status GetCustomDeviceSecurityProfileNames(
absl::string_view owner_name,
std::vector<std::string>* profile_names) const;
// Return the custom profiles associated with |owner_name|.
Status GetCustomDeviceSecurityProfiles(
absl::string_view owner_name,
std::vector<SecurityProfile>* custom_device_security_profiles) const;
// If |auto_set_provider_session_token| is 'true', the provider session token
// may be automatically set,
//
// The default setting for |auto_set_provider_session_token| is 'true'.
virtual void SetAutoSetProviderSessionToken(
bool auto_set_provider_session_token);
// Returns the setting as to whether the provider session token will be
// automatically set.
virtual bool GetAutoSetProviderSessionToken() const;
// Set the provider key used for L3 CDM.
// |provider_key_config_bytes| is a serialized ProviderKeyConfig proto
// message. Return OK if parsing is successful, otherwise an error is
// returned.
virtual Status SetProviderKeyConfig(
const std::string& provider_key_config_bytes);
private:
friend class EnvironmentTest;
// Environment::CreateSession which also fills in the parsed
// ExternalLicenseRequest. Used to create a Session object.
Status CreateSession(SignedMessage* signed_message, Session** session,
ExternalLicenseRequest* parsed_request_out);
std::string provider_;
std::unique_ptr<video_widevine::SecurityProfileList>
device_security_profile_list_;
std::shared_ptr<EnvironmentImpl> env_impl_;
// Provider key configuration assigned to a provider for use with L3 CDM.
ProviderKeyConfig provider_key_config_;
};
} // namespace video_widevine
#endif // VIDEO_WIDEVINE_EXPORT_SDK_EXTERNAL_CPP_WVDRM_LICENSE_SERVER_SDK_ENVIRONMENT_H_

View File

@@ -0,0 +1,197 @@
// Copyright 2017 Google LLC. All rights reserved.
#ifndef VIDEO_WIDEVINE_EXPORT_SDK_EXTERNAL_CPP_WVDRM_LICENSE_SERVER_SDK_SESSION_H_
#define VIDEO_WIDEVINE_EXPORT_SDK_EXTERNAL_CPP_WVDRM_LICENSE_SERVER_SDK_SESSION_H_
#include <cstdint>
#include <list>
#include <map>
#include <memory>
#include <string>
#include "absl/strings/string_view.h"
#include "absl/synchronization/mutex.h"
#include "common/status.h"
#include "protos/public/client_identification.pb.h"
#include "protos/public/device_certificate_status.pb.h"
#include "protos/public/drm_certificate.pb.h"
#include "protos/public/external_license.pb.h"
#include "protos/public/license_protocol.pb.h"
#include "protos/public/license_server_sdk.pb.h"
#include "protos/public/oem_key_container.pb.h"
#include "protos/public/security_profile.pb.h"
#include "protos/public/provider_key.pb.h"
namespace video_widevine {
class ClientIdentification;
class ContentInfo;
class DrmRootCertificate;
class ExternalPlayReadySessionImpl;
class License;
class LicenseRequest;
class ProvisionedDeviceInfo;
class SecurityProfile;
class SecurityProfileList;
class SessionImpl;
class EnvironmentImpl;
class SessionInit;
class SessionState;
class SessionUsage;
// TODO(tinskip): Rename this to LicenseSession and add LicenseEngine to hold
// global settings and create new sessions.
class Session {
public:
virtual ~Session();
virtual const LicenseRequest& request() const;
virtual const std::string& GetSessionId();
// Return list of Widevine profiles meeting the DRM requirements for this
// session.
virtual Status GetQualifiedDefaultDeviceSecurityProfiles(
std::vector<std::string>* qualified_profiles) const;
// Retrieves qualifying Custom Security Profiles names given the owner name.
virtual Status GetQualifiedCustomDeviceSecurityProfiles(
absl::string_view owner_name,
std::vector<std::string>* custom_qualified_profile_names) const;
// Returns true if a provisioned device info exists. Caller
// owns |provisioned_device_info| and it must not be null.
virtual bool GetProvisionedDeviceInfo(
video_widevine::ProvisionedDeviceInfo* device_info);
// Accessor for request_id field which may be encoded in one of multiple
// places in the liciense request protcol buffer. Use this method instead
// of accessing directly. |request_id| is a pointer to a std::string to contain
// the request ID upon successful return.
virtual Status GetRequestId(std::string* request_id) const;
// Accessor for license_type field which may be encoded in one of multiple
// places in the license request protocol buffer. Use this method instead
// of accessing directly. |license_type| is a pointer to a value to contain
// the license type upon successful return.
virtual Status GetLicenseType(LicenseType* license_type) const;
// Method used to get ContentIdentification in a consistent message regardless
// of the type or version of initialization data contained in the content_id
// field of the license request. Use this method instead of accessing the
// fields of ContentIdentification directly. |content_info| is a pointer to a
// message to contain the parsed values from content_id upon successful
// return.
virtual Status GetContentInfo(ContentInfo* content_info) const;
// Returns the serial number of certificate associated with this device and
// content provider.
virtual std::string GetDrmDeviceId() const;
// Copies the session usage table from license request to |usage_report|.
// Returns true if session usage exist in the license request, otherwise
// returns false.
bool GetSessionUsage(SessionUsage* usage_report) const;
// Returns true if client info exists, otherwise returns false. Populate the
// specified |client_info| structure.
virtual bool GetClientInfo(ClientIdentification* client_info) const;
// Generates a serialized signed License response, emptying |policy| and
// |key_container|, encrypting the keys therein. |session_init| and
// |session_state| are returned to be cached and provided in subsequent
// calls to the function. If no additional PolicyItem or KeyContainer objects
// are necessary to fulfill the request (such as the case with license
// renewal), |policy| and/or |key_container| may be NULL.
// The response is expected to be sent to the Widevine CDM.
virtual Status GenerateSignedLicense(
/*IN*/ const License::Policy* policy,
/*IN*/ const std::list<License::KeyContainer>* key_container,
/*IN*/ const SessionInit* session_init,
/*INOUT*/ SessionState* session_state,
/*OUT*/ std::string* signed_message_bytes);
virtual PlatformVerificationStatus GetPlatformVerificationStatus() const;
// Returns the service id of the provider that owns the device certificate.
virtual std::string GetDrmDeviceServiceId() const;
// Returns true, if the license request for this session included a key
// control nonce, else false.
virtual bool HasKeyControlNonce() const;
// If set to 'true', allow licenses to be generated even if VMP data was
// determined to be video_widevine::PLATFORM_UNVERIFIED.
virtual void set_allow_unverified_platform(bool allow_unverified_platform);
// Return the setting of whether licenses are allowed to be generated even
// when VMP data was determined to be video_widevine::PLATFORM_UNVERIFIED.
virtual bool allow_unverified_platform() const;
// If set to 'true', allow licenses to be generated even if VMP data was
// determined to be video_widevine::PLATFORM_TAMPERED.
virtual void set_allow_tampered_platform(bool allow_tampered_platform);
/**
* If set to true, reject WvDrm SDK to reject licensing behaviors to unknown
* make model. Default value is false.
*/
virtual void set_reject_unknown_make_model(bool reject_unknown_make_model);
/**
* Retrieves the setting of whether unknown make model is rejected.
*/
virtual bool reject_unknown_make_model() const;
// Return the setting of whether licenses are allowed to be generated even
// when VMP data was determined to be video_widevine::PLATFORM_TAMPERED.
virtual bool allow_tampered_platform() const;
virtual void SetKeys(std::list<OemKeyContainer>* oem_key_container);
// 'Provider' making the request.
virtual void set_provider(const std::string& provider);
// Return the device status such as as RELEASED or REVOKED.
virtual DeviceCertificateStatus::Status GetDeviceStatus() const;
// Returns message type such as LICENSE_REQUEST, SERVICE_CERTIFICATE_REQUEST
// or EXTERNAL_LICENSE_REQUEST.
virtual SignedMessage::MessageType message_type() const;
// Retrieves Widevine Security Profile DrmInfo of the device.
// Returns true if |drm_info| was successully populated, else false.
virtual bool GetDrmInfo(SecurityProfile::DrmInfo* drm_info) const;
// Retrieves the ContentIdentification from the request. Returns OK, if
// successful, else failure.
virtual Status GetContentId(
LicenseRequest::ContentIdentification* content_id) const;
// Retrieves the request type.
virtual LicenseRequest::RequestType request_type() const;
protected:
Session(std::shared_ptr<EnvironmentImpl> env_impl,
std::unique_ptr<SessionImpl> impl);
Session(std::shared_ptr<EnvironmentImpl> env_impl,
std::unique_ptr<ExternalPlayReadySessionImpl>
external_playready_session_impl);
// For testing only. This allows unit tests to define a mock Session class.
Session();
friend class Environment;
private:
#ifndef SWIG
Session(const Session&) = delete;
Session& operator=(const Session&) = delete;
#endif
std::shared_ptr<EnvironmentImpl> env_impl_;
std::unique_ptr<SessionImpl> impl_;
std::unique_ptr<ExternalPlayReadySessionImpl>
external_playready_session_impl_;
};
} // namespace video_widevine
#endif // VIDEO_WIDEVINE_EXPORT_SDK_EXTERNAL_CPP_WVDRM_LICENSE_SERVER_SDK_SESSION_H_

View File

@@ -1,9 +1,9 @@
// Copyright 2020 Google LLC. All rights reserved.
#ifndef VIDEO_WIDEVINE_EXPORT_SDK_EXTERNAL_COMMON_WVPL_WVPL_SDK_COPY_UTILS_H_
#define VIDEO_WIDEVINE_EXPORT_SDK_EXTERNAL_COMMON_WVPL_WVPL_SDK_COPY_UTILS_H_
#ifndef VIDEO_WIDEVINE_EXPORT_SDK_EXTERNAL_CPP_WVPL_COMMON_WVPL_SDK_COPY_UTILS_H_
#define VIDEO_WIDEVINE_EXPORT_SDK_EXTERNAL_CPP_WVPL_COMMON_WVPL_SDK_COPY_UTILS_H_
#include "sdk/external/common/wvpl/wvpl_types.h"
#include "sdk/external/cpp/wvpl/common/wvpl_types.h"
#include "protos/public/client_identification.pb.h"
#include "protos/public/device_security_profile_data.pb.h"
#include "protos/public/license_server_sdk.pb.h"
@@ -53,4 +53,4 @@ void CopyBrowserRequirement(
} // namespace wv_pl_sdk
} // namespace video_widevine_server
#endif // VIDEO_WIDEVINE_EXPORT_SDK_EXTERNAL_COMMON_WVPL_WVPL_SDK_COPY_UTILS_H_
#endif // VIDEO_WIDEVINE_EXPORT_SDK_EXTERNAL_CPP_WVPL_COMMON_WVPL_SDK_COPY_UTILS_H_

View File

@@ -1,15 +1,16 @@
// Copyright 2017 Google LLC. All rights reserved.
#ifndef VIDEO_WIDEVINE_EXPORT_SDK_EXTERNAL_COMMON_WVPL_WVPL_SDK_ENVIRONMENT_H_
#define VIDEO_WIDEVINE_EXPORT_SDK_EXTERNAL_COMMON_WVPL_WVPL_SDK_ENVIRONMENT_H_
#ifndef VIDEO_WIDEVINE_EXPORT_SDK_EXTERNAL_CPP_WVPL_COMMON_WVPL_SDK_ENVIRONMENT_H_
#define VIDEO_WIDEVINE_EXPORT_SDK_EXTERNAL_CPP_WVPL_COMMON_WVPL_SDK_ENVIRONMENT_H_
#include <memory>
#include <string>
#include "absl/synchronization/mutex.h"
#include "sdk/external/common/wvpl/wvpl_types.h"
#include "sdk/external/cpp/wvpl/common/wvpl_types.h"
#include "protos/public/client_identification.pb.h"
#include "protos/public/device_security_profile_list.pb.h"
#include "protos/public/provider_key.pb.h"
#include "protos/public/security_profile.pb.h"
namespace video_widevine {
@@ -152,6 +153,22 @@ class WvPLSDKEnvironment {
*/
static WvPLDeviceInfo GetDeviceInfo(uint32_t system_id);
/**
* Set the provider key used for L3 CDM.
* |provider_key_config_bytes| is a serialized ProviderKeyConfig proto
* message. Returns OK if parsing is successful, otherwise an error is
* returned.
*/
virtual WvPLStatus SetProviderKeyConfig(
const std::string& provider_key_config_bytes);
/**
* Returns the provider key config used for L3 CDM.
*/
const video_widevine::ProviderKeyConfig& GetProviderKeyConfig() const {
return provider_key_config_;
}
protected:
// Return the signature for the provider specified in the |config_values|
// parameter in the constructor. |signature| is owned by the caller.
@@ -197,6 +214,8 @@ class WvPLSDKEnvironment {
// List of device system Ids to succeed even if the device is revoked.
std::vector<uint32_t> allowed_revoked_devices_
ABSL_GUARDED_BY(allowed_revoked_devices_mutex_);
// Provider key config used with L3 CDM.
video_widevine::ProviderKeyConfig provider_key_config_;
private:
// Get the expected service type for drm service certificate.
@@ -244,4 +263,4 @@ class WvPLSDKEnvironment {
} // namespace wv_pl_sdk
} // namespace video_widevine_server
#endif // VIDEO_WIDEVINE_EXPORT_SDK_EXTERNAL_COMMON_WVPL_WVPL_SDK_ENVIRONMENT_H_
#endif // VIDEO_WIDEVINE_EXPORT_SDK_EXTERNAL_CPP_WVPL_COMMON_WVPL_SDK_ENVIRONMENT_H_

View File

@@ -1,12 +1,12 @@
// Copyright 2018 Google LLC. All rights reserved.
#ifndef VIDEO_WIDEVINE_EXPORT_SDK_EXTERNAL_COMMON_WVPL_WVPL_SDK_SESSION_H_
#define VIDEO_WIDEVINE_EXPORT_SDK_EXTERNAL_COMMON_WVPL_WVPL_SDK_SESSION_H_
#ifndef VIDEO_WIDEVINE_EXPORT_SDK_EXTERNAL_CPP_WVPL_COMMON_WVPL_SDK_SESSION_H_
#define VIDEO_WIDEVINE_EXPORT_SDK_EXTERNAL_CPP_WVPL_COMMON_WVPL_SDK_SESSION_H_
#include <memory>
#include "common/security_profile_list.h"
#include "sdk/external/common/wvpl/wvpl_types.h"
#include "sdk/external/cpp/wvpl/common/wvpl_types.h"
#include "protos/public/device_certificate_status.pb.h"
namespace video_widevine {
@@ -219,6 +219,7 @@ class WvPLSDKSession {
WvPLRequestType request_type_;
bool has_session_state_ = false;
bool has_encrypted_client_id_ = false;
bool using_generated_content_id_ = false;
std::string provider_;
std::string provider_iv_;
std::string provider_key_;
@@ -331,6 +332,10 @@ class WvPLSDKSession {
DeviceStatus GetDeviceStatus(video_widevine::DeviceCertificateStatus::Status
device_certificate_status) const;
bool using_generated_content_id() const {
return using_generated_content_id_;
}
private:
std::unique_ptr<uint32_t> system_id_;
bool has_policy_ = false;
@@ -344,4 +349,4 @@ class WvPLSDKSession {
} // namespace wv_pl_sdk
} // namespace video_widevine_server
#endif // VIDEO_WIDEVINE_EXPORT_SDK_EXTERNAL_COMMON_WVPL_WVPL_SDK_SESSION_H_
#endif // VIDEO_WIDEVINE_EXPORT_SDK_EXTERNAL_CPP_WVPL_COMMON_WVPL_SDK_SESSION_H_

View File

@@ -1,7 +1,7 @@
// Copyright 2017 Google LLC. All rights reserved.
#ifndef VIDEO_WIDEVINE_EXPORT_SDK_EXTERNAL_COMMON_WVPL_WVPL_TYPES_H_
#define VIDEO_WIDEVINE_EXPORT_SDK_EXTERNAL_COMMON_WVPL_WVPL_TYPES_H_
#ifndef VIDEO_WIDEVINE_EXPORT_SDK_EXTERNAL_CPP_WVPL_COMMON_WVPL_TYPES_H_
#define VIDEO_WIDEVINE_EXPORT_SDK_EXTERNAL_CPP_WVPL_COMMON_WVPL_TYPES_H_
#include <stdint.h>
@@ -46,7 +46,9 @@ enum KeyType {
// for now.
PROVIDER_ECM_VERIFIER_PUBLIC_KEY = 4,
// Wrapped key for auxiliary crypto operations.
OPERATOR_SESSION = 5
OPERATOR_SESSION = 5,
// OEM-specified Entitlement key.
OEM_ENTITLEMENT = 6
};
// A shortcut for specifying whether to return keys for the video feature only
@@ -66,7 +68,7 @@ enum VideoFeatureKeySet {
// not associated with the video feature (e.g., SDR keys).
VF_INCLUDED = 3,
};
// LINT.ThenChange(//depot/google3/license_server_sdk/external/wvpl/java/com/google/video/widevine/sdk/wvpl/WvPLVideoFeatureKeySet.java)
// LINT.ThenChange(//depot/google3/sdk/external/java/com/google/video/widevine/wvpl/license/WvPLVideoFeatureKeySet.java)
// LINT.IfChange
enum LicenseType {
@@ -75,7 +77,7 @@ enum LicenseType {
OFFLINE = 2,
AUTOMATIC = 3,
};
// LINT.ThenChange(//depot/google3/license_server_sdk/external/wvpl/java/com/google/video/widevine/sdk/wvpl/WvPLLicenseType.java)
// LINT.ThenChange(//depot/google3/sdk/external/java/com/google/video/widevine/wvpl/license/WvPLLicenseType.java)
/**
* Represents the type of message. This struct is used by WvPL License SDK,
@@ -88,7 +90,7 @@ enum MessageType {
SERVICE_CERTIFICATE_REQUEST = 4,
EXTERNAL_LICENSE_REQUEST = 9
};
// LINT.ThenChange(//depot/google3/license_server_sdk/external/wvpl/java/com/google/video/widevine/sdk/wvpl/WvPLMessageType.java)
// LINT.ThenChange(//depot/google3/sdk/external/java/com/google/video/widevine/wvpl/license/WvPLMessageType.java)
enum CertificateKeyType {
RSA_2048 = 0,
@@ -121,7 +123,7 @@ enum HDCP {
HDCP_V2_3 = 5,
HDCP_NO_DIGITAL_OUTPUT = 0xff
};
// LINT.ThenChange(//depot/google3/license_server_sdk/external/wvpl/java/com/google/video/widevine/sdk/wvpl/WvPLHdcp.java)
// LINT.ThenChange(//depot/google3/sdk/external/java/com/google/video/widevine/wvpl/license/WvPLHdcp.java)
enum Platform {
PLATFORM_UNSPECIFIED = 0,
@@ -234,6 +236,17 @@ enum DeviceState {
REVOKED_LICENSING = 7,
};
// Client-side watermarking restrictions for the license.
enum WatermarkingControl {
// Watermarking may or may not be used, provider does not care.
WATERMARKING_CONTROL_UNSPECIFIED = 0,
// Watermarking must not be used. The device must disable watermarking
// if it supports it.
WATERMARKING_FORBIDDEN = 1,
// Watermarking is required if the device supports it.
WATERMARKING_REQUIRED = 2,
};
/*
* Defines the type wrapper for wvpl request.
*/
@@ -288,6 +301,7 @@ class WvPLPlaybackPolicy {
soft_enforce_rental_duration_ = true;
always_include_client_id_ = false;
renew_with_usage_ = false;
watermarking_control_ = WATERMARKING_CONTROL_UNSPECIFIED;
}
void set_license_duration_seconds(int64_t duration) {
@@ -352,6 +366,12 @@ class WvPLPlaybackPolicy {
bool soft_enforce_rental_duration() const {
return soft_enforce_rental_duration_;
}
void set_watermarking_control(WatermarkingControl watermarking_control) {
watermarking_control_ = watermarking_control;
}
WatermarkingControl watermarking_control() const {
return watermarking_control_;
}
private:
// The license window. Once a license is granted, the number of seconds to use
@@ -415,6 +435,9 @@ class WvPLPlaybackPolicy {
// indicates to client that RENEWAL and RELEASE requests should include
// Clientidentification.
bool always_include_client_id_;
// Optional requirement to indicate watermarking is allowed.
WatermarkingControl watermarking_control_;
};
/**
@@ -703,8 +726,9 @@ class WvPLKey {
const std::string& key_bytes() const { return key_bytes_; }
// |wrapping_key| must be specified in bytes. This is to be specified only
// when 'key_type' is OEM_CONTENT. This key will be used to 'wrap' or
// 'encrypt' the Widevine-generated key control block in the license.
// when 'key_type' is OEM_CONTENT or OEM_ENTITLEMENT. This key will be used
// to 'wrap' or 'encrypt' the Widevine-generated key control block in the
// license.
void set_wrapping_key(const std::string& wrapping_key) {
wrapping_key_ = wrapping_key;
}
@@ -902,7 +926,7 @@ struct WvPLLicenseStatusCounterData {
// Initialize members
WvPLLicenseStatusCounterData() : license_status_(0), status_count_(0) {}
uint32_t license_status_;
// Count of occurences of the above status;
// Count of occurrences of the above status;
uint32_t status_count_;
};
@@ -1779,4 +1803,4 @@ class WvPLSecurityProfile {
} // namespace wv_pl_sdk
} // namespace video_widevine_server
#endif // VIDEO_WIDEVINE_EXPORT_SDK_EXTERNAL_COMMON_WVPL_WVPL_TYPES_H_
#endif // VIDEO_WIDEVINE_EXPORT_SDK_EXTERNAL_CPP_WVPL_COMMON_WVPL_TYPES_H_

View File

@@ -0,0 +1,176 @@
// Copyright 2017 Google LLC. All rights reserved.
#ifndef VIDEO_WIDEVINE_EXPORT_SDK_EXTERNAL_CPP_WVPL_LICENSE_SERVER_SDK_WVPL_ENVIRONMENT_H_
#define VIDEO_WIDEVINE_EXPORT_SDK_EXTERNAL_CPP_WVPL_LICENSE_SERVER_SDK_WVPL_ENVIRONMENT_H_
#include <map>
#include <memory>
#include <string>
#include "sdk/external/cpp/wvdrm/license_server_sdk/environment.h"
#include "sdk/external/cpp/wvpl/common/wvpl_sdk_environment.h"
#include "sdk/external/cpp/wvpl/common/wvpl_types.h"
// TODO(yawenyu): Use generateSignature function in WvPLSDKEnvironement instead
// of get GetSignature.
namespace video_widevine_server {
namespace wv_pl_sdk {
using video_widevine::Environment;
class WvPLLicenseCounter;
class WvPLSession;
// Generate a Widevine environment object one time.  The WvPLEnvironment object
// is used to create widevine sessions.  WvPLEnvironment is used to hold data
// and spans the lifetime of a session, therefore it should be retained by
// the caller.  Sessions will not get generated if the initial call to
// SetDeviceCertificateStatusList() is not successful.
//
// Example:
// map<string, string> config_values;
// config_values.insert(std::make_pair(kAllowUnknownDevice, "true"));
// config_values.insert(std::make_pair(kProvider, "<Provider Name>"));
// config_values.insert(std::make_pair(kProviderIv, "<Provider IV>"));
// config_values.insert(std::make_pair(kProviderKey, "<Provider Key>"));
// WvPLEnvironment* wvpl_environment = new WvPLEnvironment(config_values);
// std::string error_str;
// std::string cert_status_list;
// std::string service_cert;
// WVStatus status;
// status = wvpl_environment->Initialize();
// wvpl_environment->SetPreProvisioningKeys(prev_prov_keys);
// status = wvpl_environment->SetDrmServiceCertificate(
// "<Provider service cert>", "<Service Private Key>",
// "<Service Private Key Passphrase>");
// -- Fetch the intermediate certificates.
// SomeFunctionToFetchTheCertificates(&cert_status_list);
// status =
// wvpl_environment->SetDeviceCertificateStatusList(cert_status_list);
class WvPLEnvironment : public WvPLSDKEnvironment {
public:
// Copies the config_values and constructs a new WvPLEnvironment object.
// TODO(hali): Add all config parameters.
explicit WvPLEnvironment(
const std::map<std::string, std::string>& config_values);
virtual ~WvPLEnvironment();
// TODO(b/194740480): This function will be changed to private since end-Q3
// 2021 release.
// One time initialization. Must be called once after construction.
virtual WvPLStatus Initialize();
// Create a new Session object. Upon success, |session| points to a new
// object created on the heap. Caller retains ownership of the session.
// Example usage:
// WvPLSession* session;
// WvPLStatus status = wvpl_environment->CreateSession(request_from_client,
// &session);
// if (!status.ok()) {
// std::string error_license;
// if (wvpl_environment->GenerateErrorResponse(status, &error_license)) {
// // Send error_license to the client.
// } else {
// // Handle error
// }
// return ...
// }
// // Continue with license flow, invoke GenerateLicense(), etc.
virtual WvPLStatus CreateSession(const std::string& request,
WvPLSession** session) const;
virtual WvPLStatus CreateSessionWithOptions(
const std::string& request, const WvPLSessionCreateOptions& options,
WvPLSession** session) const;
// Deletes |session|. Should be called if CreateSession() was successful and
// the session is no longer needed.
virtual void DestroySession(WvPLSession* session) const;
// Thread-safe call to set the pre-provisioning keys. Map key is the
// system id, value. Value should be human-readable hex digits.
virtual WvPLStatus SetPreProvisioningKeys(
const std::map<uint32_t, std::string>& keys);
// Return the license counter data as bytes. The bytes in
// |signed_license_stats| are binary. The internal data is flushed
// if |flush_data| is true. If |flush_data| is false, license counters will
// accumulate. If this call returns "Status::OK", |signed_license_stats| is
// populated. |signed_license_stats| is owned by the caller.
virtual WvPLStatus GetStatsAsBytes(bool flush_data,
std::string* signed_license_stats);
// TODO(b/193921143): flush_data parameter will be removed since end-Q3 2021
// release.
// Return the license counter data in a human-readable format. The
// internal data is flushed if |flush_data| is true. If |flush_data| is
// false, license counters will accumulate. If this call returns
// "Status::OK", |license_stats| is populated. |license_stats| is owned by
// the caller.
virtual WvPLStatus GetStatsAsString(bool flush_data,
std::string* license_stats);
// Deprecated.
// This API will be replaced by
// WvPLSDKEnvironment::SetDeviceCertificateStatusList(). It will be removed
// from this header file in the mid-Q3 2021 SDK release.
// In order to stay up-to-date with new devices, it is recommended to fetch
// new certificates on a regular basis. The update interval should be once a
// day. If UpdateWithCerftificates() fails, the existing certificates are
// still valid, but are subject to expiration. The expiration is controlled
// by the content provider and configured as a parameter when creating
// WvPLEnvironment.
// |cert_list| is the response provided from the Widevine API
// that produces the certificate list. The method can handle either the new
// API format (the serialized PublishedDevices proto), or the legacy format (a
// JSON response containing the base64-encoded certificate list).
virtual WvPLStatus UpdateWithCertificates(const std::string& cert_list);
// Generate a signed request to be sent to Widevine Certificate Provisioning
// Server to retrieve 'DeviceCertificateStatusList'.
WvPLStatus GenerateDeviceStatusListRequest(
std::string* signed_device_certificate_status_list_request) override;
// TODO(b/193920802): This function will be changed to private function in
// mid-2021 Q3 release.
// Get the expected service type for drm service certificate. The expected
// value is LICENSE_SERVER_SDK.
int GetExpectedServiceCertificateType() override;
// Enable delivery of licenses to revoked client devices. |system_id_list| is
// a comma separated list of systems Ids to allow even if revoked.
void AllowRevokedDevices(const std::string& system_id_list) override;
private:
// Return the signature for the provider specified in the |config_values|
// parameter in the constructor. |signature| is owned by the caller.
virtual WvPLStatus GetSignature(const std::string& text_to_sign,
std::string* signature);
std::unique_ptr<WvPLLicenseCounter> license_counter_;
// list of system ids that support having the Crypto API version specified in
// the Key Control Block (KCB).
std::string system_ids_for_api_ver_in_kcb_;
// Comma separated list of system ids by make allowed to be TEST_ONLY.
std::string allow_test_only_by_make_;
// Comma separated list of system ids by provider allowed to be TEST_ONLY.
std::string allow_test_only_by_provider_;
// Whether all test devices should be allowed.
bool allow_development_clients_;
// Restriction on core message features. If this is an empty string, the
// default feature set is used. If it is an integer, that is the ODK version
// supported. This restricts the features that the server will support in an
// oemcrypto core message. For example, we may restrict the server to never
// send a v17 message by setting the std::string to "16". For details, please see
// common/oemcrypto_core_message/odk/include/core_message_features.h
std::string core_message_features_;
// A few functions from sdk/external/cpp/wvdrm/license_server_sdk/environment
// are used to run wvpl_environment.
std::unique_ptr<Environment> environment_;
};
} // namespace wv_pl_sdk
} // namespace video_widevine_server
#endif // VIDEO_WIDEVINE_EXPORT_SDK_EXTERNAL_CPP_WVPL_LICENSE_SERVER_SDK_WVPL_ENVIRONMENT_H_

View File

@@ -0,0 +1,119 @@
// Copyright 2017 Google LLC. All rights reserved.
#ifndef VIDEO_WIDEVINE_EXPORT_SDK_EXTERNAL_CPP_WVPL_LICENSE_SERVER_SDK_WVPL_SESSION_H_
#define VIDEO_WIDEVINE_EXPORT_SDK_EXTERNAL_CPP_WVPL_LICENSE_SERVER_SDK_WVPL_SESSION_H_
#include <stdint.h>
#include <string>
#include <vector>
#include "sdk/external/cpp/wvdrm/license_server_sdk/session.h"
#include "sdk/external/cpp/wvpl/common/wvpl_sdk_session.h"
#include "sdk/external/cpp/wvpl/common/wvpl_types.h"
#include "protos/public/errors.pb.h"
#include "protos/public/license_server_sdk.pb.h"
#include "protos/public/oem_key_container.pb.h"
#include "protos/public/playready.pb.h"
#include "protos/public/widevine_pssh.pb.h"
namespace video_widevine {
class Session;
} // namespace video_widevine
namespace video_widevine_server {
namespace wv_pl_sdk {
// Because we do not want to export wvpl_license_counter.h outside google3, add
// WvPLLicenseCounter here.
class WvPLLicenseCounter;
// major version to line up with latest released OEMCryptoAPI version.
const uint32_t kMajorVersion = 17;
const uint32_t kMinorVersion = 0;
const uint32_t kRelease = 1;
// Once a Widevine environment object is successfully initialized, generate a
// Widevine session object for each license request. CreateSession() parses
// the request and validates the request by verifying the signature. If
// successful, a session object is created and OK is returned.
// Once a Widevine session object is successfully created, setup the session
// object with the policy and keys. Call AddKey() multiple times for each key.
class WvPLSession : public WvPLSDKSession {
public:
WvPLSession();
~WvPLSession() override;
// Generates the license for sending back to the Widevine client. Caller owns
// |license|.
virtual WvPLStatus GenerateLicense(std::string* license);
// Set the session state.
virtual void set_session_state(const WvPLSessionState& wvpl_session_state) {
wvpl_session_state_ = wvpl_session_state;
has_session_state_ = true;
}
// Get the session state.
virtual const WvPLSessionState& session_state() const {
return wvpl_session_state_;
}
bool has_sdk_session() { return !(sdk_session_ == nullptr); }
PlatformVerificationStatus VerifyPlatform() override;
// Returns a std::string containing the WVPL version in the form:
// <major_version>.<minor_version>.<release>
static std::string GetVersionString();
// TODO(b/193921795): this API will be deprecated since end-Q3 2021 release.
// Please use GetDeviceInfo() instead.
// Returns true if a provisioned device info exists. Populates the specified
// |device_info| structure.
virtual bool GetProvisionedDeviceInfo(WvPLDeviceInfo* device_info) const;
// Populates the specified |device_info| structure. This API works only for
// * NEW license requests.
// * RENEWAL/RELEASE requests that include a Client Identification.
WvPLStatus GetDeviceInfo(WvPLDeviceInfo* device_info) const override;
protected:
// This class takes ownership of |sdk_session|. This class keeps a pointer
// to |license_counter| but the caller maintains ownership of
// |license_counter|. Both arguments must not be NULL.
WvPLSession(
const video_widevine::DrmRootCertificate* drm_root_certificate,
video_widevine::Session* sdk_session, WvPLLicenseCounter* license_counter,
const video_widevine::SecurityProfileList* device_security_profile_list);
video_widevine::Session* sdk_session() { return sdk_session_; }
void set_sdk_session(video_widevine::Session* sdk_session) {
sdk_session_ = sdk_session;
}
// Sets the license counter to use. The caller maintains ownership of
// |license_counter| but this class keeps a pointer to |license_counter|.
void set_license_counter(WvPLLicenseCounter* license_counter) {
license_counter_ = license_counter;
}
void CopyOemKey(const WvPLKey& wvpl_key,
video_widevine::OemKeyContainer* oem_key_container);
private:
friend class WvPLEnvironment;
friend class WvPLEnvironmentTest;
friend class WvPLSessionTest;
video_widevine::Session* sdk_session_ = nullptr;
WvPLLicenseCounter* license_counter_ = nullptr;
WvPLSessionState wvpl_session_state_;
video_widevine::SessionState session_state_;
};
} // namespace wv_pl_sdk
} // namespace video_widevine_server
#endif // VIDEO_WIDEVINE_EXPORT_SDK_EXTERNAL_CPP_WVPL_LICENSE_SERVER_SDK_WVPL_SESSION_H_