Widevine SDK Release Branch: beta-19.10.1
This commit is contained in:
316
ubuntu_arm64/sdk/examples/license_server_sdk/cpp/wvpl_license_sdk_example.cc
Executable file
316
ubuntu_arm64/sdk/examples/license_server_sdk/cpp/wvpl_license_sdk_example.cc
Executable file
@@ -0,0 +1,316 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Copyright 2018 Google LLC
|
||||
////
|
||||
//// This software is licensed under the terms defined in the Widevine Master
|
||||
//// License Agreement. For a copy of this agreement, please contact
|
||||
//// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "sdk/external/cpp/wvpl/common/wvpl_sdk_environment.h"
|
||||
#include "sdk/external/cpp/wvpl/common/wvpl_types.h"
|
||||
#include "sdk/external/cpp/wvpl/license_server_sdk/wvpl_environment.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::WvPLCapabilityStatus;
|
||||
using ::video_widevine_server::wv_pl_sdk::WvPLDeviceStatusOptions;
|
||||
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::WvPLSessionInit;
|
||||
using ::video_widevine_server::wv_pl_sdk::WvPLStatus;
|
||||
|
||||
constexpr uint32_t kSystemId = 0x112;
|
||||
constexpr char kKeyBytes1[] =
|
||||
"\x69\xea\xa8\x02\xa6\x76\x3a\xf9\x79\xe8\xd1\x94\x0f\xb8\x83\x92";
|
||||
constexpr char kKeyId1[] =
|
||||
"\xab\xba\x27\x1e\x8b\xcf\x55\x2b\xbd\x2e\x86\xa4\x34\xa9\xa5\xd9";
|
||||
constexpr char kKeyBytes2[] =
|
||||
"\xa4\x63\x1a\x15\x3a\x44\x3d\xf9\xee\xd0\x59\x30\x43\xdb\x75\x19";
|
||||
constexpr char kKeyId2[] =
|
||||
"\xf3\xc5\xe0\x36\x1e\x66\x54\xb2\x8f\x80\x49\xc7\x78\xb2\x39\x46";
|
||||
constexpr char kKeyId3[] =
|
||||
"\x10\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10";
|
||||
constexpr char kKeyBytes3[] =
|
||||
"\x10\x20\x30\x40\x50\x60\x70\x80\x90\xa0\xb0\xc0\xd0\xe0\xf0\x10";
|
||||
|
||||
constexpr char kPreProvisioningKey[] = "f7538b38acc78ec68c732ac665c55c65";
|
||||
constexpr 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 the value.
|
||||
std::map<uint32_t, std::string> prev_prov_keys;
|
||||
prev_prov_keys[kSystemId] = kPreProvisioningKey;
|
||||
status = wvpl_env.SetPreProvisioningKeys(prev_prov_keys);
|
||||
if (!status.ok()) {
|
||||
std::cerr << "Failed to set pre-prov keys: " << status.error_message()
|
||||
<< std::endl;
|
||||
return status.error_code();
|
||||
}
|
||||
|
||||
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 b64EncryptedProviderKeyConfig =
|
||||
"ChJoYW1pZC13dnBsLXNkay11YXQSENU2hMYAMrDf+Z9TlWepj1Ua0AHd4Ov+L/"
|
||||
"2wwOhiCSmULAExqAav8E8Ori0F2/"
|
||||
"fIjdP4dCU+1M5EGJgMQhoyrYz9VFpUpFm9nFZdMhcafHjcKkN4NUVeH24xbJDPIN2+"
|
||||
"B9TyRmqVYR+2iIqrHnIRwd8jJMOy8X6tS3hi1fJk7JFYRnPHPhkiUANkV/"
|
||||
"4pAZINquNkFnoswt7oo40dFv+ztgrIbkofXdW/"
|
||||
"COJYZKWdVM27RFWk0VxSTglYMVPkXTNSwhR+i6DpB11ZwAFXw2ndiFYd3Skx47knYGobNW/"
|
||||
"kUV7azqnUIhDdntgweqt2QSyUFYa4c99MKoACBTGaIdLXmRh6sVRhQ4f6pYWEK+b+Kf/"
|
||||
"9cGItNmH4Y+3Pzpr61wmV8b8NIju2ztRuOR+gzQwyWWoqZh3otqnpd6Fds4Bs+4+"
|
||||
"FQRLOTSBoV9d6JFKvokLtCqFDPWTHVZnKX9PKcjng6MNwGjwMlYELam7qWwXElXKjvBZGNEx"
|
||||
"INA5v97xF+"
|
||||
"CCCXysVAxC8MQmRQA9rDBhroN2akkrk1qau4L7Nb3T8oKIGEfiwHcENP2TlgDzmRK9W//"
|
||||
"+tFnZLJ1e12L1Vd390pV4G9cLOrTkF368LZ4lykOHeIQ1jgvUjuGk0uEwzi+"
|
||||
"hHDyA7WtbtpUXbteq8ytD9MUvIOGVyHwRnzw==";
|
||||
|
||||
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 b64DecodedEncryptedProviderKeyConfig;
|
||||
std::string b64DecodedPrivateKey;
|
||||
if (!absl::Base64Unescape(b64DrmServiceCertificate,
|
||||
&b64DecodedDrmServiceCertificate)) {
|
||||
std::cerr << "Failed to decode ServiceCertificate." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
if (!absl::Base64Unescape(b64EncryptedProviderKeyConfig,
|
||||
&b64DecodedEncryptedProviderKeyConfig)) {
|
||||
std::cerr << "Failed to decode EncryptedProviderKeyConfig." << 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();
|
||||
}
|
||||
|
||||
// If using provider keys: set an encrypted provider key config. Note that the
|
||||
// service certificate has to be set first.
|
||||
status = wvpl_env.SetEncryptedProviderKeyConfig(
|
||||
b64DecodedEncryptedProviderKeyConfig);
|
||||
if (!status.ok()) {
|
||||
std::cerr << "Failed to set an encrypted provider key config: "
|
||||
<< 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();
|
||||
}
|
||||
|
||||
// We also could use filterKey to filter out the key.
|
||||
WvPLKey filtered_key1;
|
||||
filtered_key1.set_key_id(kKeyId3);
|
||||
filtered_key1.set_key_bytes(kKeyBytes3);
|
||||
filtered_key1.set_track_type(VIDEO_HD);
|
||||
WvPLCapabilityStatus capability_status = session->FilterKey(filtered_key1);
|
||||
if (!capability_status.ok()) {
|
||||
std::cerr << "Failed to add filtered key: " << capability_status.status()
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
// Set SessionInit
|
||||
WvPLDeviceStatusOptions wvpl_device_status_options;
|
||||
wvpl_device_status_options.set_allow_test_only_system_id(true);
|
||||
WvPLSessionInit wvpl_session_init;
|
||||
wvpl_session_init.set_device_status_options(wvpl_device_status_options);
|
||||
session->set_session_init(wvpl_session_init);
|
||||
|
||||
// -- 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;
|
||||
}
|
||||
@@ -0,0 +1,441 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Copyright 2021 Google LLC
|
||||
////
|
||||
//// This software is licensed under the terms defined in the Widevine Master
|
||||
//// License Agreement. For a copy of this agreement, please contact
|
||||
//// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "common/status.h"
|
||||
#include "sdk/external/cpp/wvpl/common/wvpl_sdk_environment.h"
|
||||
#include "sdk/external/cpp/wvpl/common/wvpl_types.h"
|
||||
#include "sdk/external/cpp/wvpl/license_server_sdk/wvpl_environment.h"
|
||||
#include "sdk/external/cpp/wvpl/license_server_sdk/wvpl_session.h"
|
||||
|
||||
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_HD;
|
||||
using ::video_widevine_server::wv_pl_sdk::VIDEO_SD;
|
||||
using ::video_widevine_server::wv_pl_sdk::WvPLCapabilityStatus;
|
||||
using ::video_widevine_server::wv_pl_sdk::WvPLDeviceStatusOptions;
|
||||
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::WvPLSessionInit;
|
||||
using ::video_widevine_server::wv_pl_sdk::WvPLStatus;
|
||||
|
||||
constexpr uint32_t kSystemId = 0x112;
|
||||
constexpr char kKeyBytes1[] =
|
||||
"\x69\xea\xa8\x02\xa6\x76\x3a\xf9\x79\xe8\xd1\x94\x0f\xb8\x83\x92";
|
||||
constexpr char kKeyId1[] =
|
||||
"\xab\xba\x27\x1e\x8b\xcf\x55\x2b\xbd\x2e\x86\xa4\x34\xa9\xa5\xd9";
|
||||
constexpr char kKeyBytes2[] =
|
||||
"\xa4\x63\x1a\x15\x3a\x44\x3d\xf9\xee\xd0\x59\x30\x43\xdb\x75\x19";
|
||||
constexpr char kKeyId2[] =
|
||||
"\xf3\xc5\xe0\x36\x1e\x66\x54\xb2\x8f\x80\x49\xc7\x78\xb2\x39\x46";
|
||||
constexpr char kKeyId3[] =
|
||||
"\x10\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10";
|
||||
constexpr char kKeyBytes3[] =
|
||||
"\x10\x20\x30\x40\x50\x60\x70\x80\x90\xa0\xb0\xc0\xd0\xe0\xf0\x10";
|
||||
constexpr char kPreProvisioningKey[] = "f7538b38acc78ec68c732ac665c55c65";
|
||||
constexpr 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"
|
||||
"\" }";
|
||||
constexpr 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=";
|
||||
|
||||
constexpr 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=";
|
||||
constexpr 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";
|
||||
constexpr 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, absl::string_view content_owner,
|
||||
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);
|
||||
|
||||
// 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);
|
||||
|
||||
// Optional. Check the qualified custom profiles for the content owner.
|
||||
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() && !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;
|
||||
}
|
||||
}
|
||||
// Set key parameters.
|
||||
// Content keys could be 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 `wvpl_key1` could be
|
||||
// generated based on qualified custom profiles.
|
||||
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);
|
||||
status = session.AddKey(wvpl_key1);
|
||||
if (!status.ok()) {
|
||||
std::cerr << "Failed to add key: " << status.error_message() << std::endl;
|
||||
return status.error_code();
|
||||
}
|
||||
|
||||
// Or we could call FilterKey() to filter out the keys. Keys could be inserted
|
||||
// with one security profile as required profile, as well as content owner. In
|
||||
// Filterkey() API it already contains the logic of checking the qualified
|
||||
// security profiles for the content owner.
|
||||
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);
|
||||
wvpl_key2.set_required_profile("dsp_test");
|
||||
wvpl_key2.set_content_owner(content_owner);
|
||||
WvPLCapabilityStatus wvpl_capability_status2 = session.FilterKey(wvpl_key2);
|
||||
if (!wvpl_capability_status2.ok()) {
|
||||
std::cerr << "Failed to add key: " << wvpl_key2.key_id()
|
||||
<< " with status:" << wvpl_capability_status2.status()
|
||||
<< std::endl;
|
||||
}
|
||||
// The following example `wvpl_key3` will be filtered out because the profile
|
||||
// name the key associated with is not qualified.
|
||||
WvPLKey wvpl_key3;
|
||||
wvpl_key3.set_key_id(kKeyId3);
|
||||
wvpl_key3.set_key_bytes(kKeyBytes3);
|
||||
wvpl_key3.set_track_type(VIDEO_HD);
|
||||
wvpl_key3.mutable_output_protection()->set_hdcp(HDCP_NONE);
|
||||
wvpl_key3.mutable_output_protection()->set_secure_data_path(true);
|
||||
wvpl_key3.set_required_profile("L3");
|
||||
wvpl_key3.set_content_owner(content_owner);
|
||||
WvPLCapabilityStatus wvpl_capability_status1 = session.FilterKey(wvpl_key3);
|
||||
if (!wvpl_capability_status1.ok()) {
|
||||
std::cerr << "Failed to add key: " << wvpl_capability_status1.status()
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
/** 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;
|
||||
video_widevine::Status wv_status =
|
||||
wvpl_env.SetPreProvisioningKeys(prev_prov_keys);
|
||||
if (!wv_status.ok()) {
|
||||
std::cerr << "Failed to set pre prov keys with stats: " << wv_status
|
||||
<< std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
// Set SessionInit
|
||||
WvPLDeviceStatusOptions wvpl_device_status_options;
|
||||
wvpl_device_status_options.set_allow_test_only_system_id(true);
|
||||
WvPLSessionInit wvpl_session_init;
|
||||
wvpl_session_init.set_device_status_options(wvpl_device_status_options);
|
||||
session->set_session_init(wvpl_session_init);
|
||||
|
||||
// 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);
|
||||
}
|
||||
496
ubuntu_arm64/sdk/examples/license_server_sdk/java/WvPLExample.java
Executable file
496
ubuntu_arm64/sdk/examples/license_server_sdk/java/WvPLExample.java
Executable file
@@ -0,0 +1,496 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Copyright 2018 Google LLC
|
||||
////
|
||||
//// This software is licensed under the terms defined in the Widevine Master
|
||||
//// License Agreement. For a copy of this agreement, please contact
|
||||
//// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
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.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.Base64;
|
||||
import java.util.Map;
|
||||
|
||||
/** Tests for {@link WvPLSession} Java API in Widevine License Server SDK. */
|
||||
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);
|
||||
|
||||
// We also could use filterKey to filter out the key.
|
||||
WvPLKey filteredKey1 = new WvPLKey();
|
||||
byte[] filteredKeyData1 = {10, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
|
||||
filteredKey1.setKeyId(filteredKeyData1);
|
||||
filteredKey1.setKeyBytes(filteredKeyData1);
|
||||
filteredKey1.setTrackType(WvPLTrackType.TrackType.VIDEO_HD);
|
||||
WvPLCapabilityStatus capabilityStatus = session.filterKey(filteredKey1);
|
||||
System.out.println("WvPLCapabilityStatus for adding filtered key = " + capabilityStatus);
|
||||
|
||||
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(Path.of(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 b64EncryptedProviderKeyConfig =
|
||||
"ChlhdHMud3Yuc2FuZGJveC5nb29nbGUuY29tEhCKyjwi9zzm4e7lLD6IB9W8GtAB_ap4K2QYS3tx"
|
||||
+ "7Xj8siqnpONGJOfEBR5yJs4FxHBVRfVLw2_lvz4EavH0q5WjXFcODJyGjR-jTzhsoM_z4Qpi2rpp"
|
||||
+ "K1_TPv82glRJbmU9aKQwMa6GldCjjvJqhVJzcVRVhx19Y2b7stOQb5i4i9CHYWn4C4fjSXbkaaYl"
|
||||
+ "pYISNd0MbVrRK53vvyGWs8v9v6s-CsqrTJC7--gsn2fjNJfOTMN3Z_9pZzemoyCpdOWhqWChfjv0"
|
||||
+ "4e1PGHjNroGQOjxtNI0GAzi_e65NZcUHF1aDciIQk2NXF2Btbu59TB657lNEmSqAAl55dLmocO6O"
|
||||
+ "NLL-swezkDB6jdCjjxBehVbfLkIRM4M0MjXvyX9i78yeDbqF1FA9tdGv2brzNNgpecXxE8cuPWSi"
|
||||
+ "ED-Ml5-Pwt0Pcwnj8Z1o5lj4Is0J6BTeR8Mqs_VLzLNVc5YKsqNWA-Ad7WMDt5oasP54m-T5FhCS"
|
||||
+ "_XSd45fQG2QSgdv7niei_ZHWsVYWkIQVMbGw7Wxe33o3E5LwNZW9LQ0uMD4m_zdKhh-Tk8kvADWP"
|
||||
+ "b7YAnWZGhYWKm_5992weJ0BgD7ADjPvpO8NMX2mi-Np1RAbM92r9UMpxKYAsTTA1xz1H_HGrxvBd"
|
||||
+ "VB17Ji8nR8UrQmQokBPPAoi4iDs=";
|
||||
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[] decodedEncryptedProviderKeyConfig =
|
||||
Base64.getUrlDecoder().decode(b64EncryptedProviderKeyConfig);
|
||||
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");
|
||||
}
|
||||
// If using provider keys: set an encrypted provider key config. Note that the
|
||||
// service certificate has to be set first.
|
||||
status = env.setEncryptedProviderKeyConfig(decodedEncryptedProviderKeyConfig);
|
||||
if (status.getStatusCode() != WvPLStatus.StatusCode.OK) {
|
||||
System.out.println("setEncryptedProviderKeyConfig status = " + status.getStatusCode()
|
||||
+ ", message = " + status.getMessage());
|
||||
} else {
|
||||
System.out.println("Successfully set provider key config");
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
600
ubuntu_arm64/sdk/examples/license_server_sdk/java/WvPLUsingDspExample.java
Executable file
600
ubuntu_arm64/sdk/examples/license_server_sdk/java/WvPLUsingDspExample.java
Executable file
@@ -0,0 +1,600 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Copyright 2021 Google LLC
|
||||
////
|
||||
//// This software is licensed under the terms defined in the Widevine Master
|
||||
//// License Agreement. For a copy of this agreement, please contact
|
||||
//// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
package com.google.video.widevine.sdk.wvpl;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.google.protobuf.ExtensionRegistry;
|
||||
|
||||
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.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Base64;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/** Tests for {@link WvPLSession} Java API in Widevine License Server SDK. */
|
||||
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());
|
||||
}
|
||||
// Set parameters on the created session.
|
||||
// Set policy parameters.
|
||||
WvPLPlaybackPolicy policy = new WvPLPlaybackPolicy();
|
||||
policy.setLicenseDurationSeconds(10000000L);
|
||||
session.setPolicy(policy);
|
||||
|
||||
// Optional. Check the qualified custom profiles for the content owner.
|
||||
List<String> qualifiedProfileNames;
|
||||
WvPLStatus status;
|
||||
if (customProfileOwners.contains(contentOwner)) {
|
||||
try {
|
||||
qualifiedProfileNames = session.getQualifiedCustomDeviceSecurityProfiles(contentOwner);
|
||||
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();
|
||||
}
|
||||
} catch (WvPLStatusException e) {
|
||||
status = e.getStatus();
|
||||
System.out.println(
|
||||
"Failed to get qualifying custom profiles: "
|
||||
+ status.getStatusCode()
|
||||
+ ", message: "
|
||||
+ status.getMessage()
|
||||
+ " for owner: <"
|
||||
+ contentOwner
|
||||
+ ">");
|
||||
}
|
||||
}
|
||||
// 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 `key1` and `key2` are generated based on
|
||||
// qualified custom profiles. Those keys would support SD and HD content.
|
||||
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);
|
||||
|
||||
// Or we could call filterKey() to filter out the keys. Keys could be inserted
|
||||
// with one security profile as required profile, as well as content owner. In
|
||||
// filterkey() API it already contains the logic of checking the qualified security
|
||||
// profiles for the content owner.
|
||||
WvPLKey key3 = new WvPLKey();
|
||||
byte[] data3 = {4, 4, 4, 4, 4, 4, 4, 4, 4, 10, 11, 12, 13, 14, 15, 16};
|
||||
key3.setKeyBytes(data3);
|
||||
key3.setTrackType(WvPLTrackType.TrackType.VIDEO_HD);
|
||||
outputProtection.setHdcp(WvPLHdcp.HDCP.HDCP_NONE);
|
||||
outputProtection.setSecuredataPath(true);
|
||||
key3.setOutputProtection(outputProtection);
|
||||
String requiredProfileName = "L1";
|
||||
key3.setRequiredProfile(requiredProfileName.getBytes(UTF_8));
|
||||
key3.setContentOwner(contentOwner.getBytes(UTF_8));
|
||||
WvPLCapabilityStatus capabilityStatus = session.filterKey(key3);
|
||||
if (capabilityStatus.getStatus()
|
||||
!= WvPLCapabilityStatus.DeviceCapabilityStatus.CAPABILITY_OK) {
|
||||
System.out.println(
|
||||
"Failed to filter key: "
|
||||
+ capabilityStatus.getStatus()
|
||||
+ " for owner: <"
|
||||
+ contentOwner
|
||||
+ ">");
|
||||
}
|
||||
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, ExtensionRegistry.getGeneratedRegistry());
|
||||
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(Path.of(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 b64EncryptedProviderKeyConfig =
|
||||
"ChlhdHMud3Yuc2FuZGJveC5nb29nbGUuY29tEhCKyjwi9zzm4e7lLD6IB9W8GtAB_ap4K2QYS3tx"
|
||||
+ "7Xj8siqnpONGJOfEBR5yJs4FxHBVRfVLw2_lvz4EavH0q5WjXFcODJyGjR-jTzhsoM_z4Qpi2rpp"
|
||||
+ "K1_TPv82glRJbmU9aKQwMa6GldCjjvJqhVJzcVRVhx19Y2b7stOQb5i4i9CHYWn4C4fjSXbkaaYl"
|
||||
+ "pYISNd0MbVrRK53vvyGWs8v9v6s-CsqrTJC7--gsn2fjNJfOTMN3Z_9pZzemoyCpdOWhqWChfjv0"
|
||||
+ "4e1PGHjNroGQOjxtNI0GAzi_e65NZcUHF1aDciIQk2NXF2Btbu59TB657lNEmSqAAl55dLmocO6O"
|
||||
+ "NLL-swezkDB6jdCjjxBehVbfLkIRM4M0MjXvyX9i78yeDbqF1FA9tdGv2brzNNgpecXxE8cuPWSi"
|
||||
+ "ED-Ml5-Pwt0Pcwnj8Z1o5lj4Is0J6BTeR8Mqs_VLzLNVc5YKsqNWA-Ad7WMDt5oasP54m-T5FhCS"
|
||||
+ "_XSd45fQG2QSgdv7niei_ZHWsVYWkIQVMbGw7Wxe33o3E5LwNZW9LQ0uMD4m_zdKhh-Tk8kvADWP"
|
||||
+ "b7YAnWZGhYWKm_5992weJ0BgD7ADjPvpO8NMX2mi-Np1RAbM92r9UMpxKYAsTTA1xz1H_HGrxvBd"
|
||||
+ "VB17Ji8nR8UrQmQokBPPAoi4iDs=";
|
||||
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[] decodedEncryptedProviderKeyConfig =
|
||||
Base64.getUrlDecoder().decode(b64EncryptedProviderKeyConfig);
|
||||
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");
|
||||
}
|
||||
|
||||
// If using provider keys: set an encrypted provider key config. Note that the
|
||||
// service certificate has to be set first.
|
||||
status = env.setEncryptedProviderKeyConfig(decodedEncryptedProviderKeyConfig);
|
||||
if (status.getStatusCode() != WvPLStatus.StatusCode.OK) {
|
||||
System.out.println("setProviderKeyConfig status = " + status.getStatusCode()
|
||||
+ ", message = " + status.getMessage());
|
||||
} else {
|
||||
System.out.println("Successfully set provider key config");
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user