GetDeviceInformation() and GetDeviceSignedCsrPayload() are added to cdm_engine and crypto_session, so that they can be queried by DRM plugin. This is to allow the wv drm HAL to be able to extract BCC and CSR payload to build CSR for prov 4 device registration, such that we don't need a separate RKP HAL to do this job. Changes to the DRM plugin to use the exposed methods will be in the coming CL. Bug: 286556950 Test: request_license_test Merged from https://widevine-internal-review.googlesource.com/178890 Merged from https://widevine-internal-review.googlesource.com/179730 Change-Id: Ibafa3a58c99fbb8f1f25f8951d3749110bd32176
6971 lines
296 KiB
C++
6971 lines
296 KiB
C++
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
|
|
// source code may only be used and distributed under the Widevine License
|
|
// Agreement.
|
|
|
|
#include <android-base/properties.h>
|
|
#include <errno.h>
|
|
#include <gmock/gmock.h>
|
|
#include <gtest/gtest.h>
|
|
#include <time.h>
|
|
|
|
#include <chrono>
|
|
#include <sstream>
|
|
#include <thread>
|
|
#include <utility>
|
|
|
|
#include "OEMCryptoCENC.h"
|
|
#include "arraysize.h"
|
|
#include "cdm_identifier.h"
|
|
#include "config_test_env.h"
|
|
#include "crypto_wrapped_key.h"
|
|
#include "device_files.h"
|
|
#include "device_files.pb.h"
|
|
#include "file_store.h"
|
|
#include "file_utils.h"
|
|
#include "license_protocol.pb.h"
|
|
#include "license_request.h"
|
|
#include "log.h"
|
|
#include "oemcrypto_adapter.h"
|
|
#include "properties.h"
|
|
#include "string_conversions.h"
|
|
#include "test_base.h"
|
|
#include "test_printers.h"
|
|
#include "url_request.h"
|
|
#include "wv_cdm_constants.h"
|
|
#include "wv_cdm_event_listener.h"
|
|
#include "wv_content_decryption_module.h"
|
|
|
|
using ::testing::_;
|
|
using ::testing::AllOf;
|
|
using ::testing::AtLeast;
|
|
using ::testing::Contains;
|
|
using ::testing::Each;
|
|
using ::testing::Invoke;
|
|
using ::testing::IsEmpty;
|
|
using ::testing::Not;
|
|
using ::testing::Pair;
|
|
using ::testing::StrictMock;
|
|
using ::testing::UnorderedElementsAreArray;
|
|
using wvutil::FileSystem;
|
|
|
|
namespace {
|
|
|
|
// HTTP response codes.
|
|
const int kHttpOk = 200;
|
|
|
|
// For tests checking functionality of expiring.
|
|
constexpr uint32_t kDrmCertificateExpiryPeriod = 120;
|
|
constexpr uint32_t kDrmCertificateExpirySleepPeriod =
|
|
kDrmCertificateExpiryPeriod + 30;
|
|
|
|
const wvcdm::CdmIdentifier kExampleIdentifier = {
|
|
wvcdm::EMPTY_SPOID, "com.example", "com.example", 7, 9};
|
|
|
|
const wvcdm::CdmIdentifier kAlternateCdmIdentifier1 = {
|
|
"alternate_spoid_1", "alternate_origin_1", "com.alternate1.url", 8, 10};
|
|
|
|
const wvcdm::CdmIdentifier kAlternateCdmIdentifier2 = {
|
|
"alternate_spoid_2", "" /* empty origin */, "com.alternate2.url", 9, 11};
|
|
|
|
const std::string kEmptyServiceCertificate;
|
|
const std::string kComma = ",";
|
|
|
|
// Analog output capabilities constants.
|
|
const std::vector<std::string> kAnalogOutputCapabilities = {
|
|
wvcdm::QUERY_VALUE_NONE, wvcdm::QUERY_VALUE_SUPPORTED,
|
|
wvcdm::QUERY_VALUE_CGMS_A, wvcdm::QUERY_VALUE_UNKNOWN};
|
|
const std::vector<std::string> kCanDisableAnalogOutput = {
|
|
wvcdm::QUERY_VALUE_TRUE, wvcdm::QUERY_VALUE_FALSE,
|
|
wvcdm::QUERY_VALUE_UNKNOWN};
|
|
|
|
// Protobuf generated classes
|
|
using video_widevine::LicenseIdentification;
|
|
using video_widevine::LicenseRequest_ContentIdentification;
|
|
using video_widevine::ProvisioningResponse;
|
|
using video_widevine::ProvisioningResponse_ProvisioningStatus;
|
|
using video_widevine::ProvisioningResponse_ProvisioningStatus_NO_ERROR;
|
|
using video_widevine::
|
|
ProvisioningResponse_ProvisioningStatus_REVOKED_DEVICE_CREDENTIALS;
|
|
using video_widevine::
|
|
ProvisioningResponse_ProvisioningStatus_REVOKED_DEVICE_SERIES;
|
|
using video_widevine::SignedProvisioningMessage;
|
|
using video_widevine::
|
|
SignedProvisioningMessage_ProvisioningProtocolVersion_VERSION_1_1;
|
|
|
|
using wvutil::ArraySize;
|
|
|
|
// TODO(rfrias): refactor to print out the decryption test names
|
|
struct SubSampleInfo {
|
|
bool retrieve_key;
|
|
size_t num_of_subsamples;
|
|
bool validate_key_id;
|
|
bool is_encrypted;
|
|
bool is_secure;
|
|
wvcdm::KeyId key_id;
|
|
std::vector<uint8_t> encrypt_data;
|
|
std::vector<uint8_t> decrypt_data;
|
|
std::vector<uint8_t> iv;
|
|
size_t block_offset;
|
|
uint8_t subsample_flags;
|
|
};
|
|
|
|
const SubSampleInfo kClearSubSample = {
|
|
true,
|
|
1,
|
|
true,
|
|
false,
|
|
false,
|
|
wvutil::a2bs_hex("371EA35E1A985D75D198A7F41020DC23"),
|
|
wvutil::a2b_hex(
|
|
"217ce9bde99bd91e9733a1a00b9b557ac3a433dc92633546156817fae26b6e1c"
|
|
"942ac20a89ff79f4c2f25fba99d6a44618a8c0420b27d54e3da17b77c9d43cca"
|
|
"595d259a1e4a8b6d7744cd98c5d3f921adc252eb7d8af6b916044b676a574747"
|
|
"8df21fdc42f166880d97a2225cd5c9ea5e7b752f4cf81bbdbe98e542ee10e1c6"
|
|
"ad868a6ac55c10d564fc23b8acff407daaf4ed2743520e02cda9680d9ea88e91"
|
|
"029359c4cf5906b6ab5bf60fbb3f1a1c7c59acfc7e4fb4ad8e623c04d503a3dd"
|
|
"4884604c8da8a53ce33db9ff8f1c5bb6bb97f37b39906bf41596555c1bcce9ed"
|
|
"08a899cd760ff0899a1170c2f224b9c52997a0785b7fe170805fd3e8b1127659"),
|
|
wvutil::a2b_hex(
|
|
"217ce9bde99bd91e9733a1a00b9b557ac3a433dc92633546156817fae26b6e1c"
|
|
"942ac20a89ff79f4c2f25fba99d6a44618a8c0420b27d54e3da17b77c9d43cca"
|
|
"595d259a1e4a8b6d7744cd98c5d3f921adc252eb7d8af6b916044b676a574747"
|
|
"8df21fdc42f166880d97a2225cd5c9ea5e7b752f4cf81bbdbe98e542ee10e1c6"
|
|
"ad868a6ac55c10d564fc23b8acff407daaf4ed2743520e02cda9680d9ea88e91"
|
|
"029359c4cf5906b6ab5bf60fbb3f1a1c7c59acfc7e4fb4ad8e623c04d503a3dd"
|
|
"4884604c8da8a53ce33db9ff8f1c5bb6bb97f37b39906bf41596555c1bcce9ed"
|
|
"08a899cd760ff0899a1170c2f224b9c52997a0785b7fe170805fd3e8b1127659"),
|
|
wvutil::a2b_hex("f6f4b1e600a5b67813ed2bded913ba9f"),
|
|
0,
|
|
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample};
|
|
|
|
const SubSampleInfo kClearSubSamples[2] = {
|
|
// block 0, key SD, encrypted, 128b
|
|
{true, 1, true, false, false,
|
|
wvutil::a2bs_hex("371EA35E1A985D75D198A7F41020DC23"),
|
|
wvutil::a2b_hex(
|
|
"217ce9bde99bd91e9733a1a00b9b557ac3a433dc92633546156817fae26b6e1c"
|
|
"942ac20a89ff79f4c2f25fba99d6a44618a8c0420b27d54e3da17b77c9d43cca"
|
|
"595d259a1e4a8b6d7744cd98c5d3f921adc252eb7d8af6b916044b676a574747"
|
|
"8df21fdc42f166880d97a2225cd5c9ea5e7b752f4cf81bbdbe98e542ee10e1c6"),
|
|
wvutil::a2b_hex(
|
|
"217ce9bde99bd91e9733a1a00b9b557ac3a433dc92633546156817fae26b6e1c"
|
|
"942ac20a89ff79f4c2f25fba99d6a44618a8c0420b27d54e3da17b77c9d43cca"
|
|
"595d259a1e4a8b6d7744cd98c5d3f921adc252eb7d8af6b916044b676a574747"
|
|
"8df21fdc42f166880d97a2225cd5c9ea5e7b752f4cf81bbdbe98e542ee10e1c6"),
|
|
wvutil::a2b_hex("f6f4b1e600a5b67813ed2bded913ba9f"), 0,
|
|
OEMCrypto_FirstSubsample},
|
|
// block 1, key SD, encrypted, 128b
|
|
{true, 1, true, false, false,
|
|
wvutil::a2bs_hex("371EA35E1A985D75D198A7F41020DC23"),
|
|
wvutil::a2b_hex(
|
|
"ad868a6ac55c10d564fc23b8acff407daaf4ed2743520e02cda9680d9ea88e91"
|
|
"029359c4cf5906b6ab5bf60fbb3f1a1c7c59acfc7e4fb4ad8e623c04d503a3dd"
|
|
"4884604c8da8a53ce33db9ff8f1c5bb6bb97f37b39906bf41596555c1bcce9ed"
|
|
"08a899cd760ff0899a1170c2f224b9c52997a0785b7fe170805fd3e8b1127659"),
|
|
wvutil::a2b_hex(
|
|
"ad868a6ac55c10d564fc23b8acff407daaf4ed2743520e02cda9680d9ea88e91"
|
|
"029359c4cf5906b6ab5bf60fbb3f1a1c7c59acfc7e4fb4ad8e623c04d503a3dd"
|
|
"4884604c8da8a53ce33db9ff8f1c5bb6bb97f37b39906bf41596555c1bcce9ed"
|
|
"08a899cd760ff0899a1170c2f224b9c52997a0785b7fe170805fd3e8b1127659"),
|
|
wvutil::a2b_hex("f6f4b1e600a5b67813ed2bded913ba9f"), 0,
|
|
OEMCrypto_LastSubsample}};
|
|
|
|
const SubSampleInfo kClearSubSampleNoKey = {
|
|
false,
|
|
1,
|
|
false,
|
|
false,
|
|
false,
|
|
wvutil::a2bs_hex("77777777777777777777777777777777"),
|
|
wvutil::a2b_hex(
|
|
"217ce9bde99bd91e9733a1a00b9b557ac3a433dc92633546156817fae26b6e1c"
|
|
"942ac20a89ff79f4c2f25fba99d6a44618a8c0420b27d54e3da17b77c9d43cca"
|
|
"595d259a1e4a8b6d7744cd98c5d3f921adc252eb7d8af6b916044b676a574747"
|
|
"8df21fdc42f166880d97a2225cd5c9ea5e7b752f4cf81bbdbe98e542ee10e1c6"
|
|
"ad868a6ac55c10d564fc23b8acff407daaf4ed2743520e02cda9680d9ea88e91"
|
|
"029359c4cf5906b6ab5bf60fbb3f1a1c7c59acfc7e4fb4ad8e623c04d503a3dd"
|
|
"4884604c8da8a53ce33db9ff8f1c5bb6bb97f37b39906bf41596555c1bcce9ed"
|
|
"08a899cd760ff0899a1170c2f224b9c52997a0785b7fe170805fd3e8b1127659"),
|
|
wvutil::a2b_hex(
|
|
"217ce9bde99bd91e9733a1a00b9b557ac3a433dc92633546156817fae26b6e1c"
|
|
"942ac20a89ff79f4c2f25fba99d6a44618a8c0420b27d54e3da17b77c9d43cca"
|
|
"595d259a1e4a8b6d7744cd98c5d3f921adc252eb7d8af6b916044b676a574747"
|
|
"8df21fdc42f166880d97a2225cd5c9ea5e7b752f4cf81bbdbe98e542ee10e1c6"
|
|
"ad868a6ac55c10d564fc23b8acff407daaf4ed2743520e02cda9680d9ea88e91"
|
|
"029359c4cf5906b6ab5bf60fbb3f1a1c7c59acfc7e4fb4ad8e623c04d503a3dd"
|
|
"4884604c8da8a53ce33db9ff8f1c5bb6bb97f37b39906bf41596555c1bcce9ed"
|
|
"08a899cd760ff0899a1170c2f224b9c52997a0785b7fe170805fd3e8b1127659"),
|
|
wvutil::a2b_hex("f6f4b1e600a5b67813ed2bded913ba9f"),
|
|
0,
|
|
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample};
|
|
|
|
const SubSampleInfo kSingleEncryptedSubSample = {
|
|
// key SD, encrypted, 256b
|
|
true,
|
|
1,
|
|
true,
|
|
true,
|
|
false,
|
|
wvutil::a2bs_hex("371EA35E1A985D75D198A7F41020DC23"),
|
|
wvutil::a2b_hex(
|
|
"64ab17b3e3dfab47245c7cce4543d4fc7a26dcf248f19f9b59f3c92601440b36"
|
|
"17c8ed0c96c656549e461f38708cd47a434066f8df28ccc28b79252eee3f9c2d"
|
|
"7f6c68ebe40141fe818fe082ca523c03d69ddaf183a93c022327fedc5582c5ab"
|
|
"ca9d342b71263a67f9cb2336f12108aaaef464f17177e44e9b0c4e56e61da53c"
|
|
"2150b4405cc82d994dfd9bf4087c761956d6688a9705db4cf350381085f383c4"
|
|
"9666d4aed135c519c1f0b5cba06e287feea96ea367bf54e7368dcf998276c6e4"
|
|
"6497e0c50e20fef74e42cb518fe7f22ef27202428688f86404e8278587017012"
|
|
"c1d65537c6cbd7dde04aae338d68115a9f430afc100ab83cdadf45dca39db685"),
|
|
wvutil::a2b_hex(
|
|
"217ce9bde99bd91e9733a1a00b9b557ac3a433dc92633546156817fae26b6e1c"
|
|
"942ac20a89ff79f4c2f25fba99d6a44618a8c0420b27d54e3da17b77c9d43cca"
|
|
"595d259a1e4a8b6d7744cd98c5d3f921adc252eb7d8af6b916044b676a574747"
|
|
"8df21fdc42f166880d97a2225cd5c9ea5e7b752f4cf81bbdbe98e542ee10e1c6"
|
|
"ad868a6ac55c10d564fc23b8acff407daaf4ed2743520e02cda9680d9ea88e91"
|
|
"029359c4cf5906b6ab5bf60fbb3f1a1c7c59acfc7e4fb4ad8e623c04d503a3dd"
|
|
"4884604c8da8a53ce33db9ff8f1c5bb6bb97f37b39906bf41596555c1bcce9ed"
|
|
"08a899cd760ff0899a1170c2f224b9c52997a0785b7fe170805fd3e8b1127659"),
|
|
wvutil::a2b_hex("f6f4b1e600a5b67813ed2bded913ba9f"),
|
|
0,
|
|
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample};
|
|
|
|
const SubSampleInfo kSingleEncryptedOfflineSubSample = {
|
|
// key SD, encrypted, 256b
|
|
true,
|
|
1,
|
|
true,
|
|
true,
|
|
false,
|
|
wvutil::a2bs_hex("DD003BA34DA3CDA09AA3B6D5CC6C34B2"),
|
|
wvutil::a2b_hex(
|
|
"64ab17b3e3dfab47245c7cce4543d4fc7a26dcf248f19f9b59f3c92601440b36"
|
|
"17c8ed0c96c656549e461f38708cd47a434066f8df28ccc28b79252eee3f9c2d"
|
|
"7f6c68ebe40141fe818fe082ca523c03d69ddaf183a93c022327fedc5582c5ab"
|
|
"ca9d342b71263a67f9cb2336f12108aaaef464f17177e44e9b0c4e56e61da53c"
|
|
"2150b4405cc82d994dfd9bf4087c761956d6688a9705db4cf350381085f383c4"
|
|
"9666d4aed135c519c1f0b5cba06e287feea96ea367bf54e7368dcf998276c6e4"
|
|
"6497e0c50e20fef74e42cb518fe7f22ef27202428688f86404e8278587017012"
|
|
"c1d65537c6cbd7dde04aae338d68115a9f430afc100ab83cdadf45dca39db685"),
|
|
wvutil::a2b_hex(
|
|
"4E477383EDAFA01D4C31CBCF015AE3D256FF96052F24EB50C37AEC9E798A23AE"
|
|
"58D187C627769F11E72C712BFE269F7BEB04D7E636D508F9B623888416340730"
|
|
"36C0F409B49BD9EB2DBAAF7218527371A40AB9B93A7C3FACA246A9A37C560106"
|
|
"2F6C5C5F7C0F4DA528C70639268602E08D100079A6D8CDBD82C44BFF7FC8D304"
|
|
"277E5638AA275AD1CC08F0D4F850777E0453DEFD927B49D2B5CF0372FC95BEEE"
|
|
"4287F7AEB30E3FECBDEB2981BD0691FED2D7CFACB92E115A44CADD96843F240E"
|
|
"236A9F9B2E3CB075912FE15C5056B21D809538C3C19D5B2F5FA242CD7F550306"
|
|
"6DA2F6A78C5090D9B49F78632FB6F278AC1F680E690BF3AD4933FDE77922CF6A"),
|
|
wvutil::a2b_hex("f6f4b1e600a5b67813ed2bded913ba9f"),
|
|
0,
|
|
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample};
|
|
|
|
const SubSampleInfo kSwitchKeyEncryptedSubSamples[2] = {
|
|
// block 0, key SD, encrypted, 256b
|
|
{true, 2, true, true, false,
|
|
wvutil::a2bs_hex("371EA35E1A985D75D198A7F41020DC23"),
|
|
wvutil::a2b_hex(
|
|
"9efe1b7a3973324525e9b8c516855e554b52a73ce35cd181731b005a6525624f"
|
|
"a03875c89aee02f1da7f556b7e7d9a3eba89fe3061194bc2d1446233ca022892"
|
|
"ab95083f127d6ccb01b1368e6b6fa77e3570d84477a5517f2965ff72f8e0740c"
|
|
"d8282c22e7724ce44d88526dcd2d601002b717d8ca3b97087d28f9e3810efb8e"
|
|
"d4b08ee2da6bdb05a790b6363f8ee65cae8328e86848e4caf9be92db3e5492ad"
|
|
"6363a26051c23cf23b9aee79a8002470c4a5834c6aae956b509a42f4110262e0"
|
|
"565a043befd8ef3a335c9dfedca8d218f364215859d7daf7d040b1f0cb2eda87"
|
|
"c1be18f323fb0235dd9a6e7b3b2fea1cb9c6e5bc2b349962f0b8f0b92e749db2"),
|
|
wvutil::a2b_hex(
|
|
"38a715e73c9209544c47e5eb089146de8136df5c6ed01e3e8d9cea8ae18a81c9"
|
|
"8c9c8ec67bf379dd80a21f57b0b00575827a240cd11332c5212defe9f1ef8b8e"
|
|
"2399271767bfe81e5a11abf7bca1307578217c4d5f8b942ab04351b4725d6e24"
|
|
"cd171fa3083570f7d7ae2b297224f701fd04d699c12c53e9ce9d3dab64ee6332"
|
|
"5fba183b7a1f3f20acaeabc0c446c9ca0df39fafb1e2891c72500741ad5b7941"
|
|
"4651729e30e9ddbb22f47a5026e09c795ff15a858123a7979e7be716cb8cd075"
|
|
"e8bfb91bc0cc83f7cacd5c4772f7479a1193d9307bc5f837185faed5499e66a7"
|
|
"e27db50b5d018d022279032016862883befd113b6c784889be8f9e6eb0f335f7"),
|
|
wvutil::a2b_hex("fd38d7f754a97128b78440433e1ef4a8"), 0,
|
|
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample},
|
|
// block 1, key HD, encrypted, 256b
|
|
{true, 2, true, true, false,
|
|
wvutil::a2bs_hex("6D665122C01767FC087F340E6C335EA4"),
|
|
wvutil::a2b_hex(
|
|
"d9392411d15f47de0d7dd854eae5eb5ffbd2d3f86c530d2ef619fc81725df866"
|
|
"2e6267041b947863e5779812da3220824a3d154e7b094c1af70238c65877454e"
|
|
"3c3bdcce836962ba29b69442d5e5b4a4ff32a4b026521f3fa4d442a400081cdd"
|
|
"ba6ed313c43cc34443c4dc2b9cdcc9dbd478bf6afc4d515c07b42d8b151c15cc"
|
|
"165270f6083ecd5c75313c496143068f45966bb53e35906c7568424e93e35989"
|
|
"7da702fb89eb7c970af838d56a64a7a68f7cffe529807765d62540bb06bbc633"
|
|
"6eeec62d20f5b639731e57a0851e23e146cb9502dbde93dc4aca20e471a3fa0b"
|
|
"df01a74ecb48d5f57ac2be98fb21d19de7587d8d1e6e1788726e1544d05137f6"),
|
|
wvutil::a2b_hex(
|
|
"c48a94d07c34c4315e01010dbcc63a038d50a023b1ff2a07deae6e498cb03f84"
|
|
"57911d8c9d72fa5184c738d81a49999504b7cd4532b465436b7044606a6d40a2"
|
|
"74a653c4b93ebaf8db585d180211a02e5501a8027f2235fe56682390325c88ee"
|
|
"2ada85483eddb955c56f79634a2ceeb36d04b5d6faf7611817577d9b0fda088e"
|
|
"921fbdd7fa594ee4f557f7393f51f3049cd36973f645badf7cc4672ef8d973da"
|
|
"7dae8e59f32bf950c6569845a5261b5ed9cc500706eccf8d41f015b32026e16e"
|
|
"ab274465d880ff99a5eaea603eea66c7b0e6679bfd87145de0ec1a73ebfff092"
|
|
"866346a1d66db2923bca30664f417a6b66c07e91fb491be7872ebe5c9c2d03c2"),
|
|
wvutil::a2b_hex("f56ab022666de858920e532f19bb32f6"), 0,
|
|
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample}};
|
|
|
|
const SubSampleInfo kPartialEncryptedSubSamples[3] = {
|
|
// block 1, key SD, encrypted, 1-125b, offset 0
|
|
{true, 3, true, true, false,
|
|
wvutil::a2bs_hex("371EA35E1A985D75D198A7F41020DC23"),
|
|
wvutil::a2b_hex(
|
|
"53cc758763904ea5870458e6b23d36db1e6d7f7aaa2f3eeebb5393a7264991e7"
|
|
"ce4f57b198326e1a208a821799b2a29c90567ab57321b06e51fc20dc9bc5fc55"
|
|
"10720a8bb1f5e002c3e50ff70d2d806a9432cad237050d09581f5b0d59b00090"
|
|
"b3ad69b4087f5a155b17e13c44d33fa007475d207fc4ac2ef3b571ecb9"),
|
|
wvutil::a2b_hex(
|
|
"52e65334501acadf78e2b26460def3ac973771ed7c64001a2e82917342a7eab3"
|
|
"047f5e85449692fae8f677be425a47bdea850df5a3ffff17043afb1f2b437ab2"
|
|
"b1d5e0784c4ed8f97fc24b8f565e85ed63fb7d1365980d9aea7b8b58f488f83c"
|
|
"1ce80b6096c60f3b113c988ff185b26e798da8fc6f327e4ff00e4b3fbf"),
|
|
wvutil::a2b_hex("6ba18dd40f49da7f64c368e4db43fc88"), 0,
|
|
OEMCrypto_FirstSubsample},
|
|
// block 2, key SD, encrypted, 126-187b, offset 5
|
|
{true, 3, true, true, false,
|
|
wvutil::a2bs_hex("371EA35E1A985D75D198A7F41020DC23"),
|
|
wvutil::a2b_hex(
|
|
"f3c852"
|
|
"ce00dc4806f0c6856ae1732e20308096478e1d822d75c2bb768119565d3bd6e6"
|
|
"901e36164f4802355ee758fc46ef6cf5f852dd5256c7b1e5f96d29"),
|
|
wvutil::a2b_hex(
|
|
"b1ed0a"
|
|
"a054bce40ccb0ebc70b181d1a12055f46ac55e29c7c2473a29d2a366d240ec48"
|
|
"7cede274f012813a877f99159e7062b6a37cfc9327a7bc2195814e"),
|
|
wvutil::a2b_hex("6ba18dd40f49da7f64c368e4db43fc8f"), 13, 0},
|
|
// block 3, key SD, encrypted, 188-256b, offset 5
|
|
{true, 3, true, true, false,
|
|
wvutil::a2bs_hex("371EA35E1A985D75D198A7F41020DC23"),
|
|
wvutil::a2b_hex(
|
|
"3b20525d5e"
|
|
"78b8e5aa344d5c4e425e67ddf889ea7c4bb1d49af67eba67718b765e0a940402"
|
|
"8d306f4ce693ad6dc0a931d507fa14fff4d293d4170280b3e0fca2d628f722e8"),
|
|
wvutil::a2b_hex(
|
|
"653b818d1d"
|
|
"4ab9a9128361d8ca6a9d2766df5c096ee29f4f5204febdf217a94a5b560cd692"
|
|
"cc36d3e071df789fdeac2fb7ec6dcd7af94bb1f85c22025b25e702e38212b927"),
|
|
wvutil::a2b_hex("6ba18dd40f49da7f64c368e4db43fc93"), 11,
|
|
OEMCrypto_LastSubsample}};
|
|
|
|
const SubSampleInfo kSingleEncryptedSubSampleShortExpiry = {
|
|
// key 1, encrypted, 256b
|
|
true,
|
|
1,
|
|
true,
|
|
true,
|
|
false,
|
|
wvutil::a2bs_hex("9714593E1EEE57859D34ECFA821702BB"),
|
|
wvutil::a2b_hex(
|
|
"3b2cbde084973539329bd5656da22d20396249bf4a18a51c38c4743360cc9fea"
|
|
"a1c78d53de1bd7e14dc5d256fd20a57178a98b83804258c239acd7aa38f2d7d2"
|
|
"eca614965b3d22049e19e236fc1800e60965d8b36415677bf2f843d50a6943c4"
|
|
"683c07c114a32f5e5fbc9939c483c3a1b2ecd3d82b554d649798866191724283"
|
|
"f0ab082eba2da79aaca5c4eaf186f9ee9a0c568f621f705a578f30e4e2ef7b96"
|
|
"5e14cc046ce6dbf272ee5558b098f332333e95fc879dea6c29bf34acdb649650"
|
|
"f08201b9e649960f2493fd7677cc3abf5ae70e5445845c947ba544456b431646"
|
|
"d95a133bff5f57614dda5e4446cd8837901d074149dadf4b775b5b07bb88ca20"),
|
|
wvutil::a2b_hex(
|
|
"5a36c0b633b58faf22156d78fdfb608e54a8095788b2b0463ef78d030b4abf82"
|
|
"eff34b8d9b7b6352e7d72de991b599662aa475da355033620152e2356ebfadee"
|
|
"06172be9e1058fa177e223b9fdd191380cff53c3ea810c6fd852a1df4967b799"
|
|
"415179a2276ec388ef763bab89605b9c6952c28dc8d6bf86b03fabbb46b392a3"
|
|
"1dad15be602eeeeabb45070b3e25d6bb0217073b1fc44c9fe848594121fd6a91"
|
|
"304d605e21f69615e1b57db18312b6b948725724b74e91d8aea7371e99532469"
|
|
"1b358bdee873f1936b63efe83d190a53c2d21754d302d63ff285174023473755"
|
|
"58b938c2e3ca4c2ce48942da97f9e45797f2c074ac6004734e93784a48af6160"),
|
|
wvutil::a2b_hex("4cca615fc013102892f91efee936639b"),
|
|
0,
|
|
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample};
|
|
|
|
const SubSampleInfo kSecureStopSubSamplesIcp[] = {
|
|
{true, 1, true, true, false,
|
|
wvutil::a2bs_hex("E82DDD1D07CBB31CDD31EBAAE0894609"),
|
|
wvutil::a2b_hex(
|
|
"fe8670a01c86906c056b4bf85ad278464c4eb79c60de1da8480e66e78561350e"
|
|
"a25ae19a001f834c43aaeadf900b3c5a6745e885a4d1d1ae5bafac08dc1d60e5"
|
|
"f3465da303909ec4b09023490471f670b615d77db844192854fdab52b7806203"
|
|
"89b374594bbb6a2f2fcc31036d7cb8a3f80c0e27637b58a7650028fbf2470d68"
|
|
"1bbd77934af165d215ef325c74438c9d99a20fc628792db28c05ed5deff7d9d4"
|
|
"dba02ddb6cf11dc6e78cb5200940af9a2321c3a7c4c79be67b54a744dae1209c"
|
|
"fa02fc250ce18d30c7da9c3a4a6c9619bf8561a42ff1e55a7b14fa3c8de69196"
|
|
"c2b8e3ff672fc37003b479da5d567b7199917dbe5aa402890ebb066bce140b33"),
|
|
wvutil::a2b_hex(
|
|
"d08733bd0ef671f467906b50ff8322091400f86fd6f016fea2b86e33923775b3"
|
|
"ebb4c8c6f3ba8b78dd200a74d3872a40264ab99e1d422e4f819abb7f249114aa"
|
|
"b334420b37c86ce81938615ab9d3a6b2de8db545cd88e35091031e73016fb386"
|
|
"1b754298329b52dbe483de3a532277815e659f3e05e89257333225b933d92e15"
|
|
"ef2deff287a192d2c8fc942a29a5f3a1d54440ac6385de7b34bb650b889e4ae9"
|
|
"58c957b5f5ff268f445c0a6b825fcad55290cb7b5c9814bc4c72984dcf4c8fd7"
|
|
"5f511c173b2e0a3163b18a1eac58539e5c188aeb0751b946ad4dcd08ea777a7f"
|
|
"37326df26fa509343faa98dff667629f557873f1284903202e451227ef465a62"),
|
|
wvutil::a2b_hex("7362b5140c4ce0cd5f863858668d3f1a"), 0,
|
|
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample},
|
|
{true, 1, true, true, false,
|
|
wvutil::a2bs_hex("04B2B456405CAD7170BE848CA75AA2DF"),
|
|
wvutil::a2b_hex(
|
|
"ccb68f97b03e7af1a9e208d91655ba645cc5a5d454f3cb7c3d621e98a7592d90"
|
|
"4ff7023555f0e99bcf3918948f4fca7a430faf17d7d67268d81d8096d7c48809"
|
|
"c14220e634680fbe0c760571dd86a1905835035f4a238c2d7f17bd1363b113c1"
|
|
"91782aebb77a1064142a68b59ecdcc6990ed4244082464d91dbfe09e08744b2f"
|
|
"d1e850a008acbbe129fbd050c8dc1b28cb8cc2c1e2d920ea458f74809297b513"
|
|
"85307b481cbb81d6759385ee782d6c0e101c20ca1937cfd0d6e024da1a0f718a"
|
|
"fb7c4ff3df1ca87e67602d28168233cc2448d44b79f405d4c6e67eb88d705050"
|
|
"2a806cb986423e3b0e7a97738e1d1d143b4f5f926a4e2f37c7fbe65f56d5b690"),
|
|
wvutil::a2b_hex(
|
|
"fa35aa1f5e5d7b958880d5eed9cc1bb81d36ebd04c0250a8c752ea5f413bbdcf"
|
|
"3785790c8dba7a0b21c71346bb7f946a9b71c0d2fe87d2e2fab14e35ee8400e7"
|
|
"097a7d2d9a25b468e848e8dee2388f890967516c7dab96db4713c7855f717aed"
|
|
"2ae9c2895baaa636e4a610ab26b35d771d62397ba40d78694dab70dcbdfa91c3"
|
|
"6af79ad6b6ebb479b4a5fbc242a8574ebe6717f0813fbd6f726ce2af4d522e66"
|
|
"b36c940fce519c913db56a6372c3636b10c0149b4cd97e74c576765b533abdc2"
|
|
"729f1470dd7f9a60d3572dcc9839582a4606ee17eaced39797daef8f885d3f8f"
|
|
"e14877ae530451c4242bbc3934f85a5bb71b363351894f881896471cfeaf68b2"),
|
|
wvutil::a2b_hex("4a59e3e5f3e4f7e2f494ad09c12a9e4c"), 0,
|
|
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample},
|
|
{true, 1, true, true, false,
|
|
wvutil::a2bs_hex("3AE243D83B93B3311A1D777FF5FBE01A"),
|
|
wvutil::a2b_hex(
|
|
"934997779aa1aeb45d6ba8845f13786575d0adf85a5e93674d9597f8d4286ed7"
|
|
"dcce02f306e502bbd9f1cadf502f354038ca921276d158d911bdf3171d335b18"
|
|
"0ae0f9abece16ff31ee263228354f724da2f3723b19caa38ea02bd6563b01208"
|
|
"fb5bf57854ac0fe38d5883197ef90324b2721ff20fdcf9a53819515e6daa096e"
|
|
"70f6f5c1d29a4a13dafd127e2e1f761ea0e28fd451607552ecbaef5da3c780bc"
|
|
"aaf2667b4cc4f858f01d480cac9e32c3fbb5705e5d2adcceebefc2535c117208"
|
|
"e65f604799fc3d7223e16908550f287a4bea687008cb0064cf14d3aeedb8c705"
|
|
"09ebc5c2b8b5315f43c04d78d2f55f4b32c7d33e157114362106395cc0bb6d93"),
|
|
wvutil::a2b_hex(
|
|
"2dd54eee1307753508e1f250d637044d6e8f5abf057dab73e9e95f83910e4efc"
|
|
"191c9bac63950f13fd51833dd94a4d03f2b64fb5c721970c418fe53fa6f74ad5"
|
|
"a6e16477a35c7aa6e28909b069cd25770ef80da20918fc30fe95fd5c87fd3522"
|
|
"1649de17ca2c7b3dc31f936f0cbdf97c7b1c15de3a86b279dc4b4de64943914a"
|
|
"99734556c4b7a1a0b022c1933cb0786068fc18d49fed2f2b49f3ac6d01c32d07"
|
|
"92175ce2844eaf9064e6a3fcffade038d690cbed81659351163a22432f0d0545"
|
|
"037e1c805d8e92a1272b4196ad0ce22f26bb80063137a8e454d3b97e2414283d"
|
|
"ed0716cd8bceb80cf59166a217006bd147c51b04dfb183088ce3f51e9b9f759e"),
|
|
wvutil::a2b_hex("b358ab21ac90455bbf60490daad457e3"), 0,
|
|
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample},
|
|
{true, 1, true, true, false,
|
|
wvutil::a2bs_hex("5292104C011418973C31235953FC8205"),
|
|
wvutil::a2b_hex(
|
|
"d433adfd3d892d5c3e7d54ab0218ee0712400a920d4b71d553912451169f9b79"
|
|
"3f103260cf04c34f6a5944bb96da79946a62bdbcd804ca28b17656338edfa700"
|
|
"5c090f2750663a026fd15a0b0e448adbbfd53f613ea3993d9fd504421b575f28"
|
|
"12020bb8cca0ce333eabee0403df9f410c6473d7673d6991caab6ea2ece8f743"
|
|
"5a3ca049fa00c96c9b7c47e3073d25d08f23b47ffc509c48a81a2f98c9ec8a1d"
|
|
"e41764c14a5010df8b4692e8612a45bf0645601d4910119e6268ca4f6d8016a8"
|
|
"3d933d53f44243674b522bae43043c068c8cae43f0ac224198de71315b3a6f82"
|
|
"c1b523bbdcdb3e9f162c308684dd17e364b448ed0e90b0e496b8cf633a982708"),
|
|
wvutil::a2b_hex(
|
|
"5efb5e5b913785e9935e67e763b8ff29a6687ac6c18d5a7e16951beb704f9c95"
|
|
"f081ca28f54c3e237fb5a7b0444e9a3e17da91e5cf2c0a8f009a873fb079c339"
|
|
"81b0ebc565b2c56d983ee33686fa5057c9891e246b67bb6950400acb06d5ae50"
|
|
"0e61a7e9289ea67ec2e88e8d0cc3c494fd996e93270e9b264a21818987e969c5"
|
|
"1e2955c5a53202e5aec1e2c906e1c006325112eb5c33ee37d0c07ea97d80c17f"
|
|
"d56e0efcf40c8c98981a86c18a159f05d851891236c124641d4584c49ccd7478"
|
|
"4f328a9cacae0f945238d98741b2969fe258903e85f963daba7168f05c18b09f"
|
|
"660dae18de41b1c49769cd38e24b135c37a65b69533f5c7d085898faedfbed5d"),
|
|
wvutil::a2b_hex("cef7e8aaa6ec1154cb68a988f7c9e803"), 0,
|
|
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample},
|
|
{true, 1, true, true, false,
|
|
wvutil::a2bs_hex("D7C01C2F868AE314BCB893E4E9C6AC75"),
|
|
wvutil::a2b_hex(
|
|
"fa5d28677721de488ffc209321529728c77bc338accd45ccc98ab2063fc8c373"
|
|
"48c7698534175d72bf185690d19474d08c4fd4ed4eb46d858633f05337d70e92"
|
|
"03f7ee6bec0f7003bdf6fa665ba172855a51a82da406348ba651a2f62888c30a"
|
|
"7b4e1355bb94a9ff5c458f397c9a09e5d7785b286ef83142ddad324cc74e1929"
|
|
"60ad1c34c425cdefbedcb62ca9b21ac4f3df7f5922e263cb7798de54b622ab3f"
|
|
"64a0dd6ee1e40be6ecc857e657994ecac02ccfafc9036f382d7dbdf35c903356"
|
|
"40b7c9db088143060b24f24b21c4a7c2faeb3d308e57c5a75955fd704cfe4dee"
|
|
"71a4a7d823102b90eddded795ca6eb36282d777db8cfd783e50e5c2a816ee9ed"),
|
|
wvutil::a2b_hex(
|
|
"d5db2f50c0f5a39414ddfa5129c2c641836a8c6312b26a210c996988e0c768d5"
|
|
"9a3adff117293b52b0653c0d6e22589edda804fb8caa7442362fe4caf9053b6a"
|
|
"2a34896399259a188f0c805de54b091a7eabff098b28d54584c01dd83301e4ca"
|
|
"a01b226c4541af1592d4440e103eb55bbd08c471efb0856ec9ced43211fc3325"
|
|
"3d402dff0d15f40833dd71259a8d40d527659ef3e5f9fd0826c9471dddb17e1e"
|
|
"fab916abc957fb07d7eac4a368ac92a8fb16d995613af47303034ee57b59b1d7"
|
|
"101aa031f5586b2f6b4c74372c4d7306db02509b5924d52c46a270f427743a85"
|
|
"614f080d83f3b15cbc6600ddda43adff5d2941da13ebe49d80fd0cea5025412b"),
|
|
wvutil::a2b_hex("964c2dfda920357c668308d52d33c652"), 0,
|
|
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample}};
|
|
|
|
const SubSampleInfo kEntitlementWithKeyRotationSubSampleSinglePssh[] = {
|
|
{true, 1, true, true, false,
|
|
wvutil::a2bs_hex("c8326486bb5d5c4a958f00b1111afc81"),
|
|
wvutil::a2b_hex(
|
|
"64ab17b3e3dfab47245c7cce4543d4fc7a26dcf248f19f9b59f3c92601440b36"
|
|
"17c8ed0c96c656549e461f38708cd47a434066f8df28ccc28b79252eee3f9c2d"
|
|
"7f6c68ebe40141fe818fe082ca523c03d69ddaf183a93c022327fedc5582c5ab"
|
|
"ca9d342b71263a67f9cb2336f12108aaaef464f17177e44e9b0c4e56e61da53c"
|
|
"2150b4405cc82d994dfd9bf4087c761956d6688a9705db4cf350381085f383c4"
|
|
"9666d4aed135c519c1f0b5cba06e287feea96ea367bf54e7368dcf998276c6e4"
|
|
"6497e0c50e20fef74e42cb518fe7f22ef27202428688f86404e8278587017012"
|
|
"c1d65537c6cbd7dde04aae338d68115a9f430afc100ab83cdadf45dca39db685"),
|
|
wvutil::a2b_hex(
|
|
"cb468f067baa5634f83947b921fdb37e66bc5925edc4bdc2424db4c33cfd55a1"
|
|
"cd0a96b6634796ddcf5ba8820b83f80d318ff49b7e614779f5e87dd6dfadd8d6"
|
|
"cf95c7b0f21e4174e2e91371ebdb69866340a37a581a7c0713eda5168bcfc003"
|
|
"12a341a83defa754c4601772ed9171526720b6a2b2b084030f21ef13f2a35dec"
|
|
"e93f5c394c56d9ce108c2f5b0e5edb857d322fae24ec22f3ad726496b382306b"
|
|
"4fdf5a0a99efc2db2a9458f0bfd6b21869c9acf6ea222fe942af6cd9b38d9a50"
|
|
"a96db14ad27d368c5753aa5da8d8507603ed08086e2492bdff267ee64862f159"
|
|
"b19d2c72b2f5a39520d5ae2aacae1a192d375d45f3f9ba86d5026cbcacdfecbe"),
|
|
wvutil::a2b_hex("f6f4b1e600a5b67813ed2bded913ba9f"), 0,
|
|
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample},
|
|
{true, 1, true, true, false,
|
|
wvutil::a2bs_hex("f8488775a99855ff94b93ec5bd499356"),
|
|
wvutil::a2b_hex(
|
|
"64ab17b3e3dfab47245c7cce4543d4fc7a26dcf248f19f9b59f3c92601440b36"
|
|
"17c8ed0c96c656549e461f38708cd47a434066f8df28ccc28b79252eee3f9c2d"
|
|
"7f6c68ebe40141fe818fe082ca523c03d69ddaf183a93c022327fedc5582c5ab"
|
|
"ca9d342b71263a67f9cb2336f12108aaaef464f17177e44e9b0c4e56e61da53c"
|
|
"2150b4405cc82d994dfd9bf4087c761956d6688a9705db4cf350381085f383c4"
|
|
"9666d4aed135c519c1f0b5cba06e287feea96ea367bf54e7368dcf998276c6e4"
|
|
"6497e0c50e20fef74e42cb518fe7f22ef27202428688f86404e8278587017012"
|
|
"c1d65537c6cbd7dde04aae338d68115a9f430afc100ab83cdadf45dca39db685"),
|
|
wvutil::a2b_hex(
|
|
"51b0133e2e5f40c22d10ec0562799e5e4118aacaa6a820be976acee4b7689280"
|
|
"541b56836c454414fbaebf9a3142baa2c8009a4f19ac033665bd3495f2ad19f3"
|
|
"3b850d1e8e6957172571e82d3c812d03588c95a1ef49f08b33f21ea4f2d382c9"
|
|
"28704e329847e5fe98966949f39e272ec30126f5d7a4ae21d3a0d25aa8ccf637"
|
|
"d5f880a6733b07bdd33cfdc3c36dece2bffb6049f218162b024df4f800557568"
|
|
"a792e0add16fc388ee13595313c3fbeef28f69737523e449dc2cf893f0566a79"
|
|
"8a83110c8d3aaf5c1f7e8e8fe355a294a9a77b5494704b18e27f1315cb19c104"
|
|
"3ad2061a2a414d40cc768fc4c8f49a3905e3a82095aa4eef6a1ad8af1029fced"),
|
|
wvutil::a2b_hex("f6f4b1e600a5b67813ed2bded913ba9f"), 0,
|
|
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample}};
|
|
|
|
const SubSampleInfo kEntitlementWithKeyRotationSubSampleDualPssh[] = {
|
|
{true, 1, true, true, false,
|
|
wvutil::a2bs_hex("1D9B8E13B59951169348FF8D5B9394C0"),
|
|
wvutil::a2b_hex(
|
|
"64ab17b3e3dfab47245c7cce4543d4fc7a26dcf248f19f9b59f3c92601440b36"
|
|
"17c8ed0c96c656549e461f38708cd47a434066f8df28ccc28b79252eee3f9c2d"
|
|
"7f6c68ebe40141fe818fe082ca523c03d69ddaf183a93c022327fedc5582c5ab"
|
|
"ca9d342b71263a67f9cb2336f12108aaaef464f17177e44e9b0c4e56e61da53c"
|
|
"2150b4405cc82d994dfd9bf4087c761956d6688a9705db4cf350381085f383c4"
|
|
"9666d4aed135c519c1f0b5cba06e287feea96ea367bf54e7368dcf998276c6e4"
|
|
"6497e0c50e20fef74e42cb518fe7f22ef27202428688f86404e8278587017012"
|
|
"c1d65537c6cbd7dde04aae338d68115a9f430afc100ab83cdadf45dca39db685"),
|
|
wvutil::a2b_hex(
|
|
"4bb2ff540e12e4c97248b63abdf30c4474df11ae8f22ba587e5aa9b64d51f8ce"
|
|
"209b13cb24f436ac192060690d13d5a1230fe5207287678a3acbaf59b5381186"
|
|
"92dcdec42c770afc0545407c243a452214d0497f1a044adc56ac1dba5530d5a5"
|
|
"482f9fc67a5e1d1314e864ad85fec9f78657e10f68ae8720b218339c96e878c1"
|
|
"c0f09015172d8a52a85b6f09526b98aad6d7326d3799a418581efadd16f9ba3e"
|
|
"454945428a36959a296aa14fe05cb8ae7b44ae68d82950f0742d38d86f167c36"
|
|
"75e75390d3cc6cd6db267729b2aa81a7e7c4db186e82d4300c4123c0a5de73e9"
|
|
"a6bb238bd351769359d1b46c9702270b756038fd54ef609d985eecde58e9a58e"),
|
|
wvutil::a2b_hex("f6f4b1e600a5b67813ed2bded913ba9f"), 0,
|
|
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample},
|
|
{true, 1, true, true, false,
|
|
wvutil::a2bs_hex("A0396729B3795B46A5EA7F9919B96A67"),
|
|
wvutil::a2b_hex(
|
|
"64ab17b3e3dfab47245c7cce4543d4fc7a26dcf248f19f9b59f3c92601440b36"
|
|
"17c8ed0c96c656549e461f38708cd47a434066f8df28ccc28b79252eee3f9c2d"
|
|
"7f6c68ebe40141fe818fe082ca523c03d69ddaf183a93c022327fedc5582c5ab"
|
|
"ca9d342b71263a67f9cb2336f12108aaaef464f17177e44e9b0c4e56e61da53c"
|
|
"2150b4405cc82d994dfd9bf4087c761956d6688a9705db4cf350381085f383c4"
|
|
"9666d4aed135c519c1f0b5cba06e287feea96ea367bf54e7368dcf998276c6e4"
|
|
"6497e0c50e20fef74e42cb518fe7f22ef27202428688f86404e8278587017012"
|
|
"c1d65537c6cbd7dde04aae338d68115a9f430afc100ab83cdadf45dca39db685"),
|
|
wvutil::a2b_hex(
|
|
"49067453f9fe1b36fb2b37a2bba927bfe7f5f81ce715047beb99675da809b502"
|
|
"a5638f891cfad95c9fefdb32b6e8614ce3d5528032d51644a6cfaaccea2ad0b6"
|
|
"dd8bd36a0fb751bf4b70e1cb02266f373be467d3167aed4f3820eb4af884cc39"
|
|
"f60f83e060c8674b7e53d7ec8934ec07750d4677ed14ad6f6bacf46f46cf3ea8"
|
|
"560f704220bc3e32b9ad21c74aff6dbdbd64f49f38717ab7a05042dfe6fdc56f"
|
|
"47ddc384822b9a3fab3445653fa51f2405fcbcfd6d39a7fb8a99777c41960b94"
|
|
"74f1deb4b9b242bef609c625af791cba63e8c184b0312d624daba3889307b48b"
|
|
"00c362f246c3bc0b36cd41f9ec8eb72eab603f9517c7948f5e317a93ac1a5631"),
|
|
wvutil::a2b_hex("f6f4b1e600a5b67813ed2bded913ba9f"), 0,
|
|
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample}};
|
|
|
|
// License duration and uncertainty window
|
|
const uint32_t kSingleEncryptedSubSampleIcpLicenseDurationExpiration = 5;
|
|
const uint32_t kSingleEncryptedSubSampleIcpLicenseExpirationWindow = 2;
|
|
|
|
struct SessionSharingSubSampleInfo {
|
|
const SubSampleInfo* sub_sample;
|
|
bool session_sharing_enabled;
|
|
wvcdm::CdmIdentifier cdm_identifier;
|
|
};
|
|
|
|
const SessionSharingSubSampleInfo kSessionSharingSubSamples[] = {
|
|
{&kClearSubSample, false, wvcdm::kDefaultCdmIdentifier},
|
|
{&kClearSubSample, true, wvcdm::kDefaultCdmIdentifier},
|
|
{&kClearSubSampleNoKey, false, wvcdm::kDefaultCdmIdentifier},
|
|
{&kClearSubSampleNoKey, true, wvcdm::kDefaultCdmIdentifier},
|
|
{&kSingleEncryptedSubSample, false, wvcdm::kDefaultCdmIdentifier},
|
|
{&kSingleEncryptedSubSample, true, wvcdm::kDefaultCdmIdentifier},
|
|
// The last entry simulates session sharing using the non default
|
|
// identifier.
|
|
{&kSingleEncryptedSubSample, true, kExampleIdentifier}};
|
|
|
|
struct SecureStopSubSampleInfo {
|
|
const SubSampleInfo* sub_sample;
|
|
size_t sub_sample_count;
|
|
wvcdm::RequestedSecurityLevel security_level;
|
|
std::string app_id;
|
|
};
|
|
|
|
void PrintTo(const SecureStopSubSampleInfo* param, std::ostream* os) {
|
|
if (param) {
|
|
*os << "sub_sample_count=" << param->sub_sample_count << ", "
|
|
<< "level=" << (param->security_level == wvcdm::kLevel3 ? "L3" : "L1")
|
|
<< " app_id=" << param->app_id;
|
|
} else {
|
|
*os << "<null ptr>";
|
|
}
|
|
}
|
|
|
|
const SecureStopSubSampleInfo kSecureStopSubSampleInfo[] = {
|
|
{&kSecureStopSubSamplesIcp[0], 1, wvcdm::kLevelDefault, ""},
|
|
{&kSecureStopSubSamplesIcp[0], 3, wvcdm::kLevelDefault, ""},
|
|
{&kSecureStopSubSamplesIcp[0], 5, wvcdm::kLevelDefault, ""},
|
|
{&kSecureStopSubSamplesIcp[0], 3, wvcdm::kLevelDefault, "other app id"},
|
|
{&kSecureStopSubSamplesIcp[0], 1, wvcdm::kLevel3, ""},
|
|
{&kSecureStopSubSamplesIcp[0], 3, wvcdm::kLevel3, ""},
|
|
{&kSecureStopSubSamplesIcp[0], 5, wvcdm::kLevel3, ""},
|
|
{&kSecureStopSubSamplesIcp[0], 3, wvcdm::kLevel3, "other app id"}};
|
|
|
|
struct UsageLicenseAndSubSampleInfo {
|
|
std::string pssh;
|
|
const SubSampleInfo* sub_sample;
|
|
std::string provider_session_token;
|
|
};
|
|
|
|
const std::string kPsshStreamingClip3 = wvutil::a2bs_hex(
|
|
"000000427073736800000000" // blob size and pssh
|
|
"EDEF8BA979D64ACEA3C827DCD51D21ED00000022" // Widevine system id
|
|
"08011a0d7769646576696e655f74657374220f73" // pssh data
|
|
"747265616d696e675f636c697033");
|
|
const std::string kPsshStreamingClip4 = wvutil::a2bs_hex(
|
|
"000000427073736800000000" // blob size and pssh
|
|
"EDEF8BA979D64ACEA3C827DCD51D21ED00000022" // Widevine system id
|
|
"08011a0d7769646576696e655f74657374220f73" // pssh data
|
|
"747265616d696e675f636c697034");
|
|
const std::string kPsshStreamingClip5 = wvutil::a2bs_hex(
|
|
"000000427073736800000000" // blob size and pssh
|
|
"EDEF8BA979D64ACEA3C827DCD51D21ED00000022" // Widevine system id
|
|
"08011a0d7769646576696e655f74657374220f73" // pssh data
|
|
"747265616d696e675f636c697035");
|
|
const std::string kPsshStreamingClip7 = wvutil::a2bs_hex(
|
|
"000000427073736800000000" // blob size and pssh
|
|
"EDEF8BA979D64ACEA3C827DCD51D21ED00000022" // Widevine system id
|
|
"08011a0d7769646576696e655f74657374220f73" // pssh data
|
|
"747265616d696e675f636c697037");
|
|
const std::string kPsshStreamingClip20 = wvutil::a2bs_hex(
|
|
"000000437073736800000000" // blob size and pssh
|
|
"EDEF8BA979D64ACEA3C827DCD51D21ED00000023" // Widevine system id
|
|
"08011a0d7769646576696e655f746573" // pssh data
|
|
"74221073747265616d696e675f636c69703230");
|
|
const std::string kPsshStreamingClip21 = wvutil::a2bs_hex(
|
|
"000000437073736800000000" // blob size and pssh
|
|
"EDEF8BA979D64ACEA3C827DCD51D21ED00000023" // Widevine system id
|
|
"08011a0d7769646576696e655f746573" // pssh data
|
|
"74221073747265616d696e675f636c69703231");
|
|
const std::string kSinglePsshEntitlementWithKeyRotation[] = {
|
|
wvutil::a2bs_hex(
|
|
"000001fb7073736800000000" // blob size and pssh
|
|
"edef8ba979d64acea3c827dcd51d21ed000001db" // Widevine system id
|
|
"220b47726f7570563254657374381448" // pssh data
|
|
"e3dc959b065002580272580a10668093"
|
|
"381a8c5be48a0168ce372726ac1210c8"
|
|
"326486bb5d5c4a958f00b1111afc811a"
|
|
"20082cd9d3aed3ebe6239d30fbcf0b22"
|
|
"1d28cbb0360ea1295c2363973346ec00"
|
|
"512210914781334e864c8eb7f768cf26"
|
|
"49073872580a10f872d11d5b1052f2bd"
|
|
"a94e60a0e383021210450897c987a85c"
|
|
"2e9579f968554a12991a2097e603ceea"
|
|
"f35ed8cef1029eae7a0a54701e3d6db6"
|
|
"80e7da1de3b22a8db347fb2210b41c34"
|
|
"29b7bb96972bbaf6587bc0ddf172580a"
|
|
"10bac58b9fce9e5929a42a180e529f19"
|
|
"4712103f11f22988d25659b145ce4854"
|
|
"3e6b141a20416e22768e5a57b08d155e"
|
|
"5210d00658056947ff06d626668bceb3"
|
|
"5eb01c6b57221081fb2ff3fef79d332f"
|
|
"f98be46233596972580a101261c8036d"
|
|
"ae5c8caa968858aa0ca9cc12106d583c"
|
|
"b37c1456519843a81cf49912221a20c2"
|
|
"1116bb54a226e8d879a4cd41d8879920"
|
|
"2ae85b80d83b1b4447e5d7fcad6f6a22"
|
|
"100b27a4c3f44771d2b0c7c34c66af35"
|
|
"b572580a10ab1c8c259c6b5967991389"
|
|
"65bff5ac0c1210b5b4473658565d3786"
|
|
"efaf4b85d8e6e21a203ce6a9085285c2"
|
|
"ece0b650dc83dd7aa8ac849611a8e3f8"
|
|
"3c8f389223c0f3621522101946f0c2a3"
|
|
"d543101cc842bbec2d0b30"),
|
|
wvutil::a2bs_hex(
|
|
"000001fb7073736800000000" // blob size and pssh
|
|
"edef8ba979d64acea3c827dcd51d21ed000001db" // Widevine system id
|
|
"220b47726f7570563254657374381548" // pssh data
|
|
"e3dc959b065002580272580a10668093"
|
|
"381a8c5be48a0168ce372726ac1210f8"
|
|
"488775a99855ff94b93ec5bd4993561a"
|
|
"20d15ba631c20e95da0d4857f6a1d25a"
|
|
"a3bccbd3fde18b3fdc1dd8c4f0ede76f"
|
|
"402210d6dd3675f0d1150052e81b9107"
|
|
"6d7fc172580a10f872d11d5b1052f2bd"
|
|
"a94e60a0e383021210ad1f93ad921e53"
|
|
"b097c415b2bf1ef1c61a20b2087b60a2"
|
|
"d253ac2158a1bfa789b150b79701b29e"
|
|
"c852a2662560f8b8977a4c2210051ed3"
|
|
"2628671fbda58f506ba5ea713972580a"
|
|
"10bac58b9fce9e5929a42a180e529f19"
|
|
"47121027cdda7bfe5e5fd4bff2ebc9c7"
|
|
"c020701a20f2cb1184d648a2404517e6"
|
|
"7a39d698332aae6bb890a69bf7ddb536"
|
|
"75b8ac41c62210a80ed7f9b728fdd566"
|
|
"0b01b173ace26372580a101261c8036d"
|
|
"ae5c8caa968858aa0ca9cc1210769a70"
|
|
"0442a25bf5ae17174c70f4cb8e1a206c"
|
|
"7b2012723fc47c83b003ea214204915f"
|
|
"9a63dc373bf219f36ccf5697589aa422"
|
|
"10bcc3c16e836cca264d5493a0c334d3"
|
|
"4872580a10ab1c8c259c6b5967991389"
|
|
"65bff5ac0c1210894b04aef78557c6a7"
|
|
"e6e8855febbcc91a2025cc545ee3cd0c"
|
|
"c323586610ff6a8f8f22a78f5fade2f2"
|
|
"1083f152c52208f16d2210257aacacec"
|
|
"512a2e769396b10e6d9dfa")};
|
|
const std::string kDualPsshEntitlementWithKeyRotation[] = {
|
|
wvutil::a2bs_hex(
|
|
"0000003e7073736800000000" // blob size and pssh
|
|
"edef8ba979d64acea3c827dcd51d21ed0000001e" // Widevine system id
|
|
"2210426f6f5261646c6579666137613664393800" // pssh data
|
|
"48e3dc959b0650126a00000002027073"
|
|
"736800000000edef8ba979d64acea3c8"
|
|
"27dcd51d21ed000001e22210426f6f52"
|
|
"61646c657966613761366439380048e3"
|
|
"dc959b06501258026a0072580a109ab3"
|
|
"fb7eaffc5ee087568b9581ff0e721210"
|
|
"4a2da018480c51358c660c4b1a198721"
|
|
"1a20a00425ef5fe538adba59a6c4ce9a"
|
|
"fbff394351b79552643e08ed3a0cde2c"
|
|
"3ca42210931a2ef95b898d804b9a25ef"
|
|
"6d2805f072580a100fba9bd0f8e055d5"
|
|
"900bbfaabf5033141210f9bfca8cd3b8"
|
|
"593897f0b9283cd768f61a209c1d9a96"
|
|
"573a4dbba68327f03e598adfde1bd29f"
|
|
"1e709f206c2424d82ca14c1c22108428"
|
|
"46cffba58c12742418a702138c3a7258"
|
|
"0a109fec6ad1d36e56cdaf5c232480f6"
|
|
"3f8812101d9b8e13b59951169348ff8d"
|
|
"5b9394c01a2058ee6164ae759802e0b1"
|
|
"f9f7eeeaa3606faf21182777fe716c01"
|
|
"4c2412621543221039c8a6caab12515e"
|
|
"3c744a5447e9b72372580a10cd2e9ebf"
|
|
"89c257e2a3196c82ac4c76ba1210d48f"
|
|
"8f11c2d853289e981f5775db60441a20"
|
|
"3b491980b27587592c86e73da27caa12"
|
|
"d95acda2295f768746090b55b81c9d61"
|
|
"2210dd7f29944aaeb08826ba4fc0dec6"
|
|
"c88d72580a10b3fd79204b9d5cbf8f67"
|
|
"2eca27255e9e121023feaaf517585fde"
|
|
"bdf0d4693f32c1091a20d18a1c0e54b8"
|
|
"fbc189b58ccc0dc5a0c541b628b8fe23"
|
|
"34c862944b5555ad7f7f221091269127"
|
|
"e4feb6a8fa878c6e9781a55f"),
|
|
wvutil::a2bs_hex(
|
|
"0000003e7073736800000000ed" // blob size and pssh
|
|
"ef8ba979d64acea3c827dcd51d21ed0000001e" // Widevine system id
|
|
"2210426f6f5261646c65796661" // pssh data
|
|
"3761366439380448e3dc959b0650126a"
|
|
"00000002027073736800000000edef8b"
|
|
"a979d64acea3c827dcd51d21ed000001"
|
|
"e22210426f6f5261646c657966613761"
|
|
"366439380448e3dc959b06501258026a"
|
|
"0072580a109ab3fb7eaffc5ee087568b"
|
|
"9581ff0e721210777bc13c94c4568b84"
|
|
"ddaf99de3f03311a2042949649205d0d"
|
|
"f4f4a4eec94263561931066a9fc18b61"
|
|
"8b3929a168b4c2404222101e7aad142f"
|
|
"59edb962816e8d0702356b72580a100f"
|
|
"ba9bd0f8e055d5900bbfaabf50331412"
|
|
"10537cb8f017ec59e7be3c309e6d7f4b"
|
|
"5d1a2089c49c0f0e214c3765cbb37ab5"
|
|
"41dc0625c0087fa94317528ee8265431"
|
|
"71cabe2210dddba401d4eaaa76437638"
|
|
"a29dbdc38472580a109fec6ad1d36e56"
|
|
"cdaf5c232480f63f881210a0396729b3"
|
|
"795b46a5ea7f9919b96a671a209245a1"
|
|
"c821de9c65fb1086d9af8aaca4b7da0e"
|
|
"bbd0850a56ab36c23bb71b507e2210b0"
|
|
"531d2235575ff9ba5af4545bb43fdd72"
|
|
"580a10cd2e9ebf89c257e2a3196c82ac"
|
|
"4c76ba121011647820b33352349942cd"
|
|
"31ce352e571a20277b4392c76a04335d"
|
|
"3eadf22705184eb1adc057a61e372e78"
|
|
"30b7a20361d2472210d954557dc853a7"
|
|
"42283e6dfe16677a6a72580a10b3fd79"
|
|
"204b9d5cbf8f672eca27255e9e1210c4"
|
|
"78fb09c0c6531b92c571972c36098b1a"
|
|
"20bf17c678ac01685e258192eb4d2d49"
|
|
"157c3a07a95342d8be8b2f9f121f596b"
|
|
"8622100d9cfe972bc17003b49ecd5f45"
|
|
"f3bb28")};
|
|
|
|
const std::string kProviderSessionTokenStreamingClip3 =
|
|
wvutil::a2bs_hex("4851305A4A4156485A554936444E4931");
|
|
const std::string kProviderSessionTokenStreamingClip4 =
|
|
wvutil::a2bs_hex("4942524F4355544E5557553145463243");
|
|
const std::string kProviderSessionTokenStreamingClip7 =
|
|
wvutil::a2bs_hex("44434C53524F4E30394C4E5535544B4C");
|
|
const std::string kProviderSessionTokenStreamingClip20 =
|
|
wvutil::a2bs_hex("4851305A4A4156485A554936444E4931");
|
|
const std::string kProviderSessionTokenStreamingClip21 =
|
|
wvutil::a2bs_hex("4851305A4A4156485A554936444E4931");
|
|
|
|
// playback duration is 10 seconds+uncertainty window
|
|
const std::chrono::milliseconds
|
|
kExpirationStreamingClip21PlaybackDurationTimeMs =
|
|
std::chrono::milliseconds(12 * 1000);
|
|
|
|
const UsageLicenseAndSubSampleInfo kUsageLicenseTestVector1[] = {
|
|
{kPsshStreamingClip3, &kSecureStopSubSamplesIcp[0],
|
|
kProviderSessionTokenStreamingClip3},
|
|
{kPsshStreamingClip4, &kSecureStopSubSamplesIcp[1],
|
|
kProviderSessionTokenStreamingClip4},
|
|
};
|
|
|
|
const UsageLicenseAndSubSampleInfo kUsageLicenseTestVector2[] = {
|
|
{kPsshStreamingClip7, &kSecureStopSubSamplesIcp[4],
|
|
kProviderSessionTokenStreamingClip7},
|
|
// TODO(rfrias): Add another streaming usage license. Streaming
|
|
// clip 5 has includes a randomly generated PST, while
|
|
// streaming clip 6 does not include a PST.
|
|
};
|
|
|
|
struct RenewWithClientIdTestConfiguration {
|
|
bool always_include_client_id;
|
|
bool specify_app_parameters;
|
|
bool enable_privacy_mode;
|
|
bool specify_service_certificate;
|
|
std::string test_description;
|
|
};
|
|
|
|
void PrintTo(const RenewWithClientIdTestConfiguration* param,
|
|
std::ostream* os) {
|
|
if (param) {
|
|
*os << param->test_description;
|
|
} else {
|
|
*os << "<null ptr>";
|
|
}
|
|
}
|
|
|
|
const RenewWithClientIdTestConfiguration
|
|
kStreamingRenewClientIdTestConfiguration[] = {
|
|
{false, false, false, false,
|
|
"Test: Streaming renewal without client Id"},
|
|
{true, false, false, false, "Test: Streaming renewal with client Id"},
|
|
{true, true, false, false,
|
|
"Test: Streaming renewal with app parameters"},
|
|
{true, false, true, false,
|
|
"Test: Streaming renewal fetch service cert"},
|
|
{true, false, true, true,
|
|
"Test: Streaming renewal, service cert provided"}};
|
|
|
|
// Note: Offline renewal/release with encrypted client Ids and where a service
|
|
// certificate needs to be fetched is not supported.
|
|
const RenewWithClientIdTestConfiguration
|
|
kOfflineReleaseClientIdTestConfiguration[] = {
|
|
{false, false, false, false,
|
|
"Test: Offline renewal/release without client Id"},
|
|
{true, false, false, false,
|
|
"Test: Offline renewal/release with client Id"},
|
|
{true, true, false, false,
|
|
"Test: Offline renewal/release with app parameters"},
|
|
{true, false, true, true,
|
|
"Test: Offline renewal/release, service cert provided"}};
|
|
|
|
const RenewWithClientIdTestConfiguration kUsageClientIdTestConfiguration[] = {
|
|
{false, false, false, false, "Test: Usage reporting without client Id"},
|
|
{true, false, false, false, "Test: Usage reporting with client Id"}};
|
|
|
|
struct EntitlementTestConfiguration {
|
|
std::string entitlement_pssh;
|
|
std::string key_rotation_pssh;
|
|
const SubSampleInfo* sub_sample_with_initial_keys;
|
|
const SubSampleInfo* sub_sample_with_rotated_keys;
|
|
};
|
|
|
|
const EntitlementTestConfiguration kEntitlementTestConfiguration[] = {
|
|
{// Single Widevine PSSH containing PSSH data of type ENTITLED_KEY
|
|
kSinglePsshEntitlementWithKeyRotation[0],
|
|
kSinglePsshEntitlementWithKeyRotation[1],
|
|
&kEntitlementWithKeyRotationSubSampleSinglePssh[0],
|
|
&kEntitlementWithKeyRotationSubSampleSinglePssh[1]},
|
|
{// Two Widevine PSSHs containing PSSH data of type SINGLE, ENTITLED_KEY
|
|
kDualPsshEntitlementWithKeyRotation[0],
|
|
kDualPsshEntitlementWithKeyRotation[1],
|
|
&kEntitlementWithKeyRotationSubSampleDualPssh[0],
|
|
&kEntitlementWithKeyRotationSubSampleDualPssh[1]},
|
|
};
|
|
|
|
// provider:"widevine_test",
|
|
// content_id":"aGxzX3NhbXBsZV9hZXNfc3RyZWFtaW5n" (hls_sample_aes_streaming)
|
|
// key_id:613db35603320eb8e7ea24bdeea3fdb8
|
|
// key:78a1dc0646119707e903514d8a00735f
|
|
const std::string kAttributeListSampleAes =
|
|
"#EXT-X-KEY:"
|
|
"METHOD=SAMPLE-AES,"
|
|
"KEYFORMAT=\"com.widevine\","
|
|
"KEYFORMATVERSIONS=\"1\","
|
|
"URI=\"data:text/"
|
|
"plain;base64,"
|
|
"ew0KICAgInByb3ZpZGVyIjoid2lkZXZpbmVfdGVzdCIsDQogICAiY29udGVudF9pZCI6ImFHeH"
|
|
"pYM05oYlhCc1pWOWhaWE5mYzNSeVpXRnRhVzVuIiwNCiAgICJrZXlfaWRzIjoNCiAgIFsNCiAg"
|
|
"ICAgICI2MTNkYjM1NjAzMzIwZWI4ZTdlYTI0YmRlZWEzZmRiOCINCiAgIF0NCn0=\","
|
|
"IV=0x9FBE45DD47DA7EBA09A3E24CBA95C9AF";
|
|
|
|
// provider:"widevine_test",
|
|
// content_id":"aGxzX2Flc18xMjhfc3RyZWFtaW5n" (hls_aes_128_streaming)
|
|
// key_id:ab6531ff6e6ea15e387b019e59c2de0a
|
|
// key:78a1dc0646119707e903514d8a00735f
|
|
const std::string kAttributeListAes128 =
|
|
"#EXT-X-KEY:"
|
|
"METHOD=AES-128,"
|
|
"KEYFORMAT=\"com.widevine\","
|
|
"KEYFORMATVERSIONS=\"1\","
|
|
"URI=\"data:text/"
|
|
"plain;base64,"
|
|
"ew0KICAgInByb3ZpZGVyIjoid2lkZXZpbmVfdGVzdCIsDQogICAiY29udGVudF9pZCI6ImFHeH"
|
|
"pYMkZsYzE4eE1qaGZjM1J5WldGdGFXNW4iLA0KICAgImtleV9pZHMiOg0KICAgWw0KICAgICAg"
|
|
"ImFiNjUzMWZmNmU2ZWExNWUzODdiMDE5ZTU5YzJkZTBhIg0KICAgXQ0KfQ==\","
|
|
"IV=0x9FBE45DD47DA7EBA09A3E24CBA95C9AF";
|
|
|
|
const std::string kAttributeListKeyFormatNonWidevine =
|
|
"#EXT-X-KEY:METHOD=SAMPLE-AES,KEYFORMAT=\"com.example\",KEYFORMATVERSIONS="
|
|
"\"1\",URI=\"data:text/"
|
|
"plain;base64,"
|
|
"eyAKICAgInByb3ZpZGVyIjoiY2FzdCIsCiAgICJjb250ZW50X2lkIjoiU0VKUElGTkJUVkJNUl"
|
|
"NBeCIsCiAgICJrZXlfaWRzIjoKICAgWwogICAgICAiZDhlMzQ0ODM1ZTM1NzA3MjUzZDU3MWYz"
|
|
"NjE0ZTdmMmYiCiAgIF0KfQ==\",IV=0x9FBE45DD47DA7EBA09A3E24CBA95C9AF";
|
|
|
|
const std::string kAttributeListKeyFormatVersionUnregonized =
|
|
"#EXT-X-KEY:METHOD=SAMPLE-AES,KEYFORMAT=\"com.widevine\",KEYFORMATVERSIONS="
|
|
"\"2\",URI=\"data:text/"
|
|
"plain;base64,"
|
|
"eyAKICAgInByb3ZpZGVyIjoiY2FzdCIsCiAgICJjb250ZW50X2lkIjoiU0VKUElGTkJUVkJNUl"
|
|
"NBeCIsCiAgICJrZXlfaWRzIjoKICAgWwogICAgICAiZDhlMzQ0ODM1ZTM1NzA3MjUzZDU3MWYz"
|
|
"NjE0ZTdmMmYiCiAgIF0KfQ==\",IV=0x9FBE45DD47DA7EBA09A3E24CBA95C9AF";
|
|
|
|
const std::string kAttributeListUnspecifiedIv =
|
|
"#EXT-X-KEY:"
|
|
"METHOD=SAMPLE-AES,"
|
|
"KEYFORMAT=\"com.widevine\","
|
|
"KEYFORMATVERSIONS=\"1\","
|
|
"URI=\"data:text/plain;base64,ew0KICAgInByb3ZpZGVyIjoid2lkZXZpbmVfdGVzdCIsD"
|
|
"QogICAiY29udGVudF9pZCI6ImFHeHpYM05oYlhCc1pWOWhaWE5mYzNSeVpXRnRhVzVuIiwNCiA"
|
|
"gICJrZXlfaWRzIjoNCiAgIFsNCiAgICAgICI3OGExZGMwNjQ2MTE5NzA3ZTkwMzUxNGQ4YTAwN"
|
|
"zM1ZiINCiAgIF0NCn0=\",";
|
|
|
|
const std::string kAttributeListUnspecifiedMethod =
|
|
"#EXT-X-KEY:"
|
|
"KEYFORMAT=\"com.widevine\","
|
|
"KEYFORMATVERSIONS=\"1\","
|
|
"URI=\"data:text/plain;base64,ew0KICAgInByb3ZpZGVyIjoid2lkZXZpbmVfdGVzdCIsD"
|
|
"QogICAiY29udGVudF9pZCI6ImFHeHpYM05oYlhCc1pWOWhaWE5mYzNSeVpXRnRhVzVuIiwNCiA"
|
|
"gICJrZXlfaWRzIjoNCiAgIFsNCiAgICAgICI3OGExZGMwNjQ2MTE5NzA3ZTkwMzUxNGQ4YTAwN"
|
|
"zM1ZiINCiAgIF0NCn0=\","
|
|
"IV=0x9FBE45DD47DA7EBA09A3E24CBA95C9AF";
|
|
|
|
struct HlsSegmentInfo {
|
|
bool start_segment;
|
|
std::string iv;
|
|
std::string clear_data;
|
|
std::string encrypted_data;
|
|
uint8_t subsample_flags;
|
|
};
|
|
|
|
const HlsSegmentInfo kAes128SingleSegmentInfo[] = {
|
|
{
|
|
true,
|
|
wvutil::a2bs_hex("9FBE45DD47DA7EBA09A3E24CBA95C9AF"),
|
|
wvutil::a2bs_hex(
|
|
"AD868A6AC55C10D564FC23B8ACFF407DAAF4ED2743520E02CDA9680D9EA88E9148"
|
|
"84604C8DA8A53CE33DB9FF8F1C5BB6BB97F37B39906BF41596555C1BCCE9ED0293"
|
|
"59C4CF5906B6AB5BF60FBB3F1A1C7C59ACFC7E4FB4AD8E623C04D503A3DDAD868A"
|
|
"6AC55C10D564FC23B8ACFF407DAAF4ED2743520E02CDA9680D9EA88E918DF21FDC"
|
|
"42F166880D97A2225CD5C9EA5E7B752F4CF81BBDBE98E542EE10E1C6595D259A1E"
|
|
"4A8B6D7744CD98C5D3F921ADC252EB7D8AF6B916044B676A574747942AC20A89FF"
|
|
"79F4C2F25FBA99D6A44618A8C0420B27D54E3DA17B77C9D43CCA217CE9BDE99BD9"
|
|
"1E9733A1A00B9B557AC3A433DC92633546156817FAE26B6E1C"),
|
|
wvutil::a2bs_hex(
|
|
"E7C566D86E98C36D2DCA54A966E7B469B6094B9201F200932F8EB5E1A94FB0B977"
|
|
"FAB8DFDAD57C677E279615F4EAFA872CA8EFF83179E4DE2AB78E6B41A860C42203"
|
|
"4B875AC282406E03AC01F2E407A55DE38C6C35707F34B3319646FA016A01CE9056"
|
|
"E55D28C48ED72F10FA6625656ED62B758CBADA757DDC52533C9CBD54FC1A46F827"
|
|
"CC7B69BA66AE19A15D725FCBB972B23C83F95C0F00F481A7C9AC8687B3A4AD15AD"
|
|
"A2ABBB84D8E3CBEC3E8771720A35234070799173B37219127141922CBA8CB2DC48"
|
|
"EC2477832D1AE477942DCDA93C0886AF72D71E56DA3D7737E49670B024639A195B"
|
|
"7377C7F45A797C6E5DBB1BB2843DA3FC76043E33687BEF3172"),
|
|
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample,
|
|
},
|
|
};
|
|
|
|
const HlsSegmentInfo kAes128PartialSegmentInfo[] = {
|
|
{
|
|
true,
|
|
wvutil::a2bs_hex("9FBE45DD47DA7EBA09A3E24CBA95C9AF"),
|
|
wvutil::a2bs_hex(
|
|
"AD868A6AC55C10D564FC23B8ACFF407DAAF4ED2743520E02CDA9680D9EA88E9148"
|
|
"84604C8DA8A53CE33DB9FF8F1C5BB6BB97F37B39906BF41596555C1BCCE9ED0293"
|
|
"59C4CF5906B6AB5BF60FBB3F1A1C7C59ACFC7E4FB4AD8E623C04D503A3DDAD868A"
|
|
"6AC55C10D564FC23B8ACFF407DAAF4ED2743520E02CDA9680D9EA88E918DF21FDC"
|
|
"42F166880D97A2225CD5C9EA5E7B752F4CF81BBDBE98E542EE10E1C6595D259A1E"
|
|
"4A8B6D7744CD98C5D3F921ADC252EB7D8AF6B916044B676A574747942AC20A89FF"
|
|
"79F4C2F25FBA99D6A44618A8C0420B27D54E3DA17B77C9D43CCA217CE9BDE99BD9"
|
|
"1E9733A1A00B9B557AC3A433DC92633546156817FAE26B6E1C"),
|
|
wvutil::a2bs_hex(
|
|
"E7C566D86E98C36D2DCA54A966E7B469B6094B9201F200932F8EB5E1A94FB0B977"
|
|
"FAB8DFDAD57C677E279615F4EAFA872CA8EFF83179E4DE2AB78E6B41A860C42203"
|
|
"4B875AC282406E03AC01F2E407A55DE38C6C35707F34B3319646FA016A01CE9056"
|
|
"E55D28C48ED72F10FA6625656ED62B758CBADA757DDC52533C9CBD54FC1A46F827"
|
|
"CC7B69BA66AE19A15D725FCBB972B23C83F95C0F00F481A7C9AC8687B3A4AD15AD"
|
|
"A2ABBB84D8E3CBEC3E8771720A35234070799173B37219127141922CBA8CB2DC48"
|
|
"EC2477832D1AE477942DCDA93C0886AF72D71E56DA3D7737E49670B024639A195B"
|
|
"7377C7F45A797C6E5DBB1BB2843DA3FC76043E33687BEF3172"),
|
|
OEMCrypto_FirstSubsample,
|
|
},
|
|
{
|
|
false,
|
|
wvutil::a2bs_hex("BB1BB2843DA3FC76043E33687BEF3172"),
|
|
wvutil::a2bs_hex(
|
|
"64ab17b3e3dfab47245c7cce4543d4fc7a26dcf248f19f9b59f3c92601440b3617"
|
|
"c8ed0c96c656549e461f38708cd47a434066f8df28ccc28b79252eee3f9c2d7f6c"
|
|
"68ebe40141fe818fe082ca523c03d69ddaf183a93c022327fedc5582c5abca9d34"
|
|
"2b71263a67f9cb2336f12108aaaef464f17177e44e9b0c4e56e61da53c2150b440"
|
|
"5cc82d994dfd9bf4087c761956d6688a9705db4cf350381085f383c49666d4aed1"
|
|
"35c519c1f0b5cba06e287feea96ea367bf54e7368dcf998276c6e46497e0c50e20"
|
|
"fef74e42cb518fe7f22ef27202428688f86404e8278587017012c1d65537c6cbd7"
|
|
"dde04aae338d68115a9f430afc100ab83cdadf45dca39db685"),
|
|
wvutil::a2bs_hex(
|
|
"5E61E95F918560A046682BB03CF3DBAB6E052150AEB3D46F458CF7480BF8F875E2"
|
|
"833367415656E4BA2B01421A0B18C18F802657573BF0B239173114AFE992BCD852"
|
|
"955301FAE562FC6BCC3AC89A4CA58BEC1BCCD07C89165DF77F29F42AE052858BA3"
|
|
"AC59A82652E6B432CB4993C707755C32EEF137111160A086CFC4FB15E5F76B1DAF"
|
|
"2E8217C51BDB4DE0B987288A68F88824DBD7BF4D2978147A4AE4DA019AAA2A59E9"
|
|
"94C294768D86FDADED52931E7ACEBC915C8124785E4BE9CE89BFFA631C3F5E67C4"
|
|
"EE5BA77A83B6E582DF2C1907FA572B61D656DCF9B8DEAE4B81508378732F952152"
|
|
"CF7DCAB0A3CA3EE8E5F72D24E96647EBBAAA2394BD6EC458EF"),
|
|
OEMCrypto_LastSubsample,
|
|
},
|
|
};
|
|
|
|
const HlsSegmentInfo kAes128MultiSegmentInfo[] = {
|
|
{
|
|
true,
|
|
wvutil::a2bs_hex("9FBE45DD47DA7EBA09A3E24CBA95C9AF"),
|
|
wvutil::a2bs_hex(
|
|
"AD868A6AC55C10D564FC23B8ACFF407DAAF4ED2743520E02CDA9680D9EA88E9148"
|
|
"84604C8DA8A53CE33DB9FF8F1C5BB6BB97F37B39906BF41596555C1BCCE9ED0293"
|
|
"59C4CF5906B6AB5BF60FBB3F1A1C7C59ACFC7E4FB4AD8E623C04D503A3DDAD868A"
|
|
"6AC55C10D564FC23B8ACFF407DAAF4ED2743520E02CDA9680D9EA88E918DF21FDC"
|
|
"42F166880D97A2225CD5C9EA5E7B752F4CF81BBDBE98E542EE10E1C6595D259A1E"
|
|
"4A8B6D7744CD98C5D3F921ADC252EB7D8AF6B916044B676A574747942AC20A89FF"
|
|
"79F4C2F25FBA99D6A44618A8C0420B27D54E3DA17B77C9D43CCA217CE9BDE99BD9"
|
|
"1E9733A1A00B9B557AC3A433DC92633546156817FAE26B6E1C"),
|
|
wvutil::a2bs_hex(
|
|
"E7C566D86E98C36D2DCA54A966E7B469B6094B9201F200932F8EB5E1A94FB0B977"
|
|
"FAB8DFDAD57C677E279615F4EAFA872CA8EFF83179E4DE2AB78E6B41A860C42203"
|
|
"4B875AC282406E03AC01F2E407A55DE38C6C35707F34B3319646FA016A01CE9056"
|
|
"E55D28C48ED72F10FA6625656ED62B758CBADA757DDC52533C9CBD54FC1A46F827"
|
|
"CC7B69BA66AE19A15D725FCBB972B23C83F95C0F00F481A7C9AC8687B3A4AD15AD"
|
|
"A2ABBB84D8E3CBEC3E8771720A35234070799173B37219127141922CBA8CB2DC48"
|
|
"EC2477832D1AE477942DCDA93C0886AF72D71E56DA3D7737E49670B024639A195B"
|
|
"7377C7F45A797C6E5DBB1BB2843DA3FC76043E33687BEF3172"),
|
|
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample,
|
|
},
|
|
{
|
|
true,
|
|
wvutil::a2bs_hex("9FBE45DD47DA7EBA09A3E24CBA95C9AF"),
|
|
wvutil::a2bs_hex(
|
|
"64AB17B3E3DFAB47245C7CCE4543D4FC7A26DCF248F19F9B59F3C92601440B3617"
|
|
"C8ED0C96C656549E461F38708CD47A434066F8DF28CCC28B79252EEE3F9C2D7F6C"
|
|
"68EBE40141FE818FE082CA523C03D69DDAF183A93C022327FEDC5582C5ABCA9D34"
|
|
"2B71263A67F9CB2336F12108AAAEF464F17177E44E9B0C4E56E61DA53C2150B440"
|
|
"5CC82D994DFD9BF4087C761956D6688A9705DB4CF350381085F383C49666D4AED1"
|
|
"35C519C1F0B5CBA06E287FEEA96EA367BF54E7368DCF998276C6E46497E0C50E20"
|
|
"FEF74E42CB518FE7F22EF27202428688F86404E8278587017012C1D65537C6CBD7"
|
|
"DDE04AAE338D68115A9F430AFC100AB83CDADF45DCA39DB685"),
|
|
wvutil::a2bs_hex(
|
|
"BDA1AF662A108B3D34421B001E589A64E378CBE5386C17999F5C9E7079F52F56E3"
|
|
"EEC51C75BCD1C44FFE38709218C66B61A1CB8A7274E89F210D2D6C52905FC963DC"
|
|
"3F8D79B4EC016642AE3C8AF8D3E322A6EABE4761902D4D79FA3D2D7B827FC5797B"
|
|
"D909B5C14325643164915D528557F48A95EB3FD2437A7D553197559822AADE0A6A"
|
|
"A617729FCC58AAF581E78F7C332ED85F3470707E29AFF7BD9D8E785798311CA74C"
|
|
"C9FB1226EDF74A803B171A891DD56393FB1322D47988E1055095C5F35CF83DB5A3"
|
|
"940D329B51DDC89EC463712E9B32AEA8877AFE8C5BED67AE700FFC4CCEFFBC4021"
|
|
"0E7ECA01DFF357ADF4D73A80D2B0C46867B33A5605DFDBD06E"),
|
|
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample,
|
|
},
|
|
};
|
|
|
|
const HlsSegmentInfo kSampleAes10ByteSegmentInfo[] = {
|
|
{
|
|
true,
|
|
wvutil::a2bs_hex("9FBE45DD47DA7EBA09A3E24CBA95C9AF"),
|
|
wvutil::a2bs_hex("4884604C8DA8A53CE33D"),
|
|
wvutil::a2bs_hex("4884604C8DA8A53CE33D"),
|
|
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample,
|
|
},
|
|
};
|
|
|
|
const HlsSegmentInfo kSampleAes16ByteSegmentInfo[] = {
|
|
{
|
|
true,
|
|
wvutil::a2bs_hex("9FBE45DD47DA7EBA09A3E24CBA95C9AF"),
|
|
wvutil::a2bs_hex("4884604C8DA8A53CE33DB9FF8F1C5BB6"),
|
|
wvutil::a2bs_hex("8E6B884AB604495C675CA38F0425E23E"),
|
|
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample,
|
|
},
|
|
};
|
|
|
|
const HlsSegmentInfo kSampleAes18ByteSegmentInfo[] = {
|
|
{
|
|
true,
|
|
wvutil::a2bs_hex("9FBE45DD47DA7EBA09A3E24CBA95C9AF"),
|
|
wvutil::a2bs_hex("4884604C8DA8A53CE33DB9FF8F1C5BB6BB97"),
|
|
wvutil::a2bs_hex("8E6B884AB604495C675CA38F0425E23EBB97"),
|
|
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample,
|
|
},
|
|
};
|
|
|
|
const HlsSegmentInfo kSampleAes160ByteSegmentInfo[] = {
|
|
{
|
|
true,
|
|
wvutil::a2bs_hex("9FBE45DD47DA7EBA09A3E24CBA95C9AF"),
|
|
wvutil::a2bs_hex(
|
|
"4884604C8DA8A53CE33DB9FF8F1C5BB6BB97F37B39906BF4159655"
|
|
"5C1BCCE9ED029359C4CF5906B6AB5BF60FBB3F1A1C7C59ACFC7E4F"
|
|
"B4AD8E623C04D503A3DDAD868A6AC55C10D564FC23B8ACFF407DAA"
|
|
"F4ED2743520E02CDA9680D9EA88E918DF21FDC42F166880D97A222"
|
|
"5CD5C9EA5E7B752F4CF81BBDBE98E542EE10E1C6595D259A1E4A8B"
|
|
"6D7744CD98C5D3F921ADC252EB7D8AF6B916044B676A574747"),
|
|
wvutil::a2bs_hex(
|
|
"8E6B884AB604495C675CA38F0425E23EBB97F37B39906BF4159655"
|
|
"5C1BCCE9ED029359C4CF5906B6AB5BF60FBB3F1A1C7C59ACFC7E4F"
|
|
"B4AD8E623C04D503A3DDAD868A6AC55C10D564FC23B8ACFF407DAA"
|
|
"F4ED2743520E02CDA9680D9EA88E918DF21FDC42F166880D97A222"
|
|
"5CD5C9EA5E7B752F4CF81BBDBE98E542EE10E1C6595D259A1E4A8B"
|
|
"6D7744CD98C5D3F921ADC252EB7D8AF6B916044B676A574747"),
|
|
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample,
|
|
},
|
|
};
|
|
|
|
const HlsSegmentInfo kSampleAes175ByteSegmentInfo[] = {
|
|
{
|
|
true,
|
|
wvutil::a2bs_hex("9FBE45DD47DA7EBA09A3E24CBA95C9AF"),
|
|
wvutil::a2bs_hex(
|
|
"4884604C8DA8A53CE33DB9FF8F1C5BB6BB97F37B39906BF41596555C1BCC"
|
|
"E9ED029359C4CF5906B6AB5BF60FBB3F1A1C7C59ACFC7E4FB4AD8E623C04"
|
|
"D503A3DDAD868A6AC55C10D564FC23B8ACFF407DAAF4ED2743520E02CDA9"
|
|
"680D9EA88E918DF21FDC42F166880D97A2225CD5C9EA5E7B752F4CF81BBD"
|
|
"BE98E542EE10E1C6595D259A1E4A8B6D7744CD98C5D3F921ADC252EB7D8A"
|
|
"F6B916044B676A574747942AC20A89FF79F4C2F25FBA99D6A4"),
|
|
wvutil::a2bs_hex(
|
|
"8E6B884AB604495C675CA38F0425E23EBB97F37B39906BF4159655"
|
|
"5C1BCCE9ED029359C4CF5906B6AB5BF60FBB3F1A1C7C59ACFC7E4F"
|
|
"B4AD8E623C04D503A3DDAD868A6AC55C10D564FC23B8ACFF407DAA"
|
|
"F4ED2743520E02CDA9680D9EA88E918DF21FDC42F166880D97A222"
|
|
"5CD5C9EA5E7B752F4CF81BBDBE98E542EE10E1C6595D259A1E4A8B"
|
|
"6D7744CD98C5D3F921ADC252EB7D8AF6B916044B676A574747942A"
|
|
"C20A89FF79F4C2F25FBA99D6A4"),
|
|
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample,
|
|
},
|
|
};
|
|
|
|
const HlsSegmentInfo kSampleAes176ByteSegmentInfo[] = {
|
|
{
|
|
true,
|
|
wvutil::a2bs_hex("9FBE45DD47DA7EBA09A3E24CBA95C9AF"),
|
|
wvutil::a2bs_hex(
|
|
"4884604C8DA8A53CE33DB9FF8F1C5BB6BB97F37B39906BF41596555C1BCC"
|
|
"E9ED029359C4CF5906B6AB5BF60FBB3F1A1C7C59ACFC7E4FB4AD8E623C04"
|
|
"D503A3DDAD868A6AC55C10D564FC23B8ACFF407DAAF4ED2743520E02CDA9"
|
|
"680D9EA88E918DF21FDC42F166880D97A2225CD5C9EA5E7B752F4CF81BBD"
|
|
"BE98E542EE10E1C6595D259A1E4A8B6D7744CD98C5D3F921ADC252EB7D8A"
|
|
"F6B916044B676A574747942AC20A89FF79F4C2F25FBA99D6A446"),
|
|
wvutil::a2bs_hex(
|
|
"8E6B884AB604495C675CA38F0425E23EBB97F37B39906BF4159655"
|
|
"5C1BCCE9ED029359C4CF5906B6AB5BF60FBB3F1A1C7C59ACFC7E4F"
|
|
"B4AD8E623C04D503A3DDAD868A6AC55C10D564FC23B8ACFF407DAA"
|
|
"F4ED2743520E02CDA9680D9EA88E918DF21FDC42F166880D97A222"
|
|
"5CD5C9EA5E7B752F4CF81BBDBE98E542EE10E1C6595D259A1E4A8B"
|
|
"6D7744CD98C5D3F921ADC252EB7D8AF6B916044B676A574747FBDC"
|
|
"189C42C3C213A71DBCF73A05A1A1"),
|
|
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample,
|
|
},
|
|
};
|
|
|
|
const HlsSegmentInfo kSampleAes192ByteSegmentInfo[] = {
|
|
{
|
|
true,
|
|
wvutil::a2bs_hex("9FBE45DD47DA7EBA09A3E24CBA95C9AF"),
|
|
wvutil::a2bs_hex(
|
|
"4884604C8DA8A53CE33DB9FF8F1C5BB6BB97F37B39906BF41596555C1BCCE9ED02"
|
|
"9359C4CF5906B6AB5BF60FBB3F1A1C7C59ACFC7E4FB4AD8E623C04D503A3DDAD86"
|
|
"8A6AC55C10D564FC23B8ACFF407DAAF4ED2743520E02CDA9680D9EA88E918DF21F"
|
|
"DC42F166880D97A2225CD5C9EA5E7B752F4CF81BBDBE98E542EE10E1C6595D259A"
|
|
"1E4A8B6D7744CD98C5D3F921ADC252EB7D8AF6B916044B676A574747942AC20A89"
|
|
"FF79F4C2F25FBA99D6A44618A8C0420B27D54E3DA17B77C9D43CCA"),
|
|
wvutil::a2bs_hex(
|
|
"8E6B884AB604495C675CA38F0425E23EBB97F37B39906BF41596555C1BCCE9ED02"
|
|
"9359C4CF5906B6AB5BF60FBB3F1A1C7C59ACFC7E4FB4AD8E623C04D503A3DDAD86"
|
|
"8A6AC55C10D564FC23B8ACFF407DAAF4ED2743520E02CDA9680D9EA88E918DF21F"
|
|
"DC42F166880D97A2225CD5C9EA5E7B752F4CF81BBDBE98E542EE10E1C6595D259A"
|
|
"1E4A8B6D7744CD98C5D3F921ADC252EB7D8AF6B916044B676A574747FBDC189C42"
|
|
"C3C213A71DBCF73A05A1A118A8C0420B27D54E3DA17B77C9D43CCA"),
|
|
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample,
|
|
},
|
|
};
|
|
|
|
const HlsSegmentInfo kSampleAes338ByteSegmentInfo[] = {
|
|
{
|
|
true,
|
|
wvutil::a2bs_hex("9FBE45DD47DA7EBA09A3E24CBA95C9AF"),
|
|
wvutil::a2bs_hex(
|
|
"4884604C8DA8A53CE33DB9FF8F1C5BB6BB97F37B39906BF41596555C1BCCE9ED02"
|
|
"9359C4CF5906B6AB5BF60FBB3F1A1C7C59ACFC7E4FB4AD8E623C04D503A3DDAD86"
|
|
"8A6AC55C10D564FC23B8ACFF407DAAF4ED2743520E02CDA9680D9EA88E918DF21F"
|
|
"DC42F166880D97A2225CD5C9EA5E7B752F4CF81BBDBE98E542EE10E1C6595D259A"
|
|
"1E4A8B6D7744CD98C5D3F921ADC252EB7D8AF6B916044B676A574747942AC20A89"
|
|
"FF79F4C2F25FBA99D6A44618A8C0420B27D54E3DA17B77C9D43CCA217CE9BDE99B"
|
|
"D91E9733A1A00B9B557AC3A433DC92633546156817FAE26B6E1C942AC20A89FF79"
|
|
"F4C2F25FBA99D6A44618A8C0420B27D54E3DA17B77C9D43CCAE7C566D86E98C36D"
|
|
"2DCA54A966E7B469B6094B9201F200932F8EB5E1A94FB0B977FAB8DFDAD57C677E"
|
|
"279615F4EAFA872CA8EFF83179E4DE2AB78E6B41A860C422034B875AC282406E03"
|
|
"AC01F2E407A55DE3"),
|
|
wvutil::a2bs_hex(
|
|
"8E6B884AB604495C675CA38F0425E23EBB97F37B39906BF41596555C1BCCE9ED02"
|
|
"9359C4CF5906B6AB5BF60FBB3F1A1C7C59ACFC7E4FB4AD8E623C04D503A3DDAD86"
|
|
"8A6AC55C10D564FC23B8ACFF407DAAF4ED2743520E02CDA9680D9EA88E918DF21F"
|
|
"DC42F166880D97A2225CD5C9EA5E7B752F4CF81BBDBE98E542EE10E1C6595D259A"
|
|
"1E4A8B6D7744CD98C5D3F921ADC252EB7D8AF6B916044B676A574747FBDC189C42"
|
|
"C3C213A71DBCF73A05A1A118A8C0420B27D54E3DA17B77C9D43CCA217CE9BDE99B"
|
|
"D91E9733A1A00B9B557AC3A433DC92633546156817FAE26B6E1C942AC20A89FF79"
|
|
"F4C2F25FBA99D6A44618A8C0420B27D54E3DA17B77C9D43CCAE7C566D86E98C36D"
|
|
"2DCA54A966E7B469B6094B9201F200932F8EB5E1A94FB0B977FAB8DFDAD57C677E"
|
|
"279615F4EAFA872CA8EFF83179E4DE2AB78E6B41A860C44A27BD03EAB2D3CFCB2C"
|
|
"F4F5AA93E7025DE3"),
|
|
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample,
|
|
},
|
|
};
|
|
|
|
struct HlsDecryptionInfo {
|
|
bool sample_aes;
|
|
size_t number_of_segments;
|
|
const HlsSegmentInfo* segment_info;
|
|
const std::string& attribute_list;
|
|
};
|
|
|
|
const HlsDecryptionInfo kHlsDecryptionTestVectors[] = {
|
|
{false, 1, &kAes128SingleSegmentInfo[0], kAttributeListAes128},
|
|
{false, 2, &kAes128PartialSegmentInfo[0], kAttributeListAes128},
|
|
{false, 2, &kAes128MultiSegmentInfo[0], kAttributeListAes128},
|
|
{true, 1, &kSampleAes10ByteSegmentInfo[0], kAttributeListSampleAes},
|
|
{true, 1, &kSampleAes16ByteSegmentInfo[0], kAttributeListSampleAes},
|
|
{true, 1, &kSampleAes18ByteSegmentInfo[0], kAttributeListSampleAes},
|
|
{true, 1, &kSampleAes160ByteSegmentInfo[0], kAttributeListSampleAes},
|
|
{true, 1, &kSampleAes175ByteSegmentInfo[0], kAttributeListSampleAes},
|
|
{true, 1, &kSampleAes176ByteSegmentInfo[0], kAttributeListSampleAes},
|
|
{true, 1, &kSampleAes192ByteSegmentInfo[0], kAttributeListSampleAes},
|
|
{true, 1, &kSampleAes338ByteSegmentInfo[0], kAttributeListSampleAes},
|
|
};
|
|
|
|
const std::string kHlsPsshAes128LittleEndianFourCC = wvutil::a2bs_hex(
|
|
"00000060" // blob size
|
|
"70737368" // "pssh"
|
|
"00000000" // flags
|
|
"edef8ba979d64acea3c827dcd51d21ed" // Widevine system id
|
|
"00000040" // pssh data size
|
|
// pssh data:
|
|
"08011210AB6531FF6E6EA15E387B019E"
|
|
"59C2DE0A1A0D7769646576696E655F74"
|
|
"6573742215686C735F6165735F313238"
|
|
"5F73747265616D696E6748E3C48D8B03");
|
|
const std::string kHlsPsshSampleAesLittleEndianFourCC = wvutil::a2bs_hex(
|
|
"00000063" // blob size
|
|
"70737368" // "pssh"
|
|
"00000000" // flags
|
|
"edef8ba979d64acea3c827dcd51d21ed" // Widevine system id
|
|
"00000043" // pssh data size
|
|
// pssh data:
|
|
"08011210613DB35603320EB8E7EA24BD"
|
|
"EEA3FDB81A0D7769646576696E655F74"
|
|
"6573742218686C735F73616D706C655F"
|
|
"6165735F73747265616D696E6748E3C4"
|
|
"8D9B07");
|
|
const std::string kHlsPsshAes128FourCC = wvutil::a2bs_hex(
|
|
"00000060" // blob size
|
|
"70737368" // "pssh"
|
|
"00000000" // flags
|
|
"edef8ba979d64acea3c827dcd51d21ed" // Widevine system id
|
|
"00000040" // pssh data size
|
|
// pssh data:
|
|
"08011210AB6531FF6E6EA15E387B019E"
|
|
"59C2DE0A1A0D7769646576696E655F74"
|
|
"6573742215686C735F6165735F313238"
|
|
"5F73747265616D696E6748B1C6899B06");
|
|
const std::string kHlsPsshSampleAesFourCC = wvutil::a2bs_hex(
|
|
"00000063" // blob size
|
|
"70737368" // "pssh"
|
|
"00000000" // flags
|
|
"edef8ba979d64acea3c827dcd51d21ed" // Widevine system id
|
|
"00000043" // pssh data size
|
|
// pssh data:
|
|
"08011210613DB35603320EB8E7EA24BD"
|
|
"EEA3FDB81A0D7769646576696E655F74"
|
|
"6573742218686C735F73616D706C655F"
|
|
"6165735F73747265616D696E6748F3C6"
|
|
"899B06");
|
|
|
|
const HlsDecryptionInfo kHlsFourCCBackwardCompatibilityTestVectors[] = {
|
|
{false, 2, &kAes128MultiSegmentInfo[0], kHlsPsshAes128LittleEndianFourCC},
|
|
{true, 1, &kSampleAes160ByteSegmentInfo[0],
|
|
kHlsPsshSampleAesLittleEndianFourCC},
|
|
{false, 2, &kAes128MultiSegmentInfo[0], kHlsPsshAes128FourCC},
|
|
{true, 1, &kSampleAes160ByteSegmentInfo[0], kHlsPsshSampleAesFourCC},
|
|
};
|
|
|
|
const std::string kFullCencPssh = wvutil::a2bs_hex(
|
|
"00000044" // blob size
|
|
"70737368" // "pssh"
|
|
"00000000" // flags
|
|
"edef8ba979d64acea3c827dcd51d21ed" // Widevine system id
|
|
"00000024" // pssh data size
|
|
"08011201321a0d7769646576696e655f" // pssh data
|
|
"74657374220a323031355f7465617273"
|
|
"2a024844");
|
|
|
|
const std::string kFullCbcsPssh = wvutil::a2bs_hex(
|
|
"00000053" // blob size
|
|
"70737368" // "pssh"
|
|
"00000000" // flags
|
|
"edef8ba979d64acea3c827dcd51d21ed" // Widevine system id
|
|
"00000033" // pssh data size
|
|
"12103030303030303030303030303030"
|
|
"30321a0d7769646576696e655f746573"
|
|
"74220a323031355f746561727348f3c6"
|
|
"899b06");
|
|
|
|
struct Cenc30SampleInfo {
|
|
bool is_encrypted;
|
|
wvcdm::KeyId key_id;
|
|
std::string iv;
|
|
std::string clear_data;
|
|
std::string encrypted_data;
|
|
uint8_t subsample_flags;
|
|
wvcdm::CdmCipherMode cipher_mode;
|
|
};
|
|
|
|
const Cenc30SampleInfo kCenc30CencKey33Sample = {
|
|
true,
|
|
wvutil::a2bs_hex("30303030303030303030303030303033"),
|
|
wvutil::a2bs_hex("9FBE45DD47DA7EBA09A3E24CBA95C9AF"),
|
|
wvutil::a2bs_hex(
|
|
"011E88387D58EBB8E5CDCC38D431EEF404666D67944510D7120985822A6516EFE0"
|
|
"35D807AC804F348AF78F201A88B0F06E8C9BF4F5DEF448558B3F2BB15DF7A6F3F6"
|
|
"A34FA81F4478847BB4BE2F29C586F627D2370115969617EF4B35B32D2E522B0DA8"
|
|
"905B2581E5DEE0467003FB835EA2D1FEDF82635941D45E0C3A57FFBD763D1C56D6"
|
|
"B63FED9A0CAABE7436CC82DCED6563667B4F5BC037AC54BFE25B821F92E8CCDD0C"
|
|
"13571FDFD3D833777EF7C13B368224DE27FF6570B4DDA8A1F1D0F75F69701FA40A"
|
|
"4EE65DB853CF02A130E492F0D998FCA6B581E41E678AA47E301246A63DF92780D9"
|
|
"1478858417F0ED0E1E3E18AA3E4A18DFC75F1D6FCE24C6C952"),
|
|
wvutil::a2bs_hex(
|
|
"E7C566D86E98C36D2DCA54A966E7B469B6094B9201F200932F8EB5E1A94FB0B977"
|
|
"FAB8DFDAD57C677E279615F4EAFA872CA8EFF83179E4DE2AB78E6B41A860C42203"
|
|
"4B875AC282406E03AC01F2E407A55DE38C6C35707F34B3319646FA016A01CE9056"
|
|
"E55D28C48ED72F10FA6625656ED62B758CBADA757DDC52533C9CBD54FC1A46F827"
|
|
"CC7B69BA66AE19A15D725FCBB972B23C83F95C0F00F481A7C9AC8687B3A4AD15AD"
|
|
"A2ABBB84D8E3CBEC3E8771720A35234070799173B37219127141922CBA8CB2DC48"
|
|
"EC2477832D1AE477942DCDA93C0886AF72D71E56DA3D7737E49670B024639A195B"
|
|
"7377C7F45A797C6E5DBB1BB2843DA3FC76043E33687BEF3172"),
|
|
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample,
|
|
wvcdm::kCipherModeCtr,
|
|
};
|
|
|
|
const Cenc30SampleInfo kCenc30CencKey32Sample = {
|
|
true,
|
|
wvutil::a2bs_hex("30303030303030303030303030303032"),
|
|
wvutil::a2bs_hex("9FBE45DD47DA7EBA09A3E24CBA95C9AF"),
|
|
wvutil::a2bs_hex(
|
|
"1B605E32B31D6245BCCC01C4E7720725DE501171286AFE05602A6ECE249CC23BD6"
|
|
"35128297D26F488CB9599C26FBDC1EEB641CD06CD2A8DEBC29AFA8B1AE5602DA47"
|
|
"0B147A9C4FB9E3D301F7802329F13BFACB2835341BEB208FAFDDD1EE31BEC73E66"
|
|
"EA62620D6658AC17C543CA7BD65EC91BF8F1EC6FB5E7F2099FD49A65C561CF483C"
|
|
"25BEDBE8FC5889680B53543A8271BA8DD371DB351C56D0D1C8611ADE078614F333"
|
|
"C01E9C6B2B0041B2D04AF0428B706203F58E02EBE7BE37BF8E295EAA98FD454BA2"
|
|
"461666FF55743FB51846166C6EAC70009B549C3295E140DC91B68E78BEB6AF4F3C"
|
|
"AEA2B6778480DB26CFC34ABCC658AFA7FDE1AA8ABBC9012080"),
|
|
wvutil::a2bs_hex(
|
|
"E7C566D86E98C36D2DCA54A966E7B469B6094B9201F200932F8EB5E1A94FB0B977"
|
|
"FAB8DFDAD57C677E279615F4EAFA872CA8EFF83179E4DE2AB78E6B41A860C42203"
|
|
"4B875AC282406E03AC01F2E407A55DE38C6C35707F34B3319646FA016A01CE9056"
|
|
"E55D28C48ED72F10FA6625656ED62B758CBADA757DDC52533C9CBD54FC1A46F827"
|
|
"CC7B69BA66AE19A15D725FCBB972B23C83F95C0F00F481A7C9AC8687B3A4AD15AD"
|
|
"A2ABBB84D8E3CBEC3E8771720A35234070799173B37219127141922CBA8CB2DC48"
|
|
"EC2477832D1AE477942DCDA93C0886AF72D71E56DA3D7737E49670B024639A195B"
|
|
"7377C7F45A797C6E5DBB1BB2843DA3FC76043E33687BEF3172"),
|
|
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample,
|
|
wvcdm::kCipherModeCtr,
|
|
};
|
|
|
|
const Cenc30SampleInfo kCenc30CbcsKey32Sample = {
|
|
true,
|
|
wvutil::a2bs_hex("30303030303030303030303030303032"),
|
|
wvutil::a2bs_hex("9FBE45DD47DA7EBA09A3E24CBA95C9AF"),
|
|
wvutil::a2bs_hex(
|
|
"4392E38BAE263267ED15394DE349AD1577F37B7D906C3A61536EE5A288F66F22F2"
|
|
"F5098964B7F2860A848C3C4FD30E538B3BCD2E700DC3FBC1657A6E9EAE44DE97C4"
|
|
"6F27C82A49198EE185D92931F093C3342FDBF6CF8203E18CCDC4B88E79C95EC052"
|
|
"3FD10F9409945349169FAA8F6A37179D2BEDC04A158A09BCBF742DA05245428788"
|
|
"E972B9B465FED5849AEDDB74B8919673C0C8829B5B062A38B3146CB8D497F03A4D"
|
|
"5C0A1D463504C1F116A811EF32503695B8FF78D9E93CDF7B2F7493E8043D4DE110"
|
|
"FE1D342D1C0175BF1466A544FC0D02DD0E314098256DD65B48098323C3AED9B7E0"
|
|
"CF260DBC5A0F09A46E39AE5E26A66ABFA52CBA26FBA83975E4"),
|
|
wvutil::a2bs_hex(
|
|
"E7C566D86E98C36D2DCA54A966E7B469B6094B9201F200932F8EB5E1A94FB0B977"
|
|
"FAB8DFDAD57C677E279615F4EAFA872CA8EFF83179E4DE2AB78E6B41A860C42203"
|
|
"4B875AC282406E03AC01F2E407A55DE38C6C35707F34B3319646FA016A01CE9056"
|
|
"E55D28C48ED72F10FA6625656ED62B758CBADA757DDC52533C9CBD54FC1A46F827"
|
|
"CC7B69BA66AE19A15D725FCBB972B23C83F95C0F00F481A7C9AC8687B3A4AD15AD"
|
|
"A2ABBB84D8E3CBEC3E8771720A35234070799173B37219127141922CBA8CB2DC48"
|
|
"EC2477832D1AE477942DCDA93C0886AF72D71E56DA3D7737E49670B024639A195B"
|
|
"7377C7F45A797C6E5DBB1BB2843DA3FC76043E33687BEF3172"),
|
|
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample,
|
|
wvcdm::kCipherModeCbc,
|
|
};
|
|
|
|
const Cenc30SampleInfo kCenc30CbcsKey33Sample = {
|
|
true,
|
|
wvutil::a2bs_hex("30303030303030303030303030303033"),
|
|
wvutil::a2bs_hex("9FBE45DD47DA7EBA09A3E24CBA95C9AF"),
|
|
wvutil::a2bs_hex(
|
|
"E300F37FEB0CDD9F276E67B971FF423003F3BF21DCF6100BA453A473A4522A19A8"
|
|
"2E098AA25511011D386FC7092FE3B407DF2BEB3AD57D5E1178F041E3FCABE25193"
|
|
"3F5EE35670CEB96BA3DAF922484F9A37773EF75D4B17FACC80B475004A6229AB4C"
|
|
"DFFA426468E578DE6A0285D942CDE476E06FF907D03F382552C2E14399C3FC2D21"
|
|
"9A59819FFF837EBC88A9F83A42D37F48ED8564EB40AC3BA8A6D2634A81F04FC2F1"
|
|
"379A45869036FD72B39C88222646AB7486A8416D78AB75951EB87ED1E16DF69209"
|
|
"A6966AC93C7BB65B85E429357A732CBC75F6EFB1781859FB771D60D11EB381D9CA"
|
|
"63F793507B02207810773FCABED0240E5BEEAD30116014E481"),
|
|
wvutil::a2bs_hex(
|
|
"E7C566D86E98C36D2DCA54A966E7B469B6094B9201F200932F8EB5E1A94FB0B977"
|
|
"FAB8DFDAD57C677E279615F4EAFA872CA8EFF83179E4DE2AB78E6B41A860C42203"
|
|
"4B875AC282406E03AC01F2E407A55DE38C6C35707F34B3319646FA016A01CE9056"
|
|
"E55D28C48ED72F10FA6625656ED62B758CBADA757DDC52533C9CBD54FC1A46F827"
|
|
"CC7B69BA66AE19A15D725FCBB972B23C83F95C0F00F481A7C9AC8687B3A4AD15AD"
|
|
"A2ABBB84D8E3CBEC3E8771720A35234070799173B37219127141922CBA8CB2DC48"
|
|
"EC2477832D1AE477942DCDA93C0886AF72D71E56DA3D7737E49670B024639A195B"
|
|
"7377C7F45A797C6E5DBB1BB2843DA3FC76043E33687BEF3172"),
|
|
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample,
|
|
wvcdm::kCipherModeCbc,
|
|
};
|
|
|
|
struct SingleSampleDecryptionInfo {
|
|
const std::string pssh;
|
|
Cenc30SampleInfo sample_info;
|
|
};
|
|
|
|
const SingleSampleDecryptionInfo kCenc30DecryptionData[2] = {
|
|
{kFullCencPssh, kCenc30CencKey33Sample},
|
|
{kFullCbcsPssh, kCenc30CbcsKey33Sample},
|
|
};
|
|
|
|
struct FourSampleDecryptionInfo {
|
|
const std::string pssh;
|
|
Cenc30SampleInfo sample_info[4];
|
|
};
|
|
|
|
const FourSampleDecryptionInfo kCenc30SwitchCipherData[8] = {
|
|
// Switch between cipher modes
|
|
{kFullCencPssh,
|
|
{
|
|
kCenc30CencKey33Sample,
|
|
kCenc30CbcsKey33Sample,
|
|
kCenc30CencKey33Sample,
|
|
kCenc30CbcsKey33Sample,
|
|
}},
|
|
{kFullCbcsPssh,
|
|
{
|
|
kCenc30CbcsKey33Sample,
|
|
kCenc30CencKey33Sample,
|
|
kCenc30CbcsKey33Sample,
|
|
kCenc30CencKey33Sample,
|
|
}},
|
|
// Switch between cipher modes, but the first sample has a cipher mode
|
|
// that differs with the protection scheme in the pssh
|
|
{kFullCencPssh,
|
|
{
|
|
kCenc30CbcsKey33Sample,
|
|
kCenc30CencKey33Sample,
|
|
kCenc30CbcsKey33Sample,
|
|
kCenc30CencKey33Sample,
|
|
}},
|
|
{kFullCbcsPssh,
|
|
{
|
|
kCenc30CencKey33Sample,
|
|
kCenc30CbcsKey33Sample,
|
|
kCenc30CencKey33Sample,
|
|
kCenc30CbcsKey33Sample,
|
|
}},
|
|
// Switch between cipher modes and keys
|
|
{kFullCencPssh,
|
|
{
|
|
kCenc30CencKey33Sample,
|
|
kCenc30CencKey32Sample,
|
|
kCenc30CbcsKey33Sample,
|
|
kCenc30CbcsKey32Sample,
|
|
}},
|
|
{kFullCencPssh,
|
|
{
|
|
kCenc30CbcsKey33Sample,
|
|
kCenc30CbcsKey32Sample,
|
|
kCenc30CencKey33Sample,
|
|
kCenc30CencKey32Sample,
|
|
}},
|
|
{kFullCbcsPssh,
|
|
{
|
|
kCenc30CbcsKey33Sample,
|
|
kCenc30CbcsKey32Sample,
|
|
kCenc30CencKey33Sample,
|
|
kCenc30CencKey32Sample,
|
|
}},
|
|
{kFullCbcsPssh,
|
|
{
|
|
kCenc30CencKey33Sample,
|
|
kCenc30CencKey32Sample,
|
|
kCenc30CbcsKey33Sample,
|
|
kCenc30CbcsKey32Sample,
|
|
}},
|
|
};
|
|
|
|
// Extracts the scheme, hostname and path from the provided URL.
|
|
// Example:
|
|
// Input: "https://www.widevine.com/service?id=1234&device=pixel&flag"
|
|
// Output: "https://www.widevine.com/service"
|
|
std::string GetUrlHostAndPath(const std::string& full_url) {
|
|
const auto start_of_params = full_url.find('?');
|
|
if (start_of_params == std::string::npos) return full_url;
|
|
return full_url.substr(0, start_of_params);
|
|
}
|
|
|
|
// Extracts and splits the query parameters from the provided URL.
|
|
// Returns an empty list if no parameters are found.
|
|
//
|
|
// Example:
|
|
// Input: "https://www.widevine.com/service?id=1234&device=pixel&flag"
|
|
// Output: {"id=1234", "device=pixel", "flag"}
|
|
std::vector<std::string> GetUrlQueryParamPairs(const std::string& full_url) {
|
|
const auto start_of_params = full_url.find('?');
|
|
if (start_of_params == std::string::npos) return {}; // No params.
|
|
// Remove host and path.
|
|
const std::string all_params = full_url.substr(start_of_params + 1);
|
|
if (all_params.empty()) return {};
|
|
// Check if there are more than 1 parameters.
|
|
auto param_separator = all_params.find('&');
|
|
if (param_separator == std::string::npos) {
|
|
return {all_params}; // Only 1 parameter.
|
|
}
|
|
// Split parameters by '&'.
|
|
std::vector<std::string> params;
|
|
params.push_back(all_params.substr(0, param_separator));
|
|
while (param_separator != std::string::npos) {
|
|
auto next_param_separator = all_params.find('&', param_separator + 1);
|
|
if (next_param_separator == std::string::npos) {
|
|
params.push_back(all_params.substr(param_separator + 1));
|
|
} else {
|
|
params.push_back(all_params.substr(
|
|
param_separator + 1, next_param_separator - param_separator - 1));
|
|
}
|
|
param_separator = next_param_separator;
|
|
}
|
|
return params;
|
|
}
|
|
|
|
// Extracts and maps the query parameters from the provided URL.
|
|
// Creates a map between the parameter keys and values. Parameters
|
|
// that are only keys have an empty string value.
|
|
//
|
|
// Example:
|
|
// Input: "https://www.widevine.com/service?id=1234&device=pixel&flag"
|
|
// Output: {"id": "1234", "device": "pixel", "flag": ""}
|
|
std::map<std::string, std::string> GetUrlQueryParams(
|
|
const std::string& full_url) {
|
|
const std::vector<std::string> param_pairs = GetUrlQueryParamPairs(full_url);
|
|
std::map<std::string, std::string> params;
|
|
if (param_pairs.empty()) return params;
|
|
for (const auto& pair : param_pairs) {
|
|
const auto value_separator = pair.find('=');
|
|
if (value_separator == std::string::npos) {
|
|
// Just the key.
|
|
params.emplace(pair, "");
|
|
} else {
|
|
params.emplace(pair.substr(0, value_separator),
|
|
pair.substr(value_separator + 1));
|
|
}
|
|
}
|
|
return params;
|
|
}
|
|
|
|
// Checks that the |actual_url| is a super set of information compared
|
|
// to the |expected_url|. The scheme, hostname and path must be the
|
|
// same. The |actual_url| must contain at all the query parameters of
|
|
// the |expected_url|. Order of the query parameters do not matter.
|
|
//
|
|
// Example A:
|
|
// Expected: "https://www.widevine.com/service?key=1234"
|
|
// Actual: "https://www.widevine.com/service?retry=true&key=1234"
|
|
// Result: true
|
|
//
|
|
// Example B:
|
|
// Expected: "https://www.widevine.com/service?key=1234&retry=true"
|
|
// Actual: "https://www.widevine.com/service?key=1234"
|
|
// Result: false
|
|
//
|
|
// Example C:
|
|
// Expected: "https://www.widevine.com/service?key=1234"
|
|
// Actual: "https://www.widevine.org/service?key=1234"
|
|
// Result: false
|
|
bool IsUrlSimilar(const std::string& expected_url,
|
|
const std::string& actual_url) {
|
|
// First check the host and path.
|
|
const std::string expected_host_and_path = GetUrlHostAndPath(expected_url);
|
|
const std::string actual_host_and_path = GetUrlHostAndPath(actual_url);
|
|
if (expected_host_and_path != actual_host_and_path) {
|
|
return false; // Bad host and/or path.
|
|
}
|
|
// Compare query parameters.
|
|
const std::map<std::string, std::string> expected_params =
|
|
GetUrlQueryParams(expected_url);
|
|
const std::map<std::string, std::string> actual_params =
|
|
GetUrlQueryParams(actual_url);
|
|
if (actual_params.size() < expected_params.size()) {
|
|
return false; // Missing params.
|
|
}
|
|
for (const auto& expected_param : expected_params) {
|
|
const auto actual_param = actual_params.find(expected_param.first);
|
|
if (actual_param == actual_params.end())
|
|
return false; // Missing particular param.
|
|
if (actual_param->second != expected_param.second)
|
|
return false; // Incorrect param value.
|
|
}
|
|
return true;
|
|
}
|
|
} // namespace
|
|
|
|
namespace wvcdm {
|
|
// Protobuf generated classes
|
|
using video_widevine::ClientIdentification;
|
|
using video_widevine::ClientIdentification_NameValue;
|
|
using video_widevine::SignedMessage;
|
|
|
|
MATCHER_P(IsSimilarUrlTo, expected_url, "") {
|
|
return IsUrlSimilar(expected_url, arg);
|
|
}
|
|
|
|
class TestWvCdmClientPropertySet : public CdmClientPropertySet {
|
|
public:
|
|
TestWvCdmClientPropertySet()
|
|
: use_privacy_mode_(false),
|
|
is_session_sharing_enabled_(false),
|
|
session_sharing_id_(0),
|
|
use_atsc_mode_(false) {}
|
|
virtual ~TestWvCdmClientPropertySet() {}
|
|
|
|
virtual const std::string& app_id() const { return app_id_; }
|
|
virtual const std::string& security_level() const { return security_level_; }
|
|
virtual const std::string& service_certificate() const {
|
|
return service_certificate_;
|
|
}
|
|
virtual void set_service_certificate(const std::string& cert) {
|
|
service_certificate_ = cert;
|
|
}
|
|
virtual bool use_privacy_mode() const { return use_privacy_mode_; }
|
|
virtual bool is_session_sharing_enabled() const {
|
|
return is_session_sharing_enabled_;
|
|
}
|
|
virtual uint32_t session_sharing_id() const { return session_sharing_id_; }
|
|
virtual bool use_atsc_mode() const { return use_atsc_mode_; }
|
|
|
|
void set_app_id(const std::string& app_id) { app_id_ = app_id; }
|
|
void set_security_level(const std::string& security_level) {
|
|
if (!security_level.compare(QUERY_VALUE_SECURITY_LEVEL_L1) ||
|
|
!security_level.compare(QUERY_VALUE_SECURITY_LEVEL_L3)) {
|
|
security_level_ = security_level;
|
|
}
|
|
}
|
|
void set_use_privacy_mode(bool use_privacy_mode) {
|
|
use_privacy_mode_ = use_privacy_mode;
|
|
}
|
|
void set_session_sharing_mode(bool enable) {
|
|
is_session_sharing_enabled_ = enable;
|
|
}
|
|
void set_session_sharing_id(uint32_t id) { session_sharing_id_ = id; }
|
|
void set_use_atsc_mode(bool use_atsc_mode) { use_atsc_mode_ = use_atsc_mode; }
|
|
|
|
private:
|
|
std::string app_id_;
|
|
std::string security_level_;
|
|
std::string service_certificate_;
|
|
bool use_privacy_mode_;
|
|
bool is_session_sharing_enabled_;
|
|
uint32_t session_sharing_id_;
|
|
bool use_atsc_mode_;
|
|
};
|
|
|
|
class TestWvCdmEventListener : public WvCdmEventListener {
|
|
public:
|
|
TestWvCdmEventListener() : WvCdmEventListener() {}
|
|
MOCK_METHOD(void, OnSessionRenewalNeeded, (const CdmSessionId& session_id),
|
|
(override));
|
|
MOCK_METHOD(void, OnSessionKeysChange,
|
|
(const CdmSessionId& session_id,
|
|
const CdmKeyStatusMap& keys_status, bool has_new_usable_key),
|
|
(override));
|
|
MOCK_METHOD(void, OnExpirationUpdate,
|
|
(const CdmSessionId& session_id, int64_t new_expiry_time_seconds),
|
|
(override));
|
|
};
|
|
|
|
class DecryptCallbackTester {
|
|
public:
|
|
DecryptCallbackTester(
|
|
const android::sp<wvcdm::WvContentDecryptionModule>& decryptor,
|
|
const SubSampleInfo* sub_sample_info)
|
|
: decryptor_(decryptor), sub_sample_info_(sub_sample_info) {}
|
|
|
|
void Decrypt(const CdmSessionId& session_id,
|
|
const CdmKeyStatusMap& /* keys_status */,
|
|
bool /* has_new_usable_key */) {
|
|
EXPECT_TRUE(decryptor_);
|
|
ASSERT_NE(sub_sample_info_, nullptr);
|
|
|
|
std::vector<uint8_t> decrypt_buffer(sub_sample_info_->encrypt_data.size());
|
|
CdmDecryptionParameters decryption_parameters(
|
|
&sub_sample_info_->key_id, &sub_sample_info_->encrypt_data.front(),
|
|
sub_sample_info_->encrypt_data.size(), &sub_sample_info_->iv,
|
|
sub_sample_info_->block_offset, &decrypt_buffer[0]);
|
|
decryption_parameters.is_encrypted = sub_sample_info_->is_encrypted;
|
|
decryption_parameters.is_secure = sub_sample_info_->is_secure;
|
|
decryption_parameters.subsample_flags = sub_sample_info_->subsample_flags;
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->Decrypt(session_id, sub_sample_info_->validate_key_id,
|
|
decryption_parameters));
|
|
}
|
|
|
|
private:
|
|
android::sp<wvcdm::WvContentDecryptionModule> decryptor_;
|
|
const SubSampleInfo* sub_sample_info_;
|
|
};
|
|
|
|
class TestWvCdmHlsEventListener : public WvCdmEventListener {
|
|
public:
|
|
TestWvCdmHlsEventListener() : WvCdmEventListener() {}
|
|
virtual void OnSessionRenewalNeeded(const CdmSessionId&) {}
|
|
virtual void OnSessionKeysChange(const CdmSessionId&,
|
|
const CdmKeyStatusMap& keys_status, bool) {
|
|
key_status_map_ = keys_status;
|
|
}
|
|
|
|
virtual void OnExpirationUpdate(const CdmSessionId&, int64_t) {}
|
|
|
|
CdmKeyStatusMap GetKeyStatusMap() { return key_status_map_; }
|
|
|
|
private:
|
|
CdmKeyStatusMap key_status_map_;
|
|
};
|
|
|
|
class WvCdmRequestLicenseTest : public WvCdmTestBase {
|
|
public:
|
|
WvCdmRequestLicenseTest()
|
|
: decryptor_(new wvcdm::WvContentDecryptionModule()),
|
|
license_type_(kLicenseTypeStreaming) {}
|
|
~WvCdmRequestLicenseTest() {}
|
|
|
|
protected:
|
|
void GetOfflineConfiguration(std::string* key_id, std::string* client_auth) {
|
|
ConfigTestEnv config(config_.server_id(), false);
|
|
if (binary_key_id().compare(wvutil::a2bs_hex(config_.key_id())) == 0)
|
|
key_id->assign(wvutil::a2bs_hex(config.key_id()));
|
|
else
|
|
key_id->assign(binary_key_id());
|
|
|
|
client_auth->assign(config.client_auth());
|
|
}
|
|
|
|
CdmResponseType GenerateKeyRequest() {
|
|
CdmAppParameterMap app_parameters;
|
|
return GenerateKeyRequest(ISO_BMFF_VIDEO_MIME_TYPE, binary_key_id(),
|
|
app_parameters, kLicenseTypeStreaming,
|
|
kDefaultCdmIdentifier, nullptr);
|
|
}
|
|
|
|
void GenerateKeyRequest(const std::string& init_data,
|
|
CdmLicenseType license_type) {
|
|
GenerateKeyRequest(init_data, license_type, nullptr);
|
|
}
|
|
|
|
void GenerateKeyRequest(const std::string& init_data,
|
|
CdmLicenseType license_type,
|
|
const CdmIdentifier& identifier) {
|
|
CdmAppParameterMap app_parameters;
|
|
GenerateKeyRequest(CdmResponseType(wvcdm::KEY_MESSAGE), "video/mp4",
|
|
init_data, app_parameters, license_type, identifier,
|
|
nullptr);
|
|
}
|
|
|
|
void GenerateKeyRequest(const std::string& init_data,
|
|
CdmLicenseType license_type,
|
|
CdmClientPropertySet* property_set) {
|
|
CdmAppParameterMap app_parameters;
|
|
GenerateKeyRequest(init_data, app_parameters, license_type, property_set);
|
|
}
|
|
|
|
void GenerateKeyRequest(const std::string& init_data,
|
|
CdmAppParameterMap& app_parameters,
|
|
CdmLicenseType license_type,
|
|
CdmClientPropertySet* property_set) {
|
|
std::string init_data_type = "video/mp4";
|
|
GenerateKeyRequest(CdmResponseType(wvcdm::KEY_MESSAGE), init_data_type,
|
|
init_data, app_parameters, license_type, property_set);
|
|
}
|
|
|
|
void GenerateKeyRequest(CdmResponseType expected_response,
|
|
const std::string& init_data_type,
|
|
const std::string& init_data,
|
|
CdmAppParameterMap& app_parameters,
|
|
CdmLicenseType license_type,
|
|
CdmClientPropertySet* property_set) {
|
|
GenerateKeyRequest(expected_response, init_data_type, init_data,
|
|
app_parameters, license_type, kDefaultCdmIdentifier,
|
|
property_set);
|
|
}
|
|
|
|
void GenerateKeyRequest(CdmResponseType expected_response,
|
|
const std::string& init_data_type,
|
|
const std::string& init_data,
|
|
CdmAppParameterMap& app_parameters,
|
|
CdmLicenseType license_type,
|
|
const CdmIdentifier& cdm_identifier,
|
|
CdmClientPropertySet* property_set) {
|
|
CdmResponseType status =
|
|
GenerateKeyRequest(init_data_type, init_data, app_parameters,
|
|
license_type, cdm_identifier, property_set);
|
|
EXPECT_EQ(expected_response, status)
|
|
<< "session_id_ " << session_id_ << std::endl
|
|
<< "init_data (hex) " << wvutil::b2a_hex(init_data) << std::endl
|
|
<< "cdm_identifier.origin " << cdm_identifier.origin << std::endl
|
|
<< "cdm_identifier.app_package_name " << cdm_identifier.app_package_name
|
|
<< std::endl
|
|
<< "cdm_identifier.unique_id " << cdm_identifier.unique_id << std::endl;
|
|
}
|
|
|
|
CdmResponseType GenerateKeyRequest(const std::string& init_data_type,
|
|
const std::string& init_data,
|
|
CdmAppParameterMap& app_parameters,
|
|
CdmLicenseType license_type,
|
|
const CdmIdentifier& cdm_identifier,
|
|
CdmClientPropertySet* property_set) {
|
|
CdmKeyRequest key_request;
|
|
std::string key_set_id;
|
|
license_type_ = license_type;
|
|
if (wvutil::g_cutoff >= wvutil::CDM_LOG_DEBUG) {
|
|
InitializationData parsed_init_data(init_data_type, init_data);
|
|
parsed_init_data.DumpToLogs();
|
|
}
|
|
|
|
CdmResponseType status = decryptor_->GenerateKeyRequest(
|
|
session_id_, key_set_id, init_data_type, init_data, license_type,
|
|
app_parameters, property_set, cdm_identifier, &key_request);
|
|
key_msg_ = key_request.message;
|
|
EXPECT_EQ(0u, key_request.url.size());
|
|
return status;
|
|
}
|
|
|
|
void GenerateRenewalRequest(CdmLicenseType license_type,
|
|
CdmKeyMessage* key_msg, std::string* server_url) {
|
|
GenerateRenewalRequest(license_type, server_url);
|
|
*key_msg = key_msg_;
|
|
}
|
|
|
|
void GenerateRenewalRequest(CdmLicenseType license_type,
|
|
std::string* server_url) {
|
|
// TODO application makes a license request, CDM will renew the license
|
|
// when appropriate.
|
|
std::string init_data;
|
|
wvcdm::CdmAppParameterMap app_parameters;
|
|
CdmKeyRequest key_request;
|
|
EXPECT_EQ(
|
|
wvcdm::KEY_MESSAGE,
|
|
decryptor_->GenerateKeyRequest(
|
|
session_id_, key_set_id_, "video/mp4", init_data, license_type,
|
|
app_parameters, nullptr, kDefaultCdmIdentifier, &key_request));
|
|
key_msg_ = key_request.message;
|
|
*server_url = key_request.url;
|
|
EXPECT_EQ(kKeyRequestTypeRenewal, key_request.type);
|
|
// TODO(rfrias): Add tests cases for when license server url
|
|
// is empty on renewal. Need appropriate key id at the server.
|
|
EXPECT_NE(0u, key_request.url.size());
|
|
}
|
|
|
|
bool KeyRotationRequest(CdmLicenseType license_type,
|
|
const std::string& init_data) {
|
|
wvcdm::CdmAppParameterMap app_parameters;
|
|
CdmKeyRequest key_request;
|
|
CdmResponseType status = decryptor_->GenerateKeyRequest(
|
|
session_id_, key_set_id_, "video/mp4", init_data, license_type,
|
|
app_parameters, nullptr, kDefaultCdmIdentifier, &key_request);
|
|
EXPECT_EQ(wvcdm::KEY_ADDED, status);
|
|
EXPECT_TRUE(key_request.message.empty());
|
|
EXPECT_EQ(kKeyRequestTypeNone, key_request.type);
|
|
EXPECT_TRUE(key_request.url.empty());
|
|
return wvcdm::KEY_ADDED == status && key_request.message.empty() &&
|
|
key_request.url.empty();
|
|
}
|
|
|
|
void GenerateKeyRelease(CdmKeySetId key_set_id) {
|
|
GenerateKeyRelease(key_set_id, nullptr, nullptr);
|
|
}
|
|
|
|
void GenerateKeyRelease(CdmKeySetId key_set_id,
|
|
CdmClientPropertySet* property_set,
|
|
CdmKeyMessage* key_msg) {
|
|
license_type_ = kLicenseTypeRelease;
|
|
CdmSessionId session_id;
|
|
CdmInitData init_data;
|
|
wvcdm::CdmAppParameterMap app_parameters;
|
|
CdmKeyRequest key_request;
|
|
EXPECT_EQ(
|
|
wvcdm::KEY_MESSAGE,
|
|
decryptor_->GenerateKeyRequest(
|
|
session_id, key_set_id, "video/mp4", init_data, kLicenseTypeRelease,
|
|
app_parameters, property_set, kDefaultCdmIdentifier, &key_request));
|
|
key_msg_ = key_request.message;
|
|
EXPECT_EQ(kKeyRequestTypeRelease, key_request.type);
|
|
if (key_msg) *key_msg = key_request.message;
|
|
}
|
|
|
|
void LogResponseError(const std::string& message, int http_status_code) {
|
|
LOGD("HTTP Status code = %d", http_status_code);
|
|
LOGD("HTTP response(%zu): %s", message.size(),
|
|
wvutil::b2a_hex(message).c_str());
|
|
}
|
|
|
|
// Post a request and extract the drm message from the response
|
|
std::string GetKeyRequestResponse(const std::string& server_url,
|
|
const std::string& client_auth) {
|
|
// Use secure connection and chunk transfer coding.
|
|
UrlRequest url_request(server_url + client_auth);
|
|
EXPECT_TRUE(url_request.is_connected())
|
|
<< "Fail to connect to " << server_url << client_auth;
|
|
url_request.PostRequest(key_msg_);
|
|
std::string message;
|
|
EXPECT_TRUE(url_request.GetResponse(&message));
|
|
|
|
const int http_status_code = url_request.GetStatusCode(message);
|
|
if (kHttpOk != http_status_code) {
|
|
LogResponseError(message, http_status_code);
|
|
}
|
|
EXPECT_EQ(kHttpOk, http_status_code)
|
|
<< message << "\nwith server_url = " << server_url;
|
|
|
|
std::string drm_msg;
|
|
if (kHttpOk == http_status_code) {
|
|
LicenseRequest lic_request;
|
|
lic_request.GetDrmMessage(message, drm_msg);
|
|
LOGV("HTTP response body: (%zu bytes)", drm_msg.size());
|
|
}
|
|
return drm_msg;
|
|
}
|
|
|
|
// Post a request and extract the signed provisioning message from
|
|
// the HTTP response.
|
|
// Setting a non-zero value to |duration_seconds| will request a
|
|
// limited duration DRM certificate.
|
|
std::string GetCertRequestResponse(const std::string& server_url,
|
|
uint32_t duration_seconds = 0) {
|
|
// Use secure connection and chunk transfer coding.
|
|
std::string actual_url = server_url;
|
|
if (duration_seconds > 0) {
|
|
const auto query_start = actual_url.find('?');
|
|
if (query_start == std::string::npos) {
|
|
actual_url.push_back('?');
|
|
} else {
|
|
actual_url.push_back('&');
|
|
}
|
|
actual_url.append("options.expiration_delta_seconds=");
|
|
actual_url.append(std::to_string(duration_seconds));
|
|
}
|
|
|
|
UrlRequest url_request(actual_url);
|
|
EXPECT_TRUE(url_request.is_connected())
|
|
<< "Fail to connect to " << actual_url;
|
|
url_request.PostCertRequestInQueryString(key_msg_);
|
|
std::string message;
|
|
EXPECT_TRUE(url_request.GetResponse(&message));
|
|
|
|
int http_status_code = url_request.GetStatusCode(message);
|
|
if (kHttpOk != http_status_code) {
|
|
LogResponseError(message, http_status_code);
|
|
}
|
|
EXPECT_EQ(kHttpOk, http_status_code) << "message = " << message;
|
|
return message;
|
|
}
|
|
|
|
// Post a request and extract the signed provisioning message from
|
|
// the HTTP response.
|
|
std::string GetSecureStopResponse(const std::string& server_url,
|
|
const std::string& client_auth,
|
|
const std::string& secure_stop_request) {
|
|
// Use secure connection and chunk transfer coding.
|
|
UrlRequest url_request(server_url + client_auth);
|
|
EXPECT_TRUE(url_request.is_connected())
|
|
<< "Fail to connect to " << server_url << client_auth;
|
|
url_request.PostRequest(secure_stop_request);
|
|
std::string message;
|
|
EXPECT_TRUE(url_request.GetResponse(&message));
|
|
|
|
const int http_status_code = url_request.GetStatusCode(message);
|
|
if (kHttpOk != http_status_code) {
|
|
LogResponseError(message, http_status_code);
|
|
}
|
|
EXPECT_EQ(kHttpOk, http_status_code);
|
|
|
|
std::string secure_stop_response;
|
|
if (kHttpOk == http_status_code) {
|
|
LicenseRequest license;
|
|
license.GetDrmMessage(message, secure_stop_response);
|
|
LOGV("HTTP response body: (%zu bytes)", secure_stop_response.size());
|
|
}
|
|
return secure_stop_response;
|
|
}
|
|
|
|
void VerifyKeyRequestResponse(const std::string& server_url,
|
|
const std::string& client_auth) {
|
|
VerifyKeyRequestResponse(server_url, client_auth, false);
|
|
}
|
|
|
|
void VerifyUsageKeyRequestResponse(const std::string& server_url,
|
|
const std::string& client_auth) {
|
|
VerifyKeyRequestResponse(server_url, client_auth, true);
|
|
}
|
|
|
|
void VerifyKeyRequestResponse(const std::string& server_url,
|
|
const std::string& client_auth, bool is_usage) {
|
|
std::string response;
|
|
VerifyKeyRequestResponse(server_url, client_auth, is_usage, &response);
|
|
}
|
|
|
|
void VerifyKeyRequestResponse(const std::string& server_url,
|
|
const std::string& client_auth, bool is_usage,
|
|
std::string* response) {
|
|
VerifyKeyRequestResponse(CdmResponseType(wvcdm::KEY_ADDED), server_url,
|
|
client_auth, is_usage, response);
|
|
}
|
|
|
|
void VerifyKeyRequestResponse(const CdmResponseType& expected_response,
|
|
const std::string& server_url,
|
|
const std::string& client_auth, bool is_usage,
|
|
std::string* response) {
|
|
*response = GetKeyRequestResponse(server_url, client_auth);
|
|
|
|
EXPECT_EQ(decryptor_->AddKey(session_id_, *response, &key_set_id_),
|
|
expected_response);
|
|
EXPECT_EQ(is_usage || license_type_ == kLicenseTypeOffline,
|
|
key_set_id_.size() > 0);
|
|
}
|
|
|
|
bool VerifyDecryption(const CdmSessionId& session_id,
|
|
const SubSampleInfo& data) {
|
|
return VerifyDecryption(session_id, data, CdmResponseType(wvcdm::NO_ERROR));
|
|
}
|
|
|
|
bool VerifyDecryption(const CdmSessionId& session_id,
|
|
const SubSampleInfo& data,
|
|
CdmResponseType expected_response) {
|
|
std::vector<uint8_t> decrypt_buffer(data.encrypt_data.size());
|
|
CdmDecryptionParameters decryption_parameters(
|
|
&data.key_id, &data.encrypt_data.front(), data.encrypt_data.size(),
|
|
&data.iv, data.block_offset, &decrypt_buffer[0]);
|
|
decryption_parameters.is_encrypted = data.is_encrypted;
|
|
decryption_parameters.is_secure = data.is_secure;
|
|
decryption_parameters.subsample_flags = data.subsample_flags;
|
|
CdmResponseType status = decryptor_->Decrypt(
|
|
session_id, data.validate_key_id, decryption_parameters);
|
|
EXPECT_EQ(expected_response, status);
|
|
if (status != wvcdm::NO_ERROR) return false;
|
|
|
|
bool result = std::equal(data.decrypt_data.begin(), data.decrypt_data.end(),
|
|
decrypt_buffer.begin());
|
|
EXPECT_TRUE(result);
|
|
return result;
|
|
}
|
|
|
|
void Unprovision() {
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->Unprovision(kSecurityLevelL1, kDefaultCdmIdentifier));
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->Unprovision(kSecurityLevelL3, kDefaultCdmIdentifier));
|
|
}
|
|
|
|
bool IsProvisioned(const CdmIdentifier& identifier,
|
|
RequestedSecurityLevel requested_security_level) {
|
|
TestWvCdmClientPropertySet property_set_L3;
|
|
TestWvCdmClientPropertySet* property_set = nullptr;
|
|
|
|
if (kLevel3 == requested_security_level) {
|
|
property_set_L3.set_security_level(QUERY_VALUE_SECURITY_LEVEL_L3);
|
|
property_set = &property_set_L3;
|
|
}
|
|
|
|
CdmResponseType status = decryptor_->OpenSession(
|
|
config_.key_system(), property_set, identifier, nullptr, &session_id_);
|
|
|
|
if (status == wvcdm::NO_ERROR) {
|
|
wvcdm::CdmAppParameterMap app_parameters;
|
|
status = GenerateKeyRequest(ISO_BMFF_VIDEO_MIME_TYPE, binary_key_id(),
|
|
app_parameters, kLicenseTypeStreaming,
|
|
identifier, nullptr);
|
|
}
|
|
|
|
decryptor_->CloseSession(session_id_);
|
|
|
|
switch (status.code()) {
|
|
case wvcdm::NO_ERROR:
|
|
case KEY_MESSAGE:
|
|
return true;
|
|
case NEED_PROVISIONING:
|
|
return false;
|
|
default:
|
|
EXPECT_EQ(wvcdm::NO_ERROR, status);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void Provision() { Provision(kDefaultCdmIdentifier, kLevelDefault); }
|
|
|
|
void Provision(const CdmIdentifier& identifier,
|
|
RequestedSecurityLevel requested_security_level,
|
|
CdmProvisioningResponse* provisioning_response = nullptr) {
|
|
TestWvCdmClientPropertySet property_set_L3;
|
|
TestWvCdmClientPropertySet* property_set = nullptr;
|
|
|
|
if (kLevel3 == requested_security_level) {
|
|
property_set_L3.set_security_level(QUERY_VALUE_SECURITY_LEVEL_L3);
|
|
property_set = &property_set_L3;
|
|
}
|
|
|
|
if (IsProvisioned(identifier, requested_security_level)) return;
|
|
|
|
std::string provisioning_server;
|
|
std::string cert_authority, cert, wrapped_key;
|
|
|
|
const CdmResponseType status = decryptor_->GetProvisioningRequest(
|
|
kCertificateWidevine, cert_authority, identifier,
|
|
kEmptyServiceCertificate, requested_security_level, &key_msg_,
|
|
&provisioning_server);
|
|
EXPECT_EQ(wvcdm::NO_ERROR, status);
|
|
if (NO_ERROR != status) return;
|
|
EXPECT_THAT(provisioning_server,
|
|
IsSimilarUrlTo(kDefaultProvisioningServerUrl));
|
|
|
|
CdmProvisioningResponse response =
|
|
GetCertRequestResponse(config_.provisioning_server());
|
|
if (provisioning_response != nullptr) *provisioning_response = response;
|
|
EXPECT_NE(0, static_cast<int>(response.size()));
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->HandleProvisioningResponse(identifier, response,
|
|
requested_security_level,
|
|
&cert, &wrapped_key));
|
|
EXPECT_EQ(0, static_cast<int>(cert.size()));
|
|
EXPECT_EQ(0, static_cast<int>(wrapped_key.size()));
|
|
decryptor_->CloseSession(session_id_);
|
|
return;
|
|
}
|
|
|
|
std::string GetSecurityLevel(TestWvCdmClientPropertySet* property_set) {
|
|
EXPECT_EQ(
|
|
wvcdm::NO_ERROR,
|
|
decryptor_->OpenSession(config_.key_system(), property_set,
|
|
kDefaultCdmIdentifier, nullptr, &session_id_));
|
|
CdmQueryMap query_info;
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->QuerySessionStatus(session_id_, &query_info));
|
|
CdmQueryMap::iterator itr =
|
|
query_info.find(wvcdm::QUERY_KEY_SECURITY_LEVEL);
|
|
EXPECT_TRUE(itr != query_info.end());
|
|
decryptor_->CloseSession(session_id_);
|
|
if (itr != query_info.end()) {
|
|
return itr->second;
|
|
} else {
|
|
return "ERROR";
|
|
}
|
|
}
|
|
|
|
CdmSecurityLevel GetDefaultSecurityLevel() {
|
|
std::string level = GetSecurityLevel(nullptr).c_str();
|
|
CdmSecurityLevel security_level = kSecurityLevelUninitialized;
|
|
if (level.compare(wvcdm::QUERY_VALUE_SECURITY_LEVEL_L1) == 0) {
|
|
security_level = kSecurityLevelL1;
|
|
} else if (level.compare(wvcdm::QUERY_VALUE_SECURITY_LEVEL_L3) == 0) {
|
|
security_level = kSecurityLevelL3;
|
|
} else {
|
|
EXPECT_TRUE(false) << "Default Security level is undefined: " << level;
|
|
}
|
|
return security_level;
|
|
}
|
|
|
|
uint32_t QueryStatus(RequestedSecurityLevel security_level,
|
|
const std::string& key) {
|
|
std::string str;
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->QueryStatus(security_level, key, &str));
|
|
|
|
std::istringstream ss(str);
|
|
uint32_t value;
|
|
ss >> value;
|
|
EXPECT_FALSE(ss.fail());
|
|
EXPECT_TRUE(ss.eof());
|
|
return value;
|
|
}
|
|
|
|
bool GetSerialNumber(const CdmProvisioningResponse& response,
|
|
std::string* serial_number) {
|
|
if (serial_number == nullptr) {
|
|
EXPECT_TRUE(false);
|
|
return false;
|
|
}
|
|
|
|
CdmProvisioningResponse provisioning_response;
|
|
if (!CertificateProvisioning::ExtractAndDecodeSignedMessage(
|
|
response, &provisioning_response)) {
|
|
EXPECT_TRUE(false);
|
|
return false;
|
|
}
|
|
|
|
SignedProvisioningMessage signed_response;
|
|
if (!signed_response.ParseFromString(provisioning_response) ||
|
|
!signed_response.has_message()) {
|
|
EXPECT_TRUE(false);
|
|
return false;
|
|
}
|
|
|
|
ProvisioningResponse prov_response;
|
|
if (!prov_response.ParseFromString(signed_response.message()) ||
|
|
!prov_response.has_device_certificate()) {
|
|
EXPECT_TRUE(false);
|
|
return false;
|
|
}
|
|
|
|
if (!CertificateProvisioning::ExtractDeviceInfo(
|
|
prov_response.device_certificate(), serial_number, nullptr, nullptr,
|
|
nullptr)) {
|
|
EXPECT_TRUE(false);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
android::sp<wvcdm::WvContentDecryptionModule> decryptor_;
|
|
CdmKeyMessage key_msg_;
|
|
CdmSessionId session_id_;
|
|
CdmKeySetId key_set_id_;
|
|
CdmLicenseType license_type_;
|
|
};
|
|
|
|
TEST_F(WvCdmRequestLicenseTest, ProvisioningTest) {
|
|
Unprovision();
|
|
EXPECT_FALSE(IsProvisioned(kDefaultCdmIdentifier, kLevelDefault));
|
|
|
|
std::string provisioning_server;
|
|
CdmCertificateType cert_type = kCertificateWidevine;
|
|
std::string cert_authority, cert, wrapped_key;
|
|
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->GetProvisioningRequest(
|
|
cert_type, cert_authority, kDefaultCdmIdentifier,
|
|
kEmptyServiceCertificate, kLevelDefault, &key_msg_,
|
|
&provisioning_server));
|
|
EXPECT_THAT(provisioning_server,
|
|
IsSimilarUrlTo(kDefaultProvisioningServerUrl));
|
|
|
|
std::string response = GetCertRequestResponse(config_.provisioning_server());
|
|
EXPECT_NE(0, static_cast<int>(response.size()));
|
|
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_->HandleProvisioningResponse(
|
|
kDefaultCdmIdentifier, response, kLevelDefault,
|
|
&cert, &wrapped_key));
|
|
EXPECT_EQ(0, static_cast<int>(cert.size()));
|
|
EXPECT_EQ(0, static_cast<int>(wrapped_key.size()));
|
|
decryptor_->CloseSession(session_id_);
|
|
}
|
|
|
|
TEST_F(WvCdmRequestLicenseTest, ProvisioningTestWithServiceCertificate) {
|
|
Unprovision();
|
|
EXPECT_FALSE(IsProvisioned(kDefaultCdmIdentifier, kLevelDefault));
|
|
|
|
std::string provisioning_server;
|
|
CdmCertificateType cert_type = kCertificateWidevine;
|
|
std::string cert_authority, cert, wrapped_key;
|
|
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->GetProvisioningRequest(
|
|
cert_type, cert_authority, kDefaultCdmIdentifier,
|
|
config_.provisioning_service_certificate(), kLevelDefault,
|
|
&key_msg_, &provisioning_server));
|
|
EXPECT_THAT(provisioning_server,
|
|
IsSimilarUrlTo(kDefaultProvisioningServerUrl));
|
|
|
|
std::string response = GetCertRequestResponse(config_.provisioning_server());
|
|
EXPECT_NE(0, static_cast<int>(response.size()));
|
|
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_->HandleProvisioningResponse(
|
|
kDefaultCdmIdentifier, response, kLevelDefault,
|
|
&cert, &wrapped_key));
|
|
EXPECT_EQ(0, static_cast<int>(cert.size()));
|
|
EXPECT_EQ(0, static_cast<int>(wrapped_key.size()));
|
|
decryptor_->CloseSession(session_id_);
|
|
}
|
|
|
|
TEST_F(WvCdmRequestLicenseTest, L3ProvisioningTest) {
|
|
Unprovision();
|
|
TestWvCdmClientPropertySet property_set_L3;
|
|
property_set_L3.set_security_level(QUERY_VALUE_SECURITY_LEVEL_L3);
|
|
std::string provisioning_server;
|
|
CdmCertificateType cert_type = kCertificateWidevine;
|
|
std::string cert_authority, cert, wrapped_key;
|
|
|
|
EXPECT_EQ(
|
|
wvcdm::NO_ERROR,
|
|
decryptor_->GetProvisioningRequest(
|
|
cert_type, cert_authority, kDefaultCdmIdentifier,
|
|
kEmptyServiceCertificate, kLevel3, &key_msg_, &provisioning_server));
|
|
EXPECT_THAT(provisioning_server,
|
|
IsSimilarUrlTo(kDefaultProvisioningServerUrl));
|
|
|
|
std::string response = GetCertRequestResponse(config_.provisioning_server());
|
|
EXPECT_NE(0, static_cast<int>(response.size()));
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->HandleProvisioningResponse(
|
|
kDefaultCdmIdentifier, response, kLevel3, &cert, &wrapped_key));
|
|
EXPECT_EQ(0, static_cast<int>(cert.size()));
|
|
EXPECT_EQ(0, static_cast<int>(wrapped_key.size()));
|
|
decryptor_->CloseSession(session_id_);
|
|
}
|
|
|
|
TEST_F(WvCdmRequestLicenseTest, PerOriginProvisioningTest) {
|
|
Unprovision();
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->Unprovision(kSecurityLevelL3, kExampleIdentifier));
|
|
Provision();
|
|
|
|
// Verify the global identifier is provisioned.
|
|
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_->OpenSession(
|
|
config_.key_system(), nullptr,
|
|
kDefaultCdmIdentifier, nullptr, &session_id_));
|
|
|
|
CdmAppParameterMap app_parameters;
|
|
EXPECT_EQ(wvcdm::KEY_MESSAGE,
|
|
GenerateKeyRequest(ISO_BMFF_VIDEO_MIME_TYPE, binary_key_id(),
|
|
app_parameters, kLicenseTypeStreaming,
|
|
kDefaultCdmIdentifier, nullptr));
|
|
decryptor_->CloseSession(session_id_);
|
|
|
|
// The other identifier should not be provisioned.
|
|
EXPECT_EQ(wvcdm::NEED_PROVISIONING,
|
|
decryptor_->OpenSession(config_.key_system(), nullptr,
|
|
kExampleIdentifier, nullptr, &session_id_));
|
|
EXPECT_FALSE(IsProvisioned(kExampleIdentifier, kLevelDefault));
|
|
}
|
|
|
|
TEST_F(WvCdmRequestLicenseTest, PerOriginProvisioningSupportsOldPaths) {
|
|
// This is the current file name scheme. This test exists to ensure we do
|
|
// not break existing clients if we decide to change the naming/paths of
|
|
// certificates.
|
|
const char kOldFileName[] = "cert0LF0GfM75iqlNA_sByaQMA==.bin";
|
|
ASSERT_EQ("com.example", kExampleIdentifier.origin);
|
|
|
|
// Unprovision the device and delete all files.
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->Unprovision(kSecurityLevelL3, kExampleIdentifier));
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->Unprovision(kSecurityLevelL3, kDefaultCdmIdentifier));
|
|
Provision(kExampleIdentifier, kLevel3);
|
|
|
|
std::string base_path;
|
|
ASSERT_TRUE(Properties::GetDeviceFilesBasePath(kSecurityLevelL3, &base_path));
|
|
|
|
// Make sure that the cert exists.
|
|
std::vector<std::string> files;
|
|
ASSERT_TRUE(wvutil::FileUtils::List(base_path, &files));
|
|
ASSERT_LE(1u, files.size());
|
|
bool found_it = false;
|
|
for (std::string file : files) {
|
|
if (file == std::string(kOldFileName)) found_it = true;
|
|
}
|
|
EXPECT_TRUE(found_it);
|
|
|
|
// Reprovision the default identifier.
|
|
Provision(kDefaultCdmIdentifier, kLevel3);
|
|
}
|
|
|
|
TEST_F(WvCdmRequestLicenseTest, UnprovisionTest) {
|
|
CdmSecurityLevel security_level = GetDefaultSecurityLevel();
|
|
FileSystem file_system;
|
|
DeviceFiles handle(&file_system);
|
|
EXPECT_TRUE(handle.Init(security_level));
|
|
std::string certificate;
|
|
CryptoWrappedKey wrapped_private_key;
|
|
std::string serial_number;
|
|
uint32_t system_id;
|
|
EXPECT_EQ(
|
|
DeviceFiles::kCertificateValid,
|
|
handle.RetrieveCertificate(false, &certificate, &wrapped_private_key,
|
|
&serial_number, &system_id));
|
|
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->Unprovision(security_level, kDefaultCdmIdentifier));
|
|
EXPECT_NE(
|
|
DeviceFiles::kCertificateValid,
|
|
handle.RetrieveCertificate(false, &certificate, &wrapped_private_key,
|
|
&serial_number, &system_id));
|
|
}
|
|
|
|
TEST_F(WvCdmRequestLicenseTest, ProvisioningInterposedRetryTest) {
|
|
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
|
|
nullptr, &session_id_);
|
|
std::string provisioning_server;
|
|
CdmCertificateType cert_type = kCertificateWidevine;
|
|
std::string cert_authority, cert, wrapped_key;
|
|
CdmKeyMessage key_msg1, key_msg2;
|
|
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->GetProvisioningRequest(
|
|
cert_type, cert_authority, kDefaultCdmIdentifier,
|
|
kEmptyServiceCertificate, kLevelDefault, &key_msg1,
|
|
&provisioning_server));
|
|
EXPECT_THAT(provisioning_server,
|
|
IsSimilarUrlTo(kDefaultProvisioningServerUrl));
|
|
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->GetProvisioningRequest(
|
|
cert_type, cert_authority, kDefaultCdmIdentifier,
|
|
kEmptyServiceCertificate, kLevelDefault, &key_msg2,
|
|
&provisioning_server));
|
|
EXPECT_THAT(provisioning_server,
|
|
IsSimilarUrlTo(kDefaultProvisioningServerUrl));
|
|
|
|
// Second message should succeed.
|
|
key_msg_ = key_msg2;
|
|
std::string response = GetCertRequestResponse(config_.provisioning_server());
|
|
EXPECT_NE(0, static_cast<int>(response.size()));
|
|
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_->HandleProvisioningResponse(
|
|
kDefaultCdmIdentifier, response, kLevelDefault,
|
|
&cert, &wrapped_key));
|
|
EXPECT_EQ(0, static_cast<int>(cert.size()));
|
|
EXPECT_EQ(0, static_cast<int>(wrapped_key.size()));
|
|
|
|
// First message is ignored because second already succeeded.
|
|
key_msg_ = key_msg1;
|
|
response = GetCertRequestResponse(config_.provisioning_server());
|
|
EXPECT_NE(0, static_cast<int>(response.size()));
|
|
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_->HandleProvisioningResponse(
|
|
kDefaultCdmIdentifier, response, kLevelDefault,
|
|
&cert, &wrapped_key));
|
|
EXPECT_EQ(0, static_cast<int>(cert.size()));
|
|
EXPECT_EQ(0, static_cast<int>(wrapped_key.size()));
|
|
decryptor_->CloseSession(session_id_);
|
|
}
|
|
|
|
TEST_F(WvCdmRequestLicenseTest, ProvisioningInterspersedRetryTest) {
|
|
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
|
|
nullptr, &session_id_);
|
|
std::string provisioning_server;
|
|
CdmCertificateType cert_type = kCertificateWidevine;
|
|
std::string cert_authority, cert, wrapped_key;
|
|
|
|
CdmKeyMessage key_msg1, key_msg2;
|
|
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->GetProvisioningRequest(
|
|
cert_type, cert_authority, kDefaultCdmIdentifier,
|
|
kEmptyServiceCertificate, kLevelDefault, &key_msg1,
|
|
&provisioning_server));
|
|
EXPECT_THAT(provisioning_server,
|
|
IsSimilarUrlTo(kDefaultProvisioningServerUrl));
|
|
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->GetProvisioningRequest(
|
|
cert_type, cert_authority, kDefaultCdmIdentifier,
|
|
kEmptyServiceCertificate, kLevelDefault, &key_msg2,
|
|
&provisioning_server));
|
|
EXPECT_THAT(provisioning_server,
|
|
IsSimilarUrlTo(kDefaultProvisioningServerUrl));
|
|
|
|
// First request expected to fail, because only one message may be active.
|
|
key_msg_ = key_msg1;
|
|
std::string response = GetCertRequestResponse(config_.provisioning_server());
|
|
EXPECT_NE(0, static_cast<int>(response.size()));
|
|
EXPECT_EQ(LOAD_PROVISIONING_ERROR, decryptor_->HandleProvisioningResponse(
|
|
kDefaultCdmIdentifier, response,
|
|
kLevelDefault, &cert, &wrapped_key));
|
|
EXPECT_EQ(0, static_cast<int>(cert.size()));
|
|
EXPECT_EQ(0, static_cast<int>(wrapped_key.size()));
|
|
|
|
// Second message should succeed.
|
|
key_msg_ = key_msg2;
|
|
response = GetCertRequestResponse(config_.provisioning_server());
|
|
EXPECT_NE(0, static_cast<int>(response.size()));
|
|
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_->HandleProvisioningResponse(
|
|
kDefaultCdmIdentifier, response, kLevelDefault,
|
|
&cert, &wrapped_key));
|
|
EXPECT_EQ(0, static_cast<int>(cert.size()));
|
|
EXPECT_EQ(0, static_cast<int>(wrapped_key.size()));
|
|
|
|
decryptor_->CloseSession(session_id_);
|
|
}
|
|
|
|
TEST_F(WvCdmRequestLicenseTest, ProvisioningSpoidTest) {
|
|
CdmProvisioningResponse provisioning_response;
|
|
|
|
Unprovision();
|
|
Provision(kDefaultCdmIdentifier, kLevelDefault, &provisioning_response);
|
|
std::string default_cdm_id_serial_number_1;
|
|
EXPECT_TRUE(
|
|
GetSerialNumber(provisioning_response, &default_cdm_id_serial_number_1));
|
|
|
|
Unprovision();
|
|
Provision(kDefaultCdmIdentifier, kLevelDefault, &provisioning_response);
|
|
std::string default_cdm_id_serial_number_2;
|
|
EXPECT_TRUE(
|
|
GetSerialNumber(provisioning_response, &default_cdm_id_serial_number_2));
|
|
|
|
Unprovision();
|
|
Provision(kAlternateCdmIdentifier1, kLevelDefault, &provisioning_response);
|
|
std::string alternate_cdm_id_1_serial_number_1;
|
|
EXPECT_TRUE(GetSerialNumber(provisioning_response,
|
|
&alternate_cdm_id_1_serial_number_1));
|
|
|
|
Unprovision();
|
|
Provision(kAlternateCdmIdentifier1, kLevelDefault, &provisioning_response);
|
|
std::string alternate_cdm_id_1_serial_number_2;
|
|
EXPECT_TRUE(GetSerialNumber(provisioning_response,
|
|
&alternate_cdm_id_1_serial_number_2));
|
|
|
|
Unprovision();
|
|
Provision(kAlternateCdmIdentifier2, kLevelDefault, &provisioning_response);
|
|
std::string alternate_cdm_id_2_serial_number_1;
|
|
EXPECT_TRUE(GetSerialNumber(provisioning_response,
|
|
&alternate_cdm_id_2_serial_number_1));
|
|
|
|
Unprovision();
|
|
Provision(kAlternateCdmIdentifier2, kLevelDefault, &provisioning_response);
|
|
std::string alternate_cdm_id_2_serial_number_2;
|
|
EXPECT_TRUE(GetSerialNumber(provisioning_response,
|
|
&alternate_cdm_id_2_serial_number_2));
|
|
|
|
EXPECT_EQ(default_cdm_id_serial_number_1, default_cdm_id_serial_number_2);
|
|
EXPECT_EQ(alternate_cdm_id_1_serial_number_1,
|
|
alternate_cdm_id_1_serial_number_2);
|
|
EXPECT_EQ(alternate_cdm_id_2_serial_number_1,
|
|
alternate_cdm_id_2_serial_number_2);
|
|
|
|
EXPECT_NE(default_cdm_id_serial_number_1, alternate_cdm_id_1_serial_number_1);
|
|
EXPECT_NE(default_cdm_id_serial_number_1, alternate_cdm_id_2_serial_number_1);
|
|
EXPECT_NE(alternate_cdm_id_1_serial_number_1,
|
|
alternate_cdm_id_2_serial_number_1);
|
|
}
|
|
|
|
TEST_F(WvCdmRequestLicenseTest, ProvisioningRevocationTest) {
|
|
Unprovision();
|
|
|
|
CdmResponseType result =
|
|
decryptor_->OpenSession(config_.key_system(), nullptr,
|
|
kDefaultCdmIdentifier, nullptr, &session_id_);
|
|
|
|
switch (result.code()) {
|
|
case wvcdm::NO_ERROR:
|
|
decryptor_->CloseSession(session_id_);
|
|
return;
|
|
case NEED_PROVISIONING:
|
|
break;
|
|
default:
|
|
EXPECT_EQ(wvcdm::NO_ERROR, result);
|
|
return;
|
|
}
|
|
|
|
std::string provisioning_server;
|
|
std::string cert_authority, provisioning_request;
|
|
|
|
result = decryptor_->GetProvisioningRequest(
|
|
kCertificateWidevine, cert_authority, kDefaultCdmIdentifier,
|
|
kEmptyServiceCertificate, kLevelDefault, &provisioning_request,
|
|
&provisioning_server);
|
|
|
|
EXPECT_EQ(wvcdm::NO_ERROR, result);
|
|
if (NO_ERROR != result) return;
|
|
EXPECT_THAT(provisioning_server,
|
|
IsSimilarUrlTo(kDefaultProvisioningServerUrl));
|
|
|
|
if (!wvcdm::Properties::provisioning_messages_are_binary()) {
|
|
std::vector<uint8_t> request =
|
|
wvutil::Base64SafeDecode(provisioning_request);
|
|
provisioning_request.assign(request.begin(), request.end());
|
|
}
|
|
|
|
// Verify that provisioning request has protocol version set correctly
|
|
SignedProvisioningMessage signed_provisioning_msg;
|
|
EXPECT_TRUE(signed_provisioning_msg.ParseFromString(provisioning_request));
|
|
EXPECT_EQ(SignedProvisioningMessage_ProvisioningProtocolVersion_VERSION_1_1,
|
|
signed_provisioning_msg.protocol_version());
|
|
const bool supports_core_messages =
|
|
signed_provisioning_msg.has_oemcrypto_core_message() &&
|
|
!signed_provisioning_msg.oemcrypto_core_message().empty();
|
|
|
|
// Create provisioning responses with differing ProvisioningStatus values
|
|
const std::vector<ProvisioningResponse_ProvisioningStatus>
|
|
provisioning_status{
|
|
ProvisioningResponse_ProvisioningStatus_NO_ERROR,
|
|
ProvisioningResponse_ProvisioningStatus_REVOKED_DEVICE_CREDENTIALS,
|
|
ProvisioningResponse_ProvisioningStatus_REVOKED_DEVICE_SERIES,
|
|
// Add undefined ProvisioningStatus
|
|
static_cast<ProvisioningResponse_ProvisioningStatus>(
|
|
ProvisioningResponse_ProvisioningStatus_REVOKED_DEVICE_SERIES +
|
|
10)};
|
|
|
|
// Now form provisioning messages for each of the status values and verify
|
|
// that they have been handled correctly
|
|
for (ProvisioningResponse_ProvisioningStatus status : provisioning_status) {
|
|
result = decryptor_->GetProvisioningRequest(
|
|
kCertificateWidevine, cert_authority, kDefaultCdmIdentifier,
|
|
kEmptyServiceCertificate, kLevelDefault, &provisioning_request,
|
|
&provisioning_server);
|
|
|
|
EXPECT_EQ(wvcdm::NO_ERROR, result);
|
|
if (NO_ERROR != result) return;
|
|
EXPECT_THAT(provisioning_server,
|
|
IsSimilarUrlTo(kDefaultProvisioningServerUrl));
|
|
|
|
ProvisioningResponse provisioning_response;
|
|
provisioning_response.set_status(status);
|
|
|
|
SignedProvisioningMessage signed_response;
|
|
signed_response.set_signature("fake signature");
|
|
std::string message;
|
|
provisioning_response.SerializeToString(&message);
|
|
signed_response.set_message(message);
|
|
if (supports_core_messages) {
|
|
signed_response.set_oemcrypto_core_message("fake oemcrypto core msg");
|
|
}
|
|
|
|
std::string response;
|
|
signed_response.SerializeToString(&response);
|
|
|
|
if (!wvcdm::Properties::provisioning_messages_are_binary()) {
|
|
const std::string binary_response = std::move(response);
|
|
response = "\"signedResponse\": \"";
|
|
response.append(wvutil::Base64SafeEncode(binary_response));
|
|
response.append("\"");
|
|
}
|
|
|
|
std::string cert, wrapped_key;
|
|
result = decryptor_->HandleProvisioningResponse(
|
|
kDefaultCdmIdentifier, response, kLevelDefault, &cert, &wrapped_key);
|
|
|
|
switch (status) {
|
|
case ProvisioningResponse_ProvisioningStatus_REVOKED_DEVICE_CREDENTIALS:
|
|
case ProvisioningResponse_ProvisioningStatus_REVOKED_DEVICE_SERIES:
|
|
EXPECT_EQ(DEVICE_REVOKED, result) << "Provisioning status: " << status;
|
|
break;
|
|
default:
|
|
// ProvisioningResponse_ProvisioningStatus_NO_ERROR will not return
|
|
// NO_ERROR because signature, oemcrypto core message are fake
|
|
EXPECT_NE(DEVICE_REVOKED, result) << "Provisioning status: " << status;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Test checks that the CDM correctly reports provisioning status when
|
|
// a certificate has expired. It is expected that the CDM reports that
|
|
// the specified CDM engine is provisioned only during the certificate's
|
|
// validity period.
|
|
TEST_F(WvCdmRequestLicenseTest, ProvisioningWithExpiringCertTest) {
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->Unprovision(kSecurityLevelL1, kExampleIdentifier));
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->Unprovision(kSecurityLevelL3, kExampleIdentifier));
|
|
|
|
EXPECT_FALSE(IsProvisioned(kExampleIdentifier, kLevelDefault));
|
|
ConfigTestEnv config(kContentProtectionStagingServer, true);
|
|
|
|
// Provision
|
|
std::string provisioning_server;
|
|
const CdmCertificateType cert_type = kCertificateWidevine;
|
|
std::string cert_authority, cert, wrapped_key;
|
|
|
|
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_->GetProvisioningRequest(
|
|
cert_type, cert_authority, kExampleIdentifier,
|
|
kEmptyServiceCertificate, kLevelDefault,
|
|
&key_msg_, &provisioning_server));
|
|
|
|
const std::string response = GetCertRequestResponse(
|
|
config.provisioning_server(), kDrmCertificateExpiryPeriod);
|
|
EXPECT_FALSE(response.empty()) << "Failed to get DRM provisioning response";
|
|
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_->HandleProvisioningResponse(
|
|
kExampleIdentifier, response, kLevelDefault,
|
|
&cert, &wrapped_key));
|
|
EXPECT_TRUE(cert.empty()) << "Widevine certs should not be returned";
|
|
EXPECT_TRUE(wrapped_key.empty())
|
|
<< "Keys from Widevine certs should not be returned";
|
|
|
|
// Make sure it is provisioned, then wait for certificate expiry period
|
|
EXPECT_TRUE(IsProvisioned(kExampleIdentifier, kLevelDefault));
|
|
sleep(kDrmCertificateExpirySleepPeriod);
|
|
|
|
// Verify that it is no longer provisioned after the certificate expires
|
|
EXPECT_FALSE(IsProvisioned(kExampleIdentifier, kLevelDefault));
|
|
}
|
|
|
|
TEST_F(WvCdmRequestLicenseTest, PropertySetTest) {
|
|
TestWvCdmClientPropertySet property_set_L1;
|
|
TestWvCdmClientPropertySet property_set_L3;
|
|
TestWvCdmClientPropertySet property_set_Ln;
|
|
CdmSessionId session_id_L1;
|
|
CdmSessionId session_id_L3;
|
|
CdmSessionId session_id_Ln;
|
|
|
|
Unprovision();
|
|
Provision();
|
|
|
|
property_set_L1.set_security_level(QUERY_VALUE_SECURITY_LEVEL_L1);
|
|
property_set_L1.set_use_privacy_mode(true);
|
|
decryptor_->OpenSession(config_.key_system(), &property_set_L1,
|
|
kDefaultCdmIdentifier, nullptr, &session_id_L1);
|
|
property_set_L3.set_security_level(QUERY_VALUE_SECURITY_LEVEL_L3);
|
|
property_set_L3.set_use_privacy_mode(false);
|
|
|
|
CdmResponseType sts =
|
|
decryptor_->OpenSession(config_.key_system(), &property_set_L3,
|
|
kDefaultCdmIdentifier, nullptr, &session_id_L3);
|
|
|
|
if (NEED_PROVISIONING == sts) {
|
|
std::string provisioning_server;
|
|
CdmCertificateType cert_type = kCertificateWidevine;
|
|
std::string cert_authority, cert, wrapped_key;
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->GetProvisioningRequest(
|
|
cert_type, cert_authority, kDefaultCdmIdentifier,
|
|
kEmptyServiceCertificate, kLevel3, &key_msg_,
|
|
&provisioning_server));
|
|
EXPECT_THAT(provisioning_server,
|
|
IsSimilarUrlTo(kDefaultProvisioningServerUrl));
|
|
std::string response =
|
|
GetCertRequestResponse(config_.provisioning_server());
|
|
EXPECT_NE(0, static_cast<int>(response.size()));
|
|
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_->HandleProvisioningResponse(
|
|
kDefaultCdmIdentifier, response, kLevel3,
|
|
&cert, &wrapped_key));
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->OpenSession(config_.key_system(), &property_set_L3,
|
|
kDefaultCdmIdentifier, nullptr,
|
|
&session_id_L3));
|
|
} else {
|
|
EXPECT_EQ(wvcdm::NO_ERROR, sts);
|
|
}
|
|
|
|
property_set_Ln.set_security_level("");
|
|
decryptor_->OpenSession(config_.key_system(), &property_set_Ln,
|
|
kDefaultCdmIdentifier, nullptr, &session_id_Ln);
|
|
|
|
CdmQueryMap query_info;
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->QuerySessionStatus(session_id_L1, &query_info));
|
|
CdmQueryMap::iterator itr = query_info.find(wvcdm::QUERY_KEY_SECURITY_LEVEL);
|
|
EXPECT_TRUE(itr != query_info.end());
|
|
std::string security_level = itr->second;
|
|
EXPECT_TRUE(!security_level.compare(QUERY_VALUE_SECURITY_LEVEL_L1) ||
|
|
!security_level.compare(QUERY_VALUE_SECURITY_LEVEL_L3));
|
|
EXPECT_TRUE(Properties::UsePrivacyMode(session_id_L1));
|
|
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->QuerySessionStatus(session_id_L3, &query_info));
|
|
itr = query_info.find(wvcdm::QUERY_KEY_SECURITY_LEVEL);
|
|
EXPECT_TRUE(itr != query_info.end());
|
|
security_level = itr->second;
|
|
EXPECT_EQ(security_level, QUERY_VALUE_SECURITY_LEVEL_L3);
|
|
EXPECT_FALSE(Properties::UsePrivacyMode(session_id_L3));
|
|
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->QuerySessionStatus(session_id_Ln, &query_info));
|
|
itr = query_info.find(wvcdm::QUERY_KEY_SECURITY_LEVEL);
|
|
EXPECT_TRUE(itr != query_info.end());
|
|
security_level = itr->second;
|
|
EXPECT_TRUE(!security_level.compare(QUERY_VALUE_SECURITY_LEVEL_L1) ||
|
|
!security_level.compare(QUERY_VALUE_SECURITY_LEVEL_L3));
|
|
|
|
std::string app_id = "not empty";
|
|
EXPECT_TRUE(Properties::GetApplicationId(session_id_Ln, &app_id));
|
|
EXPECT_STREQ("", app_id.c_str());
|
|
|
|
property_set_Ln.set_app_id("com.unittest.mock.app.id");
|
|
EXPECT_TRUE(Properties::GetApplicationId(session_id_Ln, &app_id));
|
|
EXPECT_STREQ("com.unittest.mock.app.id", app_id.c_str());
|
|
|
|
decryptor_->CloseSession(session_id_L1);
|
|
decryptor_->CloseSession(session_id_L3);
|
|
decryptor_->CloseSession(session_id_Ln);
|
|
}
|
|
|
|
TEST_F(WvCdmRequestLicenseTest, ForceL3Test) {
|
|
TestWvCdmClientPropertySet property_set;
|
|
property_set.set_security_level(QUERY_VALUE_SECURITY_LEVEL_L3);
|
|
|
|
FileSystem file_system;
|
|
DeviceFiles handle(&file_system);
|
|
EXPECT_TRUE(handle.Init(kSecurityLevelL3));
|
|
EXPECT_TRUE(handle.DeleteAllFiles());
|
|
|
|
EXPECT_EQ(
|
|
NEED_PROVISIONING,
|
|
decryptor_->OpenSession(config_.key_system(), &property_set,
|
|
kDefaultCdmIdentifier, nullptr, &session_id_));
|
|
std::string provisioning_server;
|
|
CdmCertificateType cert_type = kCertificateWidevine;
|
|
std::string cert_authority, cert, wrapped_key;
|
|
EXPECT_EQ(NO_ERROR, decryptor_->GetProvisioningRequest(
|
|
cert_type, cert_authority, kDefaultCdmIdentifier,
|
|
kEmptyServiceCertificate, kLevel3, &key_msg_,
|
|
&provisioning_server));
|
|
EXPECT_THAT(provisioning_server,
|
|
IsSimilarUrlTo(kDefaultProvisioningServerUrl));
|
|
std::string response = GetCertRequestResponse(config_.provisioning_server());
|
|
EXPECT_NE(0, static_cast<int>(response.size()));
|
|
EXPECT_EQ(NO_ERROR,
|
|
decryptor_->HandleProvisioningResponse(
|
|
kDefaultCdmIdentifier, response, kLevel3, &cert, &wrapped_key));
|
|
|
|
EXPECT_EQ(NO_ERROR, decryptor_->OpenSession(
|
|
config_.key_system(), &property_set,
|
|
kDefaultCdmIdentifier, nullptr, &session_id_));
|
|
GenerateKeyRequest(binary_key_id(), kLicenseTypeStreaming);
|
|
VerifyKeyRequestResponse(config_.license_server(), config_.client_auth());
|
|
decryptor_->CloseSession(session_id_);
|
|
}
|
|
|
|
TEST_F(WvCdmRequestLicenseTest, PrivacyModeTest) {
|
|
TestWvCdmClientPropertySet property_set;
|
|
|
|
property_set.set_use_privacy_mode(true);
|
|
decryptor_->OpenSession(config_.key_system(), &property_set,
|
|
kDefaultCdmIdentifier, nullptr, &session_id_);
|
|
|
|
GenerateKeyRequest(binary_key_id(), kLicenseTypeStreaming);
|
|
std::string resp =
|
|
GetKeyRequestResponse(config_.license_server(), config_.client_auth());
|
|
EXPECT_EQ(decryptor_->AddKey(session_id_, resp, &key_set_id_),
|
|
wvcdm::NEED_KEY);
|
|
const std::string empty_init_data;
|
|
// Verify cached initialization data from previous request is used when
|
|
// empty initialization data is passed.
|
|
GenerateKeyRequest(empty_init_data, kLicenseTypeStreaming);
|
|
VerifyKeyRequestResponse(config_.license_server(), config_.client_auth());
|
|
decryptor_->CloseSession(session_id_);
|
|
}
|
|
|
|
TEST_F(WvCdmRequestLicenseTest, PrivacyModeWithServiceCertificateTest) {
|
|
TestWvCdmClientPropertySet property_set;
|
|
|
|
property_set.set_use_privacy_mode(true);
|
|
property_set.set_service_certificate(config_.license_service_certificate());
|
|
// TODO: pass config_.service_certificate() into
|
|
// CdmEngine::SetServiceCertificate()
|
|
decryptor_->OpenSession(config_.key_system(), &property_set,
|
|
kDefaultCdmIdentifier, nullptr, &session_id_);
|
|
GenerateKeyRequest(binary_key_id(), kLicenseTypeStreaming);
|
|
VerifyKeyRequestResponse(config_.license_server(), config_.client_auth());
|
|
decryptor_->CloseSession(session_id_);
|
|
}
|
|
|
|
TEST_F(WvCdmRequestLicenseTest, BaseMessageTest) {
|
|
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
|
|
nullptr, &session_id_);
|
|
GenerateKeyRequest(binary_key_id(), kLicenseTypeStreaming);
|
|
GetKeyRequestResponse(config_.license_server(), config_.client_auth());
|
|
decryptor_->CloseSession(session_id_);
|
|
}
|
|
|
|
TEST_F(WvCdmRequestLicenseTest, AddStreamingKeyTest) {
|
|
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
|
|
nullptr, &session_id_);
|
|
GenerateKeyRequest(binary_key_id(), kLicenseTypeStreaming);
|
|
VerifyKeyRequestResponse(config_.license_server(), config_.client_auth());
|
|
decryptor_->CloseSession(session_id_);
|
|
}
|
|
|
|
// Test checks that the CDM does not allow key request generations
|
|
// if the DRM certificate is expired.
|
|
TEST_F(WvCdmRequestLicenseTest, StreamingWithExpiringCertTest) {
|
|
EXPECT_EQ(NO_ERROR,
|
|
decryptor_->Unprovision(kSecurityLevelL1, kExampleIdentifier));
|
|
EXPECT_EQ(NO_ERROR,
|
|
decryptor_->Unprovision(kSecurityLevelL3, kExampleIdentifier));
|
|
|
|
EXPECT_FALSE(IsProvisioned(kExampleIdentifier, kLevelDefault));
|
|
ConfigTestEnv config(kContentProtectionStagingServer, true);
|
|
|
|
// Provision
|
|
std::string provisioning_server;
|
|
CdmCertificateType cert_type = kCertificateWidevine;
|
|
std::string cert_authority, cert, wrapped_key;
|
|
|
|
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_->GetProvisioningRequest(
|
|
cert_type, cert_authority, kExampleIdentifier,
|
|
kEmptyServiceCertificate, kLevelDefault,
|
|
&key_msg_, &provisioning_server));
|
|
|
|
const std::string response = GetCertRequestResponse(
|
|
config.provisioning_server(), kDrmCertificateExpiryPeriod);
|
|
EXPECT_FALSE(response.empty()) << "Failed to get DRM provisioning response";
|
|
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_->HandleProvisioningResponse(
|
|
kExampleIdentifier, response, kLevelDefault,
|
|
&cert, &wrapped_key));
|
|
EXPECT_TRUE(cert.empty()) << "Widevine certs should not be returned";
|
|
EXPECT_TRUE(wrapped_key.empty())
|
|
<< "Keys from Widevine certs should not be returned";
|
|
|
|
EXPECT_TRUE(IsProvisioned(kExampleIdentifier, kLevelDefault));
|
|
|
|
// Fetch a streaming license
|
|
EXPECT_EQ(NO_ERROR,
|
|
decryptor_->OpenSession(config_.key_system(), nullptr,
|
|
kExampleIdentifier, nullptr, &session_id_));
|
|
|
|
CdmAppParameterMap app_parameters;
|
|
GenerateKeyRequest(CdmResponseType(wvcdm::KEY_MESSAGE),
|
|
ISO_BMFF_VIDEO_MIME_TYPE, binary_key_id(), app_parameters,
|
|
kLicenseTypeStreaming, kExampleIdentifier, nullptr);
|
|
VerifyKeyRequestResponse(config_.license_server(), config_.client_auth());
|
|
decryptor_->CloseSession(session_id_);
|
|
|
|
sleep(kDrmCertificateExpirySleepPeriod);
|
|
|
|
// Fetch another stream license, after expiry.
|
|
EXPECT_EQ(NO_ERROR,
|
|
decryptor_->OpenSession(config_.key_system(), nullptr,
|
|
kExampleIdentifier, nullptr, &session_id_));
|
|
GenerateKeyRequest(CdmResponseType(NEED_PROVISIONING),
|
|
ISO_BMFF_VIDEO_MIME_TYPE, binary_key_id(), app_parameters,
|
|
kLicenseTypeStreaming, kExampleIdentifier, nullptr);
|
|
decryptor_->CloseSession(session_id_);
|
|
}
|
|
|
|
TEST_F(WvCdmRequestLicenseTest, AddKeyOfflineTest) {
|
|
Unprovision();
|
|
Provision();
|
|
|
|
// override default settings unless configured through the command line
|
|
std::string key_id;
|
|
std::string client_auth;
|
|
GetOfflineConfiguration(&key_id, &client_auth);
|
|
|
|
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
|
|
nullptr, &session_id_);
|
|
GenerateKeyRequest(key_id, kLicenseTypeOffline);
|
|
VerifyKeyRequestResponse(config_.license_server(), client_auth);
|
|
decryptor_->CloseSession(session_id_);
|
|
}
|
|
|
|
TEST_F(WvCdmRequestLicenseTest, RestoreOfflineKeyTest) {
|
|
Unprovision();
|
|
Provision();
|
|
|
|
// override default settings unless configured through the command line
|
|
std::string key_id;
|
|
std::string client_auth;
|
|
GetOfflineConfiguration(&key_id, &client_auth);
|
|
|
|
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
|
|
nullptr, &session_id_);
|
|
GenerateKeyRequest(key_id, kLicenseTypeOffline);
|
|
VerifyKeyRequestResponse(config_.license_server(), client_auth);
|
|
|
|
CdmKeySetId key_set_id = key_set_id_;
|
|
EXPECT_FALSE(key_set_id_.empty());
|
|
decryptor_->CloseSession(session_id_);
|
|
|
|
session_id_.clear();
|
|
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
|
|
nullptr, &session_id_);
|
|
EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_->RestoreKey(session_id_, key_set_id));
|
|
decryptor_->CloseSession(session_id_);
|
|
}
|
|
|
|
// Test checks that the CDM allows reloading of previously acquired offline
|
|
// licenses even if the DRM certificate has expired.
|
|
TEST_F(WvCdmRequestLicenseTest, RestoreOfflineKeysWithExpiringCertTest) {
|
|
EXPECT_EQ(NO_ERROR,
|
|
decryptor_->Unprovision(kSecurityLevelL1, kExampleIdentifier));
|
|
EXPECT_EQ(NO_ERROR,
|
|
decryptor_->Unprovision(kSecurityLevelL3, kExampleIdentifier));
|
|
|
|
EXPECT_FALSE(IsProvisioned(kExampleIdentifier, kLevelDefault));
|
|
ConfigTestEnv config(kContentProtectionStagingServer, true);
|
|
|
|
// Provision
|
|
std::string provisioning_server;
|
|
const CdmCertificateType cert_type = kCertificateWidevine;
|
|
std::string cert_authority, cert, wrapped_key;
|
|
|
|
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_->GetProvisioningRequest(
|
|
cert_type, cert_authority, kExampleIdentifier,
|
|
kEmptyServiceCertificate, kLevelDefault,
|
|
&key_msg_, &provisioning_server));
|
|
|
|
const std::string response = GetCertRequestResponse(
|
|
config.provisioning_server(), kDrmCertificateExpiryPeriod);
|
|
EXPECT_FALSE(response.empty()) << "Failed to get DRM provisioning response";
|
|
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_->HandleProvisioningResponse(
|
|
kExampleIdentifier, response, kLevelDefault,
|
|
&cert, &wrapped_key));
|
|
EXPECT_TRUE(cert.empty()) << "Widevine certs should not be returned";
|
|
EXPECT_TRUE(wrapped_key.empty())
|
|
<< "Keys from Widevine certs should not be returned";
|
|
|
|
EXPECT_TRUE(IsProvisioned(kExampleIdentifier, kLevelDefault));
|
|
|
|
std::string key_id;
|
|
std::string client_auth;
|
|
GetOfflineConfiguration(&key_id, &client_auth);
|
|
|
|
// Fetch offline license
|
|
EXPECT_EQ(NO_ERROR,
|
|
decryptor_->OpenSession(config_.key_system(), nullptr,
|
|
kExampleIdentifier, nullptr, &session_id_));
|
|
|
|
CdmAppParameterMap app_parameters;
|
|
GenerateKeyRequest(CdmResponseType(wvcdm::KEY_MESSAGE),
|
|
ISO_BMFF_VIDEO_MIME_TYPE, key_id, app_parameters,
|
|
kLicenseTypeOffline, kExampleIdentifier, nullptr);
|
|
VerifyKeyRequestResponse(config_.license_server(), client_auth);
|
|
|
|
const CdmKeySetId key_set_id = key_set_id_;
|
|
EXPECT_FALSE(key_set_id_.empty());
|
|
decryptor_->CloseSession(session_id_);
|
|
|
|
session_id_.clear();
|
|
|
|
// Wait till certificate expires
|
|
sleep(kDrmCertificateExpirySleepPeriod);
|
|
|
|
// Make sure the certificate has expired and the device is not provisioned.
|
|
EXPECT_FALSE(IsProvisioned(kExampleIdentifier, kLevelDefault));
|
|
|
|
// Restore offline license. Key restoration should not be prevented
|
|
// by the expired DRM certificate.
|
|
decryptor_->OpenSession(config_.key_system(), nullptr, kExampleIdentifier,
|
|
nullptr, &session_id_);
|
|
EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_->RestoreKey(session_id_, key_set_id));
|
|
decryptor_->CloseSession(session_id_);
|
|
}
|
|
|
|
TEST_F(WvCdmRequestLicenseTest, DisallowMultipleRestoreOfflineKeyTest) {
|
|
Unprovision();
|
|
Provision();
|
|
|
|
std::string key_id;
|
|
std::string client_auth;
|
|
GetOfflineConfiguration(&key_id, &client_auth);
|
|
|
|
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
|
|
nullptr, &session_id_);
|
|
GenerateKeyRequest(key_id, kLicenseTypeOffline);
|
|
VerifyKeyRequestResponse(config_.license_server(), client_auth);
|
|
|
|
CdmKeySetId key_set_id = key_set_id_;
|
|
decryptor_->CloseSession(session_id_);
|
|
|
|
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
|
|
nullptr, &session_id_);
|
|
EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_->RestoreKey(session_id_, key_set_id));
|
|
// Restoring a key twice should result in a failure.
|
|
EXPECT_NE(wvcdm::KEY_ADDED, decryptor_->RestoreKey(session_id_, key_set_id));
|
|
decryptor_->CloseSession(session_id_);
|
|
}
|
|
|
|
TEST_F(WvCdmRequestLicenseTest, ReleaseOfflineKeyTest) {
|
|
Unprovision();
|
|
Provision();
|
|
|
|
// override default settings unless configured through the command line
|
|
std::string key_id;
|
|
std::string client_auth;
|
|
GetOfflineConfiguration(&key_id, &client_auth);
|
|
|
|
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
|
|
nullptr, &session_id_);
|
|
GenerateKeyRequest(key_id, kLicenseTypeOffline);
|
|
VerifyKeyRequestResponse(config_.license_server(), client_auth);
|
|
|
|
CdmKeySetId key_set_id = key_set_id_;
|
|
EXPECT_FALSE(key_set_id_.empty());
|
|
decryptor_->CloseSession(session_id_);
|
|
|
|
session_id_.clear();
|
|
key_set_id_.clear();
|
|
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
|
|
nullptr, &session_id_);
|
|
EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_->RestoreKey(session_id_, key_set_id));
|
|
decryptor_->CloseSession(session_id_);
|
|
|
|
session_id_.clear();
|
|
key_set_id_.clear();
|
|
GenerateKeyRelease(key_set_id);
|
|
key_set_id_ = key_set_id;
|
|
VerifyKeyRequestResponse(config_.license_server(), client_auth);
|
|
}
|
|
|
|
TEST_F(WvCdmRequestLicenseTest, ReleaseOfflineKeySessionUsageDisabledTest) {
|
|
Unprovision();
|
|
Provision();
|
|
|
|
// The default offline asset "offline_clip2" has the session usage table
|
|
// entry enabled in the replay control portion of the key control block.
|
|
// To have it disabled we must use "offline_clip7", so replace the last
|
|
// char in init data with '7'. PST are also automatically inserted into
|
|
// persistent licenses with renewals. This asset has a policy that prohibits
|
|
// renewals so no PST is specified.
|
|
std::string key_id;
|
|
std::string client_auth;
|
|
GetOfflineConfiguration(&key_id, &client_auth);
|
|
key_id[key_id.size() - 1] = '7';
|
|
|
|
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
|
|
nullptr, &session_id_);
|
|
GenerateKeyRequest(key_id, kLicenseTypeOffline);
|
|
VerifyKeyRequestResponse(config_.license_server(), client_auth);
|
|
|
|
CdmKeySetId key_set_id = key_set_id_;
|
|
EXPECT_FALSE(key_set_id_.empty());
|
|
decryptor_->CloseSession(session_id_);
|
|
|
|
session_id_.clear();
|
|
key_set_id_.clear();
|
|
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
|
|
nullptr, &session_id_);
|
|
EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_->RestoreKey(session_id_, key_set_id));
|
|
decryptor_->CloseSession(session_id_);
|
|
|
|
session_id_.clear();
|
|
key_set_id_.clear();
|
|
CdmKeyMessage key_msg;
|
|
GenerateKeyRelease(key_set_id, nullptr, &key_msg);
|
|
key_set_id_ = key_set_id;
|
|
VerifyKeyRequestResponse(config_.license_server(), client_auth);
|
|
|
|
SignedMessage signed_message;
|
|
EXPECT_TRUE(signed_message.ParseFromString(key_msg));
|
|
EXPECT_EQ(SignedMessage::LICENSE_REQUEST, signed_message.type());
|
|
EXPECT_TRUE(signed_message.has_signature());
|
|
EXPECT_TRUE(!signed_message.msg().empty());
|
|
|
|
// Verify license request
|
|
video_widevine::LicenseRequest license_renewal;
|
|
EXPECT_TRUE(license_renewal.ParseFromString(signed_message.msg()));
|
|
|
|
// Verify Content Identification
|
|
const LicenseRequest_ContentIdentification& content_id =
|
|
license_renewal.content_id();
|
|
EXPECT_FALSE(content_id.has_widevine_pssh_data());
|
|
EXPECT_FALSE(content_id.has_webm_key_id());
|
|
EXPECT_TRUE(content_id.has_existing_license());
|
|
|
|
const LicenseRequest_ContentIdentification::ExistingLicense&
|
|
existing_license = content_id.existing_license();
|
|
EXPECT_TRUE(existing_license.license_id().provider_session_token().empty());
|
|
EXPECT_TRUE(existing_license.session_usage_table_entry().empty());
|
|
}
|
|
|
|
TEST_F(WvCdmRequestLicenseTest, ReleaseRetryOfflineKeyTest) {
|
|
Unprovision();
|
|
Provision();
|
|
|
|
// override default settings unless configured through the command line
|
|
std::string key_id;
|
|
std::string client_auth;
|
|
GetOfflineConfiguration(&key_id, &client_auth);
|
|
|
|
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
|
|
nullptr, &session_id_);
|
|
GenerateKeyRequest(key_id, kLicenseTypeOffline);
|
|
VerifyKeyRequestResponse(config_.license_server(), client_auth);
|
|
|
|
CdmKeySetId key_set_id = key_set_id_;
|
|
EXPECT_FALSE(key_set_id_.empty());
|
|
decryptor_->CloseSession(session_id_);
|
|
|
|
session_id_.clear();
|
|
key_set_id_.clear();
|
|
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
|
|
nullptr, &session_id_);
|
|
EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_->RestoreKey(session_id_, key_set_id));
|
|
decryptor_->CloseSession(session_id_);
|
|
|
|
session_id_.clear();
|
|
key_set_id_.clear();
|
|
GenerateKeyRelease(key_set_id);
|
|
|
|
session_id_.clear();
|
|
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
|
|
nullptr, &session_id_);
|
|
EXPECT_EQ(wvcdm::GET_RELEASED_LICENSE_ERROR,
|
|
decryptor_->RestoreKey(session_id_, key_set_id));
|
|
decryptor_->CloseSession(session_id_);
|
|
|
|
session_id_.clear();
|
|
key_set_id_.clear();
|
|
GenerateKeyRelease(key_set_id);
|
|
key_set_id_ = key_set_id;
|
|
VerifyKeyRequestResponse(config_.license_server(), client_auth);
|
|
}
|
|
|
|
TEST_F(WvCdmRequestLicenseTest, ReleaseRetryL3OfflineKeyTest) {
|
|
Unprovision();
|
|
|
|
TestWvCdmClientPropertySet property_set;
|
|
property_set.set_security_level(QUERY_VALUE_SECURITY_LEVEL_L3);
|
|
|
|
// override default settings unless configured through the command line
|
|
std::string key_id;
|
|
std::string client_auth;
|
|
GetOfflineConfiguration(&key_id, &client_auth);
|
|
|
|
if (!IsProvisioned(kDefaultCdmIdentifier, kLevel3)) {
|
|
std::string provisioning_server;
|
|
CdmCertificateType cert_type = kCertificateWidevine;
|
|
std::string cert_authority, cert, wrapped_key;
|
|
EXPECT_EQ(NO_ERROR, decryptor_->GetProvisioningRequest(
|
|
cert_type, cert_authority, kDefaultCdmIdentifier,
|
|
kEmptyServiceCertificate, kLevel3, &key_msg_,
|
|
&provisioning_server));
|
|
EXPECT_THAT(provisioning_server,
|
|
IsSimilarUrlTo(kDefaultProvisioningServerUrl));
|
|
std::string response =
|
|
GetCertRequestResponse(config_.provisioning_server());
|
|
EXPECT_NE(0, static_cast<int>(response.size()));
|
|
EXPECT_EQ(NO_ERROR, decryptor_->HandleProvisioningResponse(
|
|
kDefaultCdmIdentifier, response, kLevel3, &cert,
|
|
&wrapped_key));
|
|
EXPECT_EQ(NO_ERROR, decryptor_->OpenSession(
|
|
config_.key_system(), &property_set,
|
|
kDefaultCdmIdentifier, nullptr, &session_id_));
|
|
}
|
|
|
|
decryptor_->OpenSession(config_.key_system(), &property_set,
|
|
kDefaultCdmIdentifier, nullptr, &session_id_);
|
|
GenerateKeyRequest(key_id, kLicenseTypeOffline, &property_set);
|
|
VerifyKeyRequestResponse(config_.license_server(), client_auth);
|
|
|
|
CdmKeySetId key_set_id = key_set_id_;
|
|
EXPECT_FALSE(key_set_id_.empty());
|
|
decryptor_->CloseSession(session_id_);
|
|
|
|
session_id_.clear();
|
|
key_set_id_.clear();
|
|
decryptor_->OpenSession(config_.key_system(), &property_set,
|
|
kDefaultCdmIdentifier, nullptr, &session_id_);
|
|
EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_->RestoreKey(session_id_, key_set_id));
|
|
decryptor_->CloseSession(session_id_);
|
|
|
|
session_id_.clear();
|
|
key_set_id_.clear();
|
|
GenerateKeyRelease(key_set_id, &property_set, nullptr);
|
|
|
|
session_id_.clear();
|
|
decryptor_->OpenSession(config_.key_system(), &property_set,
|
|
kDefaultCdmIdentifier, nullptr, &session_id_);
|
|
EXPECT_EQ(wvcdm::GET_RELEASED_LICENSE_ERROR,
|
|
decryptor_->RestoreKey(session_id_, key_set_id));
|
|
decryptor_->CloseSession(session_id_);
|
|
|
|
session_id_.clear();
|
|
key_set_id_.clear();
|
|
GenerateKeyRelease(key_set_id, &property_set, nullptr);
|
|
key_set_id_ = key_set_id;
|
|
VerifyKeyRequestResponse(config_.license_server(), client_auth);
|
|
}
|
|
|
|
TEST_F(WvCdmRequestLicenseTest,
|
|
ReleaseRetryL3OfflineKeySessionUsageDisabledTest) {
|
|
Unprovision();
|
|
|
|
TestWvCdmClientPropertySet property_set;
|
|
property_set.set_security_level(QUERY_VALUE_SECURITY_LEVEL_L3);
|
|
|
|
// The default offline asset "offline_clip2" has the session usage table
|
|
// entry enabled in the replay control portion of the key control block.
|
|
// To have it disabled we must use "offline_clip1", so replace the last
|
|
// char in init data with '1'
|
|
std::string key_id;
|
|
std::string client_auth;
|
|
GetOfflineConfiguration(&key_id, &client_auth);
|
|
key_id[key_id.size() - 1] = '1';
|
|
|
|
if (!IsProvisioned(kDefaultCdmIdentifier, kLevel3)) {
|
|
std::string provisioning_server_url;
|
|
CdmCertificateType cert_type = kCertificateWidevine;
|
|
std::string cert_authority, cert, wrapped_key;
|
|
EXPECT_EQ(NO_ERROR, decryptor_->GetProvisioningRequest(
|
|
cert_type, cert_authority, kDefaultCdmIdentifier,
|
|
kEmptyServiceCertificate, kLevel3, &key_msg_,
|
|
&provisioning_server_url));
|
|
EXPECT_THAT(provisioning_server_url,
|
|
IsSimilarUrlTo(kDefaultProvisioningServerUrl));
|
|
std::string response =
|
|
GetCertRequestResponse(config_.provisioning_server());
|
|
EXPECT_NE(0, static_cast<int>(response.size()));
|
|
EXPECT_EQ(NO_ERROR, decryptor_->HandleProvisioningResponse(
|
|
kDefaultCdmIdentifier, response, kLevel3, &cert,
|
|
&wrapped_key));
|
|
EXPECT_EQ(NO_ERROR, decryptor_->OpenSession(
|
|
config_.key_system(), &property_set,
|
|
kDefaultCdmIdentifier, nullptr, &session_id_));
|
|
}
|
|
|
|
decryptor_->OpenSession(config_.key_system(), &property_set,
|
|
kDefaultCdmIdentifier, nullptr, &session_id_);
|
|
GenerateKeyRequest(key_id, kLicenseTypeOffline, &property_set);
|
|
VerifyKeyRequestResponse(config_.license_server(), client_auth);
|
|
|
|
CdmKeySetId key_set_id = key_set_id_;
|
|
EXPECT_FALSE(key_set_id_.empty());
|
|
decryptor_->CloseSession(session_id_);
|
|
|
|
session_id_.clear();
|
|
key_set_id_.clear();
|
|
decryptor_->OpenSession(config_.key_system(), &property_set,
|
|
kDefaultCdmIdentifier, nullptr, &session_id_);
|
|
EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_->RestoreKey(session_id_, key_set_id));
|
|
decryptor_->CloseSession(session_id_);
|
|
|
|
session_id_.clear();
|
|
key_set_id_.clear();
|
|
GenerateKeyRelease(key_set_id, &property_set, nullptr);
|
|
|
|
session_id_.clear();
|
|
decryptor_->OpenSession(config_.key_system(), &property_set,
|
|
kDefaultCdmIdentifier, nullptr, &session_id_);
|
|
EXPECT_EQ(wvcdm::GET_RELEASED_LICENSE_ERROR,
|
|
decryptor_->RestoreKey(session_id_, key_set_id));
|
|
decryptor_->CloseSession(session_id_);
|
|
|
|
session_id_.clear();
|
|
key_set_id_.clear();
|
|
GenerateKeyRelease(key_set_id, &property_set, nullptr);
|
|
key_set_id_ = key_set_id;
|
|
VerifyKeyRequestResponse(config_.license_server(), client_auth);
|
|
}
|
|
|
|
// This test verifies that repeated generation of the key release message
|
|
// for the same key_set_id results in the previous session being
|
|
// deallocated (rather than leaked) and a new one allocated.
|
|
TEST_F(WvCdmRequestLicenseTest, AutomatedOfflineSessionReleaseTest) {
|
|
Unprovision();
|
|
Provision();
|
|
|
|
// override default settings unless configured through the command line
|
|
std::string key_id;
|
|
std::string client_auth;
|
|
GetOfflineConfiguration(&key_id, &client_auth);
|
|
|
|
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
|
|
nullptr, &session_id_);
|
|
GenerateKeyRequest(key_id, kLicenseTypeOffline);
|
|
VerifyKeyRequestResponse(config_.license_server(), client_auth);
|
|
|
|
CdmKeySetId key_set_id = key_set_id_;
|
|
EXPECT_FALSE(key_set_id_.empty());
|
|
decryptor_->CloseSession(session_id_);
|
|
|
|
session_id_.clear();
|
|
key_set_id_.clear();
|
|
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
|
|
nullptr, &session_id_);
|
|
EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_->RestoreKey(session_id_, key_set_id));
|
|
decryptor_->CloseSession(session_id_);
|
|
|
|
uint32_t open_sessions =
|
|
QueryStatus(kLevelDefault, wvcdm::QUERY_KEY_NUMBER_OF_OPEN_SESSIONS);
|
|
|
|
session_id_.clear();
|
|
key_set_id_.clear();
|
|
GenerateKeyRelease(key_set_id);
|
|
key_set_id_ = key_set_id;
|
|
|
|
EXPECT_EQ(
|
|
++open_sessions,
|
|
QueryStatus(kLevelDefault, wvcdm::QUERY_KEY_NUMBER_OF_OPEN_SESSIONS));
|
|
|
|
session_id_.clear();
|
|
key_set_id_.clear();
|
|
GenerateKeyRelease(key_set_id);
|
|
key_set_id_ = key_set_id;
|
|
|
|
EXPECT_EQ(
|
|
open_sessions,
|
|
QueryStatus(kLevelDefault, wvcdm::QUERY_KEY_NUMBER_OF_OPEN_SESSIONS));
|
|
|
|
VerifyKeyRequestResponse(config_.license_server(), client_auth);
|
|
|
|
EXPECT_EQ(
|
|
--open_sessions,
|
|
QueryStatus(kLevelDefault, wvcdm::QUERY_KEY_NUMBER_OF_OPEN_SESSIONS));
|
|
}
|
|
|
|
TEST_F(WvCdmRequestLicenseTest, StreamingLicenseRenewal) {
|
|
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
|
|
nullptr, &session_id_);
|
|
GenerateKeyRequest(binary_key_id(), kLicenseTypeStreaming);
|
|
VerifyKeyRequestResponse(config_.license_server(), config_.client_auth());
|
|
|
|
std::string license_server;
|
|
GenerateRenewalRequest(kLicenseTypeStreaming, &license_server);
|
|
if (license_server.empty()) license_server = config_.license_server();
|
|
VerifyKeyRequestResponse(license_server, config_.client_auth());
|
|
decryptor_->CloseSession(session_id_);
|
|
}
|
|
|
|
TEST_F(WvCdmRequestLicenseTest, StreamingLicenseRenewalProhibited) {
|
|
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
|
|
nullptr, &session_id_);
|
|
std::string key_id = wvutil::a2bs_hex( // streaming_clip11
|
|
"000000437073736800000000" // blob size and pssh
|
|
"EDEF8BA979D64ACEA3C827DCD51D21ED00000023" // Widevine system id
|
|
"08011a0d7769646576696e655f746573" // pssh data
|
|
"74221073747265616d696e675f636c69703131");
|
|
GenerateKeyRequest(key_id, kLicenseTypeStreaming);
|
|
VerifyKeyRequestResponse(config_.license_server(), config_.client_auth());
|
|
|
|
std::string init_data;
|
|
wvcdm::CdmAppParameterMap app_parameters;
|
|
CdmKeyRequest key_request;
|
|
EXPECT_EQ(wvcdm::LICENSE_RENEWAL_PROHIBITED,
|
|
decryptor_->GenerateKeyRequest(
|
|
session_id_, key_set_id_, "video/mp4", init_data,
|
|
kLicenseTypeStreaming, app_parameters, nullptr,
|
|
kDefaultCdmIdentifier, &key_request));
|
|
key_msg_ = key_request.message;
|
|
decryptor_->CloseSession(session_id_);
|
|
}
|
|
|
|
TEST_F(WvCdmRequestLicenseTest, OfflineLicenseRenewal) {
|
|
Unprovision();
|
|
Provision();
|
|
|
|
// override default settings unless configured through the command line
|
|
std::string key_id;
|
|
std::string client_auth;
|
|
GetOfflineConfiguration(&key_id, &client_auth);
|
|
|
|
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
|
|
nullptr, &session_id_);
|
|
GenerateKeyRequest(key_id, kLicenseTypeOffline);
|
|
VerifyKeyRequestResponse(config_.license_server(), client_auth);
|
|
|
|
std::string license_server;
|
|
GenerateRenewalRequest(kLicenseTypeOffline, &license_server);
|
|
if (license_server.empty()) license_server = config_.license_server();
|
|
VerifyKeyRequestResponse(license_server, client_auth);
|
|
decryptor_->CloseSession(session_id_);
|
|
}
|
|
|
|
TEST_F(WvCdmRequestLicenseTest, OfflineLicenseRenewalAndRelease) {
|
|
Unprovision();
|
|
Provision();
|
|
|
|
// override default settings unless configured through the command line
|
|
std::string key_id;
|
|
std::string client_auth;
|
|
GetOfflineConfiguration(&key_id, &client_auth);
|
|
|
|
// Retrieve offline license
|
|
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
|
|
nullptr, &session_id_);
|
|
GenerateKeyRequest(key_id, kLicenseTypeOffline);
|
|
VerifyKeyRequestResponse(config_.license_server(), client_auth);
|
|
|
|
CdmKeySetId key_set_id = key_set_id_;
|
|
EXPECT_FALSE(key_set_id_.empty());
|
|
|
|
// Verify that we can decrypt a subsample
|
|
const SubSampleInfo* data = &kSingleEncryptedOfflineSubSample;
|
|
std::vector<uint8_t> decrypt_buffer(data->encrypt_data.size());
|
|
CdmDecryptionParameters decryption_parameters(
|
|
&data->key_id, &data->encrypt_data.front(), data->encrypt_data.size(),
|
|
&data->iv, data->block_offset, &decrypt_buffer[0]);
|
|
decryption_parameters.is_encrypted = data->is_encrypted;
|
|
decryption_parameters.is_secure = data->is_secure;
|
|
decryption_parameters.subsample_flags = data->subsample_flags;
|
|
|
|
EXPECT_EQ(NO_ERROR, decryptor_->Decrypt(session_id_, data->validate_key_id,
|
|
decryption_parameters));
|
|
EXPECT_TRUE(std::equal(data->decrypt_data.begin(), data->decrypt_data.end(),
|
|
decrypt_buffer.begin()));
|
|
|
|
decryptor_->CloseSession(session_id_);
|
|
session_id_.clear();
|
|
|
|
// Restore offline license and renew it
|
|
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
|
|
nullptr, &session_id_);
|
|
EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_->RestoreKey(session_id_, key_set_id));
|
|
|
|
std::string license_server;
|
|
GenerateRenewalRequest(kLicenseTypeOffline, &license_server);
|
|
EXPECT_FALSE(license_server.empty());
|
|
VerifyKeyRequestResponse(license_server, client_auth);
|
|
|
|
// Verify that we can decrypt a subsample
|
|
decrypt_buffer = data->encrypt_data;
|
|
EXPECT_EQ(NO_ERROR, decryptor_->Decrypt(session_id_, data->validate_key_id,
|
|
decryption_parameters));
|
|
EXPECT_TRUE(std::equal(data->decrypt_data.begin(), data->decrypt_data.end(),
|
|
decrypt_buffer.begin()));
|
|
|
|
decryptor_->CloseSession(session_id_);
|
|
session_id_.clear();
|
|
|
|
// Restore offline license and test it
|
|
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
|
|
nullptr, &session_id_);
|
|
EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_->RestoreKey(session_id_, key_set_id));
|
|
|
|
// Verify that we can decrypt a subsample
|
|
decrypt_buffer = data->encrypt_data;
|
|
EXPECT_EQ(NO_ERROR, decryptor_->Decrypt(session_id_, data->validate_key_id,
|
|
decryption_parameters));
|
|
EXPECT_TRUE(std::equal(data->decrypt_data.begin(), data->decrypt_data.end(),
|
|
decrypt_buffer.begin()));
|
|
|
|
decryptor_->CloseSession(session_id_);
|
|
session_id_.clear();
|
|
|
|
// Restore and release offline license
|
|
key_set_id_.clear();
|
|
GenerateKeyRelease(key_set_id);
|
|
key_set_id_ = key_set_id;
|
|
VerifyKeyRequestResponse(config_.license_server(), client_auth);
|
|
}
|
|
|
|
class WvCdmEntitlementTest : public WvCdmRequestLicenseTest,
|
|
public ::testing::WithParamInterface<
|
|
const EntitlementTestConfiguration*> {};
|
|
|
|
TEST_P(WvCdmEntitlementTest, EntitlementWithKeyRotation) {
|
|
const EntitlementTestConfiguration* config = GetParam();
|
|
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
|
|
nullptr, &session_id_);
|
|
|
|
// Fetch entitlement license
|
|
GenerateKeyRequest(config->entitlement_pssh, kLicenseTypeStreaming);
|
|
VerifyKeyRequestResponse(config_.license_server(), config_.client_auth());
|
|
|
|
// Verify that we can decrypt a subsample
|
|
ASSERT_TRUE(
|
|
VerifyDecryption(session_id_, *(config->sub_sample_with_initial_keys)));
|
|
|
|
// Key rotation
|
|
ASSERT_TRUE(
|
|
KeyRotationRequest(kLicenseTypeStreaming, config->key_rotation_pssh));
|
|
|
|
// Verify that we can decrypt a subsample
|
|
ASSERT_TRUE(
|
|
VerifyDecryption(session_id_, *(config->sub_sample_with_rotated_keys)));
|
|
|
|
decryptor_->CloseSession(session_id_);
|
|
}
|
|
|
|
INSTANTIATE_TEST_CASE_P(Cdm, WvCdmEntitlementTest,
|
|
::testing::Range(&kEntitlementTestConfiguration[0],
|
|
&kEntitlementTestConfiguration[2]));
|
|
|
|
TEST_F(WvCdmRequestLicenseTest, RemoveKeys) {
|
|
ASSERT_EQ(NO_ERROR, decryptor_->OpenSession(config_.key_system(), nullptr,
|
|
kDefaultCdmIdentifier, nullptr,
|
|
&session_id_));
|
|
GenerateKeyRequest(binary_key_id(), kLicenseTypeStreaming);
|
|
VerifyKeyRequestResponse(config_.license_server(), config_.client_auth());
|
|
|
|
const SubSampleInfo* data = &kSingleEncryptedSubSample;
|
|
std::vector<uint8_t> decrypt_buffer(data->encrypt_data.size());
|
|
CdmDecryptionParameters decryption_parameters(
|
|
&data->key_id, &data->encrypt_data.front(), data->encrypt_data.size(),
|
|
&data->iv, data->block_offset, &decrypt_buffer[0]);
|
|
decryption_parameters.is_encrypted = data->is_encrypted;
|
|
decryption_parameters.is_secure = data->is_secure;
|
|
decryption_parameters.subsample_flags = data->subsample_flags;
|
|
|
|
// Verify that we can decrypt a subsample
|
|
EXPECT_EQ(NO_ERROR, decryptor_->Decrypt(session_id_, data->validate_key_id,
|
|
decryption_parameters));
|
|
EXPECT_TRUE(std::equal(data->decrypt_data.begin(), data->decrypt_data.end(),
|
|
decrypt_buffer.begin()));
|
|
|
|
// Verify that decryption of a subsample fails after the keys are removed
|
|
ASSERT_EQ(NO_ERROR, decryptor_->RemoveKeys(session_id_));
|
|
decrypt_buffer.assign(data->encrypt_data.size(), 0);
|
|
EXPECT_EQ(NEED_KEY, decryptor_->Decrypt(session_id_, data->validate_key_id,
|
|
decryption_parameters));
|
|
|
|
ASSERT_EQ(NO_ERROR, decryptor_->CloseSession(session_id_));
|
|
}
|
|
|
|
class WvCdmStreamingLicenseRenewalTest
|
|
: public WvCdmRequestLicenseTest,
|
|
public ::testing::WithParamInterface<
|
|
const RenewWithClientIdTestConfiguration*> {};
|
|
|
|
TEST_P(WvCdmStreamingLicenseRenewalTest, WithClientId) {
|
|
const RenewWithClientIdTestConfiguration* config = GetParam();
|
|
std::string key_id;
|
|
if (config->always_include_client_id) {
|
|
key_id = wvutil::a2bs_hex(
|
|
"000000427073736800000000" // blob size and pssh
|
|
"EDEF8BA979D64ACEA3C827DCD51D21ED00000022" // Widevine system id
|
|
"08011a0d7769646576696e655f74657374220f73" // pssh data
|
|
"747265616d696e675f636c697036");
|
|
} else {
|
|
key_id = binary_key_id();
|
|
}
|
|
|
|
const uint32_t kNameValueParamSize = 2;
|
|
std::pair<std::string, std::string> name_value_params[kNameValueParamSize] = {
|
|
{"Name1", "Value1"}, {"Name2", "Value2"}};
|
|
wvcdm::CdmAppParameterMap app_parameters;
|
|
if (config->specify_app_parameters) {
|
|
for (uint32_t i = 0; i < kNameValueParamSize; ++i) {
|
|
app_parameters[name_value_params[i].first] = name_value_params[i].second;
|
|
}
|
|
}
|
|
|
|
TestWvCdmClientPropertySet property_set;
|
|
if (config->enable_privacy_mode) {
|
|
property_set.set_use_privacy_mode(true);
|
|
if (config->specify_service_certificate)
|
|
property_set.set_service_certificate(
|
|
config_.license_service_certificate());
|
|
}
|
|
// TODO: pass config_.service_certificate() into
|
|
// CdmEngine::SetServiceCertificate()
|
|
decryptor_->OpenSession(config_.key_system(), &property_set,
|
|
kDefaultCdmIdentifier, nullptr, &session_id_);
|
|
GenerateKeyRequest(key_id, app_parameters, kLicenseTypeStreaming,
|
|
&property_set);
|
|
if (config->enable_privacy_mode && !config->specify_service_certificate) {
|
|
std::string resp =
|
|
GetKeyRequestResponse(config_.license_server(), config_.client_auth());
|
|
EXPECT_EQ(decryptor_->AddKey(session_id_, resp, &key_set_id_),
|
|
wvcdm::NEED_KEY);
|
|
GenerateKeyRequest(key_id, kLicenseTypeStreaming);
|
|
}
|
|
std::string key_response;
|
|
VerifyKeyRequestResponse(config_.license_server(), config_.client_auth(),
|
|
false, &key_response);
|
|
|
|
// Validate signed license
|
|
SignedMessage signed_message;
|
|
EXPECT_TRUE(signed_message.ParseFromString(key_response))
|
|
<< config->test_description;
|
|
EXPECT_EQ(SignedMessage::LICENSE, signed_message.type())
|
|
<< config->test_description;
|
|
EXPECT_TRUE(signed_message.has_signature()) << config->test_description;
|
|
EXPECT_TRUE(!signed_message.msg().empty()) << config->test_description;
|
|
|
|
// Verify license request
|
|
video_widevine::License license;
|
|
EXPECT_TRUE(license.ParseFromString(signed_message.msg()))
|
|
<< config->test_description;
|
|
|
|
// Verify always_include_client_id
|
|
EXPECT_EQ(config->always_include_client_id,
|
|
license.policy().has_always_include_client_id());
|
|
|
|
std::string license_server;
|
|
CdmKeyMessage key_msg;
|
|
GenerateRenewalRequest(kLicenseTypeStreaming, &key_msg, &license_server);
|
|
if (license_server.empty()) license_server = config_.license_server();
|
|
|
|
// Validate signed renewal request
|
|
EXPECT_TRUE(signed_message.ParseFromString(key_msg))
|
|
<< config->test_description;
|
|
EXPECT_EQ(SignedMessage::LICENSE_REQUEST, signed_message.type())
|
|
<< config->test_description;
|
|
EXPECT_TRUE(signed_message.has_signature()) << config->test_description;
|
|
EXPECT_TRUE(!signed_message.msg().empty()) << config->test_description;
|
|
|
|
// Verify license request
|
|
video_widevine::LicenseRequest license_renewal;
|
|
EXPECT_TRUE(license_renewal.ParseFromString(signed_message.msg()))
|
|
<< config->test_description;
|
|
|
|
// Verify ClientId
|
|
EXPECT_EQ(config->always_include_client_id && !config->enable_privacy_mode,
|
|
license_renewal.has_client_id())
|
|
<< config->test_description;
|
|
|
|
if (config->specify_app_parameters) {
|
|
for (uint32_t i = 0; i < kNameValueParamSize; ++i) {
|
|
bool found = false;
|
|
for (int j = 0; j < license_renewal.client_id().client_info_size(); ++j) {
|
|
ClientIdentification_NameValue client_info =
|
|
license_renewal.client_id().client_info(i);
|
|
if (name_value_params[i].first.compare(client_info.name()) == 0 &&
|
|
name_value_params[i].second.compare(client_info.value()) == 0) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
EXPECT_TRUE(found) << config->test_description;
|
|
}
|
|
}
|
|
|
|
if (config->enable_privacy_mode) {
|
|
EXPECT_EQ(config->always_include_client_id,
|
|
license_renewal.has_encrypted_client_id())
|
|
<< config->test_description;
|
|
EXPECT_NE(
|
|
0u, license_renewal.encrypted_client_id().encrypted_client_id().size());
|
|
}
|
|
|
|
VerifyKeyRequestResponse(license_server, config_.client_auth());
|
|
decryptor_->CloseSession(session_id_);
|
|
}
|
|
|
|
INSTANTIATE_TEST_CASE_P(
|
|
Cdm, WvCdmStreamingLicenseRenewalTest,
|
|
::testing::Range(&kStreamingRenewClientIdTestConfiguration[0],
|
|
&kStreamingRenewClientIdTestConfiguration[5]));
|
|
|
|
class WvCdmOfflineLicenseReleaseTest
|
|
: public WvCdmRequestLicenseTest,
|
|
public ::testing::WithParamInterface<
|
|
const RenewWithClientIdTestConfiguration*> {};
|
|
|
|
TEST_P(WvCdmOfflineLicenseReleaseTest, WithClientId) {
|
|
Unprovision();
|
|
Provision();
|
|
|
|
const RenewWithClientIdTestConfiguration* config = GetParam();
|
|
std::string key_id;
|
|
std::string client_auth;
|
|
GetOfflineConfiguration(&key_id, &client_auth);
|
|
|
|
if (config->always_include_client_id) {
|
|
key_id = wvutil::a2bs_hex(
|
|
"00000040" // blob size
|
|
"70737368" // "pssh"
|
|
"00000000" // flags
|
|
"edef8ba979d64acea3c827dcd51d21ed" // Widevine system id
|
|
"00000020" // pssh data size
|
|
// pssh data:
|
|
"08011a0d7769646576696e655f746573"
|
|
"74220d6f66666c696e655f636c697033");
|
|
}
|
|
|
|
const uint32_t kNameValueParamSize = 2;
|
|
std::pair<std::string, std::string> name_value_params[kNameValueParamSize] = {
|
|
{"Name1", "Value1"}, {"Name2", "Value2"}};
|
|
wvcdm::CdmAppParameterMap app_parameters;
|
|
if (config->specify_app_parameters) {
|
|
for (uint32_t i = 0; i < kNameValueParamSize; ++i) {
|
|
app_parameters[name_value_params[i].first] = name_value_params[i].second;
|
|
}
|
|
}
|
|
|
|
TestWvCdmClientPropertySet property_set;
|
|
if (config->enable_privacy_mode) {
|
|
property_set.set_use_privacy_mode(true);
|
|
if (config->specify_service_certificate)
|
|
property_set.set_service_certificate(
|
|
config_.license_service_certificate());
|
|
}
|
|
// TODO: pass config_.service_certificate() into
|
|
// CdmEngine::SetServiceCertificate()
|
|
decryptor_->OpenSession(config_.key_system(), &property_set,
|
|
kDefaultCdmIdentifier, nullptr, &session_id_);
|
|
GenerateKeyRequest(key_id, app_parameters, kLicenseTypeOffline, nullptr);
|
|
if (config->enable_privacy_mode && !config->specify_service_certificate) {
|
|
std::string resp =
|
|
GetKeyRequestResponse(config_.license_server(), client_auth);
|
|
EXPECT_EQ(decryptor_->AddKey(session_id_, resp, &key_set_id_),
|
|
wvcdm::NEED_KEY);
|
|
GenerateKeyRequest(key_id, kLicenseTypeOffline);
|
|
}
|
|
std::string key_response;
|
|
VerifyKeyRequestResponse(config_.license_server(), client_auth, false,
|
|
&key_response);
|
|
|
|
// Validate signed license
|
|
SignedMessage signed_message;
|
|
EXPECT_TRUE(signed_message.ParseFromString(key_response))
|
|
<< config->test_description;
|
|
EXPECT_EQ(SignedMessage::LICENSE, signed_message.type())
|
|
<< config->test_description;
|
|
EXPECT_TRUE(signed_message.has_signature()) << config->test_description;
|
|
EXPECT_TRUE(!signed_message.msg().empty()) << config->test_description;
|
|
|
|
// Verify license request
|
|
video_widevine::License license;
|
|
EXPECT_TRUE(license.ParseFromString(signed_message.msg()))
|
|
<< config->test_description;
|
|
|
|
// Verify always_include_client_id
|
|
EXPECT_EQ(config->always_include_client_id,
|
|
license.policy().has_always_include_client_id());
|
|
|
|
CdmKeySetId key_set_id = key_set_id_;
|
|
EXPECT_TRUE(key_set_id_.size() > 0);
|
|
decryptor_->CloseSession(session_id_);
|
|
|
|
session_id_.clear();
|
|
key_set_id_.clear();
|
|
|
|
decryptor_->OpenSession(config_.key_system(), &property_set,
|
|
kDefaultCdmIdentifier, nullptr, &session_id_);
|
|
EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_->RestoreKey(session_id_, key_set_id));
|
|
decryptor_->CloseSession(session_id_);
|
|
|
|
session_id_.clear();
|
|
key_set_id_.clear();
|
|
CdmKeyMessage key_msg;
|
|
GenerateKeyRelease(key_set_id, &property_set, &key_msg);
|
|
key_set_id_ = key_set_id;
|
|
|
|
// Validate signed renewal request
|
|
EXPECT_TRUE(signed_message.ParseFromString(key_msg))
|
|
<< config->test_description;
|
|
EXPECT_EQ(SignedMessage::LICENSE_REQUEST, signed_message.type())
|
|
<< config->test_description;
|
|
EXPECT_TRUE(signed_message.has_signature()) << config->test_description;
|
|
EXPECT_TRUE(!signed_message.msg().empty()) << config->test_description;
|
|
|
|
// Verify license request
|
|
video_widevine::LicenseRequest license_release;
|
|
EXPECT_TRUE(license_release.ParseFromString(signed_message.msg()))
|
|
<< config->test_description;
|
|
|
|
// Verify ClientId
|
|
EXPECT_EQ(config->always_include_client_id && !config->enable_privacy_mode,
|
|
license_release.has_client_id())
|
|
<< config->test_description;
|
|
|
|
if (config->specify_app_parameters) {
|
|
for (uint32_t i = 0; i < kNameValueParamSize; ++i) {
|
|
bool found = false;
|
|
for (int j = 0; j < license_release.client_id().client_info_size(); ++j) {
|
|
ClientIdentification_NameValue client_info =
|
|
license_release.client_id().client_info(i);
|
|
if (name_value_params[i].first.compare(client_info.name()) == 0 &&
|
|
name_value_params[i].second.compare(client_info.value()) == 0) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
EXPECT_TRUE(found) << config->test_description;
|
|
}
|
|
}
|
|
|
|
if (config->enable_privacy_mode) {
|
|
EXPECT_EQ(config->always_include_client_id,
|
|
license_release.has_encrypted_client_id())
|
|
<< config->test_description;
|
|
EXPECT_NE(
|
|
0u, license_release.encrypted_client_id().encrypted_client_id().size());
|
|
}
|
|
|
|
VerifyKeyRequestResponse(config_.license_server(), client_auth);
|
|
decryptor_->CloseSession(session_id_);
|
|
}
|
|
|
|
INSTANTIATE_TEST_CASE_P(
|
|
Cdm, WvCdmOfflineLicenseReleaseTest,
|
|
::testing::Range(&kOfflineReleaseClientIdTestConfiguration[0],
|
|
&kOfflineReleaseClientIdTestConfiguration[4]));
|
|
|
|
class WvCdmUsageTest : public WvCdmRequestLicenseTest,
|
|
public ::testing::WithParamInterface<
|
|
const RenewWithClientIdTestConfiguration*> {};
|
|
|
|
TEST_P(WvCdmUsageTest, WithClientId) {
|
|
Unprovision();
|
|
Provision();
|
|
|
|
const CdmSecurityLevel security_level = GetDefaultSecurityLevel();
|
|
const std::string app_id = "";
|
|
FileSystem file_system;
|
|
DeviceFiles handle(&file_system);
|
|
EXPECT_TRUE(handle.Init(security_level));
|
|
std::vector<std::string> psts;
|
|
EXPECT_TRUE(handle.DeleteAllUsageInfoForApp(
|
|
DeviceFiles::GetUsageInfoFileName(app_id), &psts));
|
|
|
|
const RenewWithClientIdTestConfiguration* config = GetParam();
|
|
|
|
std::string key_id;
|
|
if (config->always_include_client_id) {
|
|
key_id = wvutil::a2bs_hex( // streaming_clip20
|
|
"000000437073736800000000" // blob size and pssh
|
|
"EDEF8BA979D64ACEA3C827DCD51D21ED00000023" // Widevine system id
|
|
"08011a0d7769646576696e655f746573" // pssh data
|
|
"74221073747265616d696e675f636c69703230");
|
|
} else {
|
|
key_id = wvutil::a2bs_hex( // streaming_clip3
|
|
"000000427073736800000000" // blob size and pssh
|
|
"EDEF8BA979D64ACEA3C827DCD51D21ED00000022" // Widevine system id
|
|
"08011a0d7769646576696e655f746573" // pssh data
|
|
"74220f73747265616d696e675f636c697033");
|
|
}
|
|
wvcdm::CdmAppParameterMap app_parameters;
|
|
TestWvCdmClientPropertySet property_set;
|
|
|
|
const SubSampleInfo* data = &kSecureStopSubSamplesIcp[0];
|
|
decryptor_->OpenSession(config_.key_system(), &property_set,
|
|
kDefaultCdmIdentifier, nullptr, &session_id_);
|
|
|
|
GenerateKeyRequest(key_id, app_parameters, kLicenseTypeStreaming,
|
|
&property_set);
|
|
|
|
std::string key_response;
|
|
VerifyKeyRequestResponse(config_.license_server(), config_.client_auth(),
|
|
true, &key_response);
|
|
|
|
// Validate signed license
|
|
SignedMessage signed_message;
|
|
EXPECT_TRUE(signed_message.ParseFromString(key_response))
|
|
<< config->test_description;
|
|
EXPECT_EQ(SignedMessage::LICENSE, signed_message.type())
|
|
<< config->test_description;
|
|
EXPECT_TRUE(signed_message.has_signature()) << config->test_description;
|
|
EXPECT_TRUE(!signed_message.msg().empty()) << config->test_description;
|
|
|
|
// Verify license request
|
|
video_widevine::License license;
|
|
EXPECT_TRUE(license.ParseFromString(signed_message.msg()))
|
|
<< config->test_description;
|
|
|
|
// Verify always_include_client_id
|
|
EXPECT_EQ(config->always_include_client_id,
|
|
license.policy().has_always_include_client_id());
|
|
|
|
EXPECT_FALSE(license.id().provider_session_token().empty());
|
|
|
|
std::vector<uint8_t> decrypt_buffer(data->encrypt_data.size());
|
|
CdmDecryptionParameters decryption_parameters(
|
|
&data->key_id, &data->encrypt_data.front(), data->encrypt_data.size(),
|
|
&data->iv, data->block_offset, decrypt_buffer.data());
|
|
decryption_parameters.is_encrypted = data->is_encrypted;
|
|
decryption_parameters.is_secure = data->is_secure;
|
|
decryption_parameters.subsample_flags = data->subsample_flags;
|
|
EXPECT_EQ(NO_ERROR, decryptor_->Decrypt(session_id_, data->validate_key_id,
|
|
decryption_parameters));
|
|
|
|
EXPECT_TRUE(std::equal(data->decrypt_data.begin(), data->decrypt_data.end(),
|
|
decrypt_buffer.begin()));
|
|
decryptor_->CloseSession(session_id_);
|
|
|
|
CdmUsageReportList usage_reports;
|
|
const CdmResponseType status =
|
|
decryptor_->GetUsageInfo(app_id, kDefaultCdmIdentifier, &usage_reports);
|
|
|
|
EXPECT_EQ(KEY_MESSAGE, status);
|
|
ASSERT_FALSE(usage_reports.empty());
|
|
|
|
// Validate signed renewal request
|
|
EXPECT_TRUE(signed_message.ParseFromString(usage_reports[0]))
|
|
<< config->test_description;
|
|
EXPECT_EQ(SignedMessage::LICENSE_REQUEST, signed_message.type())
|
|
<< config->test_description;
|
|
EXPECT_TRUE(signed_message.has_signature()) << config->test_description;
|
|
EXPECT_TRUE(!signed_message.msg().empty()) << config->test_description;
|
|
|
|
// Verify license request
|
|
video_widevine::LicenseRequest license_renewal;
|
|
EXPECT_TRUE(license_renewal.ParseFromString(signed_message.msg()))
|
|
<< config->test_description;
|
|
|
|
// Verify ClientId
|
|
EXPECT_EQ(config->always_include_client_id && !config->enable_privacy_mode,
|
|
license_renewal.has_client_id())
|
|
<< config->test_description;
|
|
|
|
if (config->enable_privacy_mode) {
|
|
EXPECT_EQ(config->always_include_client_id,
|
|
license_renewal.has_encrypted_client_id())
|
|
<< config->test_description;
|
|
EXPECT_NE(
|
|
0u, license_renewal.encrypted_client_id().encrypted_client_id().size());
|
|
}
|
|
|
|
const CdmKeyResponse release_msg = GetSecureStopResponse(
|
|
config_.license_server(), config_.client_auth(), usage_reports[0]);
|
|
EXPECT_EQ(NO_ERROR,
|
|
decryptor_->ReleaseUsageInfo(release_msg, kDefaultCdmIdentifier));
|
|
}
|
|
|
|
INSTANTIATE_TEST_CASE_P(Cdm, WvCdmUsageTest,
|
|
::testing::Range(&kUsageClientIdTestConfiguration[0],
|
|
&kUsageClientIdTestConfiguration[2]));
|
|
|
|
TEST_F(WvCdmRequestLicenseTest, SecureStopRetryTest) {
|
|
Unprovision();
|
|
Provision();
|
|
|
|
const CdmSecurityLevel security_level = GetDefaultSecurityLevel();
|
|
const std::string app_id = "";
|
|
FileSystem file_system;
|
|
DeviceFiles handle(&file_system);
|
|
EXPECT_TRUE(handle.Init(security_level));
|
|
std::vector<CdmSecureStopId> psts;
|
|
EXPECT_TRUE(handle.DeleteAllUsageInfoForApp(
|
|
DeviceFiles::GetUsageInfoFileName(app_id), &psts));
|
|
|
|
const SubSampleInfo* data = &kSecureStopSubSamplesIcp[0];
|
|
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
|
|
nullptr, &session_id_);
|
|
const std::string key_id = wvutil::a2bs_hex(
|
|
"000000427073736800000000" // blob size and pssh
|
|
"EDEF8BA979D64ACEA3C827DCD51D21ED00000022" // Widevine system id
|
|
"08011a0d7769646576696e655f74657374220f73" // pssh data
|
|
"747265616d696e675f636c697033");
|
|
|
|
GenerateKeyRequest(key_id, kLicenseTypeStreaming, nullptr);
|
|
VerifyUsageKeyRequestResponse(config_.license_server(),
|
|
config_.client_auth());
|
|
|
|
std::vector<uint8_t> decrypt_buffer(data->encrypt_data.size());
|
|
CdmDecryptionParameters decryption_parameters(
|
|
&data->key_id, &data->encrypt_data.front(), data->encrypt_data.size(),
|
|
&data->iv, data->block_offset, decrypt_buffer.data());
|
|
decryption_parameters.is_encrypted = data->is_encrypted;
|
|
decryption_parameters.is_secure = data->is_secure;
|
|
decryption_parameters.subsample_flags = data->subsample_flags;
|
|
EXPECT_EQ(NO_ERROR, decryptor_->Decrypt(session_id_, data->validate_key_id,
|
|
decryption_parameters));
|
|
|
|
EXPECT_TRUE(std::equal(data->decrypt_data.begin(), data->decrypt_data.end(),
|
|
decrypt_buffer.begin()));
|
|
decryptor_->CloseSession(session_id_);
|
|
|
|
CdmUsageReportList usage_reports;
|
|
CdmResponseType status =
|
|
decryptor_->GetUsageInfo(app_id, kDefaultCdmIdentifier, &usage_reports);
|
|
EXPECT_EQ(usage_reports.empty() ? NO_ERROR : KEY_MESSAGE, status.code());
|
|
|
|
// Discard and retry to verify usage reports can be generated multiple times
|
|
// before release.
|
|
status =
|
|
decryptor_->GetUsageInfo(app_id, kDefaultCdmIdentifier, &usage_reports);
|
|
EXPECT_EQ(usage_reports.empty() ? NO_ERROR : KEY_MESSAGE, status.code());
|
|
int error_count = 0;
|
|
while (usage_reports.size() > 0) {
|
|
for (size_t i = 0; i < usage_reports.size(); ++i) {
|
|
const CdmKeyResponse release_msg = GetSecureStopResponse(
|
|
config_.license_server(), config_.client_auth(), usage_reports[i]);
|
|
EXPECT_EQ(NO_ERROR, decryptor_->ReleaseUsageInfo(release_msg,
|
|
kDefaultCdmIdentifier))
|
|
<< i << "/" << usage_reports.size() << " (err " << (error_count++)
|
|
<< ")" << release_msg;
|
|
}
|
|
ASSERT_LE(error_count, 100); // Give up after 100 failures.
|
|
status =
|
|
decryptor_->GetUsageInfo(app_id, kDefaultCdmIdentifier, &usage_reports);
|
|
switch (status.code()) {
|
|
case KEY_MESSAGE:
|
|
EXPECT_FALSE(usage_reports.empty());
|
|
break;
|
|
case NO_ERROR:
|
|
EXPECT_TRUE(usage_reports.empty());
|
|
break;
|
|
default:
|
|
FAIL() << "GetUsageInfo failed with error " << static_cast<int>(status);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST_F(WvCdmRequestLicenseTest, SecureStop_ReleaseThreeRecords) {
|
|
Unprovision();
|
|
Provision();
|
|
|
|
const CdmSecurityLevel security_level = GetDefaultSecurityLevel();
|
|
const std::string app_id = "";
|
|
FileSystem file_system;
|
|
DeviceFiles handle(&file_system);
|
|
EXPECT_TRUE(handle.Init(security_level));
|
|
std::vector<std::string> psts;
|
|
EXPECT_TRUE(handle.DeleteAllUsageInfoForApp(
|
|
DeviceFiles::GetUsageInfoFileName(app_id), &psts));
|
|
|
|
std::string session_id_clip3, session_id_clip4, session_id_clip7;
|
|
|
|
// Open session for streaming_clip3 and verify decryption is successful
|
|
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
|
|
nullptr, &session_id_clip3);
|
|
|
|
session_id_ = session_id_clip3;
|
|
GenerateKeyRequest(kPsshStreamingClip3, kLicenseTypeStreaming, nullptr);
|
|
VerifyUsageKeyRequestResponse(config_.license_server(),
|
|
config_.client_auth());
|
|
|
|
EXPECT_TRUE(VerifyDecryption(session_id_clip3, kSecureStopSubSamplesIcp[0]));
|
|
|
|
// Open session for streaming_clip4 and verify decryption is successful
|
|
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
|
|
nullptr, &session_id_clip4);
|
|
|
|
session_id_ = session_id_clip4;
|
|
GenerateKeyRequest(kPsshStreamingClip4, kLicenseTypeStreaming, nullptr);
|
|
VerifyUsageKeyRequestResponse(config_.license_server(),
|
|
config_.client_auth());
|
|
|
|
EXPECT_TRUE(VerifyDecryption(session_id_clip4, kSecureStopSubSamplesIcp[1]));
|
|
|
|
// Open session for streaming_clip7 and verify decryption is successful
|
|
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
|
|
nullptr, &session_id_clip7);
|
|
|
|
session_id_ = session_id_clip7;
|
|
GenerateKeyRequest(kPsshStreamingClip7, kLicenseTypeStreaming, nullptr);
|
|
VerifyUsageKeyRequestResponse(config_.license_server(),
|
|
config_.client_auth());
|
|
|
|
EXPECT_TRUE(VerifyDecryption(session_id_clip7, kSecureStopSubSamplesIcp[4]));
|
|
|
|
// Close session for streaming_clip4 and release usage information
|
|
decryptor_->CloseSession(session_id_clip4);
|
|
|
|
CdmUsageReport usage_report;
|
|
CdmResponseType status =
|
|
decryptor_->GetUsageInfo(app_id, kProviderSessionTokenStreamingClip4,
|
|
kDefaultCdmIdentifier, &usage_report);
|
|
EXPECT_EQ(KEY_MESSAGE, status);
|
|
ASSERT_FALSE(usage_report.empty());
|
|
CdmKeyMessage release_msg = GetSecureStopResponse(
|
|
config_.license_server(), config_.client_auth(), usage_report);
|
|
EXPECT_EQ(NO_ERROR,
|
|
decryptor_->ReleaseUsageInfo(release_msg, kDefaultCdmIdentifier));
|
|
|
|
decryptor_->CloseSession(session_id_clip7);
|
|
|
|
status = decryptor_->GetUsageInfo(app_id, kProviderSessionTokenStreamingClip7,
|
|
kDefaultCdmIdentifier, &usage_report);
|
|
EXPECT_EQ(KEY_MESSAGE, status);
|
|
ASSERT_FALSE(usage_report.empty());
|
|
release_msg = GetSecureStopResponse(config_.license_server(),
|
|
config_.client_auth(), usage_report);
|
|
EXPECT_EQ(NO_ERROR,
|
|
decryptor_->ReleaseUsageInfo(release_msg, kDefaultCdmIdentifier));
|
|
|
|
decryptor_->CloseSession(session_id_clip3);
|
|
|
|
status = decryptor_->GetUsageInfo(app_id, kProviderSessionTokenStreamingClip3,
|
|
kDefaultCdmIdentifier, &usage_report);
|
|
EXPECT_EQ(KEY_MESSAGE, status);
|
|
ASSERT_FALSE(usage_report.empty());
|
|
release_msg = GetSecureStopResponse(config_.license_server(),
|
|
config_.client_auth(), usage_report);
|
|
EXPECT_EQ(NO_ERROR,
|
|
decryptor_->ReleaseUsageInfo(release_msg, kDefaultCdmIdentifier));
|
|
}
|
|
|
|
class WvCdmSecureStopTest
|
|
: public WvCdmRequestLicenseTest,
|
|
public ::testing::WithParamInterface<const SecureStopSubSampleInfo*> {};
|
|
|
|
TEST_P(WvCdmSecureStopTest, SecureStop) {
|
|
Unprovision();
|
|
|
|
const SecureStopSubSampleInfo* secure_stop_data = GetParam();
|
|
TestWvCdmClientPropertySet client_property_set;
|
|
TestWvCdmClientPropertySet* property_set = nullptr;
|
|
if (kLevel3 == secure_stop_data->security_level) {
|
|
client_property_set.set_security_level(QUERY_VALUE_SECURITY_LEVEL_L3);
|
|
property_set = &client_property_set;
|
|
Provision(kDefaultCdmIdentifier, kLevel3);
|
|
Provision();
|
|
} else {
|
|
Provision();
|
|
}
|
|
|
|
const CdmSecurityLevel security_level = GetDefaultSecurityLevel();
|
|
FileSystem file_system;
|
|
DeviceFiles handle(&file_system);
|
|
EXPECT_TRUE(handle.Init(security_level));
|
|
std::vector<std::string> psts;
|
|
EXPECT_TRUE(handle.DeleteAllUsageInfoForApp(
|
|
DeviceFiles::GetUsageInfoFileName(secure_stop_data->app_id), &psts));
|
|
|
|
for (size_t i = 0; i < secure_stop_data->sub_sample_count; ++i) {
|
|
const SubSampleInfo& data = secure_stop_data->sub_sample[i];
|
|
decryptor_->OpenSession(config_.key_system(), property_set,
|
|
kDefaultCdmIdentifier, nullptr, &session_id_);
|
|
std::string key_id = wvutil::a2bs_hex(
|
|
"000000427073736800000000" // blob size and pssh
|
|
"EDEF8BA979D64ACEA3C827DCD51D21ED00000022" // Widevine system id
|
|
"08011a0d7769646576696e655f74657374220f73" // pssh data
|
|
"747265616d696e675f636c6970");
|
|
|
|
const char ch = '3' + i;
|
|
key_id.append(1, ch);
|
|
|
|
GenerateKeyRequest(key_id, kLicenseTypeStreaming, property_set);
|
|
|
|
// TODO(rfrias): streaming_clip6 is a streaming license without a pst
|
|
if (ch == '6')
|
|
VerifyKeyRequestResponse(config_.license_server(), config_.client_auth(),
|
|
false);
|
|
else
|
|
VerifyUsageKeyRequestResponse(config_.license_server(),
|
|
config_.client_auth());
|
|
|
|
std::vector<uint8_t> decrypt_buffer(data.encrypt_data.size());
|
|
CdmDecryptionParameters decryption_parameters(
|
|
&data.key_id, &data.encrypt_data.front(), data.encrypt_data.size(),
|
|
&data.iv, data.block_offset, &decrypt_buffer[0]);
|
|
decryption_parameters.is_encrypted = data.is_encrypted;
|
|
decryption_parameters.is_secure = data.is_secure;
|
|
decryption_parameters.subsample_flags = data.subsample_flags;
|
|
EXPECT_EQ(NO_ERROR, decryptor_->Decrypt(session_id_, data.validate_key_id,
|
|
decryption_parameters));
|
|
|
|
EXPECT_TRUE(std::equal(data.decrypt_data.begin(), data.decrypt_data.end(),
|
|
decrypt_buffer.begin()));
|
|
decryptor_->CloseSession(session_id_);
|
|
}
|
|
|
|
CdmUsageReportList usage_reports;
|
|
CdmResponseType status = decryptor_->GetUsageInfo(
|
|
secure_stop_data->app_id, kDefaultCdmIdentifier, &usage_reports);
|
|
EXPECT_EQ(usage_reports.empty() ? NO_ERROR : KEY_MESSAGE, status);
|
|
int error_count = 0;
|
|
while (!usage_reports.empty()) {
|
|
for (size_t i = 0; i < usage_reports.size(); ++i) {
|
|
const CdmKeyMessage release_msg = GetSecureStopResponse(
|
|
config_.license_server(), config_.client_auth(), usage_reports[i]);
|
|
EXPECT_EQ(NO_ERROR, decryptor_->ReleaseUsageInfo(release_msg,
|
|
kDefaultCdmIdentifier))
|
|
<< i << "/" << usage_reports.size() << " (err " << (error_count++)
|
|
<< ")" << release_msg;
|
|
}
|
|
ASSERT_LE(error_count, 100); // Give up after 100 failures.
|
|
status = decryptor_->GetUsageInfo(secure_stop_data->app_id,
|
|
kDefaultCdmIdentifier, &usage_reports);
|
|
EXPECT_EQ(usage_reports.empty() ? NO_ERROR : KEY_MESSAGE, status);
|
|
}
|
|
}
|
|
|
|
INSTANTIATE_TEST_CASE_P(Cdm, WvCdmSecureStopTest,
|
|
::testing::Values(&kSecureStopSubSampleInfo[0],
|
|
&kSecureStopSubSampleInfo[1],
|
|
&kSecureStopSubSampleInfo[2],
|
|
&kSecureStopSubSampleInfo[3],
|
|
&kSecureStopSubSampleInfo[4],
|
|
&kSecureStopSubSampleInfo[5],
|
|
&kSecureStopSubSampleInfo[6],
|
|
&kSecureStopSubSampleInfo[7]));
|
|
|
|
TEST_F(WvCdmRequestLicenseTest, SecureStop_RemoveAllTest) {
|
|
Unprovision();
|
|
|
|
const std::string app_id_empty = "";
|
|
const std::string app_id_not_empty = "not empty";
|
|
|
|
TestWvCdmClientPropertySet property_set;
|
|
Provision();
|
|
|
|
const CdmSecurityLevel security_level = GetDefaultSecurityLevel();
|
|
FileSystem file_system;
|
|
DeviceFiles handle(&file_system);
|
|
EXPECT_TRUE(handle.Init(security_level));
|
|
std::vector<CdmSecureStopId> psts;
|
|
EXPECT_TRUE(handle.DeleteAllUsageInfoForApp(
|
|
DeviceFiles::GetUsageInfoFileName(""), &psts));
|
|
|
|
for (size_t i = 0; i < ArraySize(kSecureStopSubSamplesIcp); ++i) {
|
|
const SubSampleInfo& data = kSecureStopSubSamplesIcp[i];
|
|
property_set.set_app_id(i % 2 == 0 ? app_id_empty : app_id_not_empty);
|
|
decryptor_->OpenSession(config_.key_system(), &property_set,
|
|
kDefaultCdmIdentifier, nullptr, &session_id_);
|
|
std::string key_id = wvutil::a2bs_hex(
|
|
"000000427073736800000000" // blob size and pssh
|
|
"EDEF8BA979D64ACEA3C827DCD51D21ED00000022" // Widevine system id
|
|
"08011a0d7769646576696e655f74657374220f73" // pssh data
|
|
"747265616d696e675f636c6970");
|
|
|
|
const char ch = '3' + i;
|
|
key_id.append(1, ch);
|
|
|
|
GenerateKeyRequest(key_id, kLicenseTypeStreaming, &property_set);
|
|
|
|
// TODO(rfrias): streaming_clip6 is a streaming license without a pst
|
|
if (ch == '6')
|
|
VerifyKeyRequestResponse(config_.license_server(), config_.client_auth(),
|
|
false);
|
|
else
|
|
VerifyUsageKeyRequestResponse(config_.license_server(),
|
|
config_.client_auth());
|
|
|
|
std::vector<uint8_t> decrypt_buffer(data.encrypt_data.size());
|
|
CdmDecryptionParameters decryption_parameters(
|
|
&data.key_id, &data.encrypt_data.front(), data.encrypt_data.size(),
|
|
&data.iv, data.block_offset, decrypt_buffer.data());
|
|
decryption_parameters.is_encrypted = data.is_encrypted;
|
|
decryption_parameters.is_secure = data.is_secure;
|
|
decryption_parameters.subsample_flags = data.subsample_flags;
|
|
EXPECT_EQ(NO_ERROR, decryptor_->Decrypt(session_id_, data.validate_key_id,
|
|
decryption_parameters));
|
|
|
|
EXPECT_TRUE(std::equal(data.decrypt_data.begin(), data.decrypt_data.end(),
|
|
decrypt_buffer.begin()));
|
|
decryptor_->CloseSession(session_id_);
|
|
}
|
|
|
|
CdmUsageReportList usage_reports;
|
|
EXPECT_EQ(KEY_MESSAGE,
|
|
decryptor_->GetUsageInfo(app_id_empty, kDefaultCdmIdentifier,
|
|
&usage_reports));
|
|
EXPECT_FALSE(usage_reports.empty());
|
|
EXPECT_EQ(KEY_MESSAGE,
|
|
decryptor_->GetUsageInfo(app_id_not_empty, kDefaultCdmIdentifier,
|
|
&usage_reports));
|
|
EXPECT_FALSE(usage_reports.empty());
|
|
|
|
EXPECT_EQ(NO_ERROR, decryptor_->RemoveAllUsageInfo(app_id_not_empty,
|
|
kDefaultCdmIdentifier));
|
|
|
|
EXPECT_EQ(NO_ERROR,
|
|
decryptor_->GetUsageInfo(app_id_not_empty, kDefaultCdmIdentifier,
|
|
&usage_reports));
|
|
EXPECT_TRUE(usage_reports.empty());
|
|
EXPECT_EQ(KEY_MESSAGE,
|
|
decryptor_->GetUsageInfo(app_id_empty, kDefaultCdmIdentifier,
|
|
&usage_reports));
|
|
EXPECT_FALSE(usage_reports.empty());
|
|
|
|
EXPECT_EQ(NO_ERROR, decryptor_->RemoveAllUsageInfo(app_id_empty,
|
|
kDefaultCdmIdentifier));
|
|
|
|
EXPECT_EQ(NO_ERROR,
|
|
decryptor_->GetUsageInfo(app_id_not_empty, kDefaultCdmIdentifier,
|
|
&usage_reports));
|
|
EXPECT_TRUE(usage_reports.empty());
|
|
EXPECT_EQ(NO_ERROR, decryptor_->GetUsageInfo(
|
|
app_id_empty, kDefaultCdmIdentifier, &usage_reports));
|
|
EXPECT_TRUE(usage_reports.empty());
|
|
}
|
|
|
|
TEST_F(WvCdmRequestLicenseTest, SecureStop_RemoveCorruptedTest) {
|
|
Unprovision();
|
|
|
|
const std::string app_id_empty = "";
|
|
const std::string app_id_not_empty = "not empty";
|
|
|
|
TestWvCdmClientPropertySet property_set;
|
|
Provision();
|
|
|
|
const CdmSecurityLevel security_level = GetDefaultSecurityLevel();
|
|
FileSystem file_system;
|
|
DeviceFiles handle(&file_system);
|
|
ASSERT_TRUE(handle.Init(security_level));
|
|
std::vector<CdmSecureStopId> psts;
|
|
EXPECT_TRUE(handle.DeleteAllUsageInfoForApp(
|
|
DeviceFiles::GetUsageInfoFileName(""), &psts));
|
|
|
|
for (size_t i = 0; i < ArraySize(kSecureStopSubSamplesIcp); ++i) {
|
|
const SubSampleInfo& data = kSecureStopSubSamplesIcp[i];
|
|
property_set.set_app_id(i % 2 == 0 ? app_id_empty : app_id_not_empty);
|
|
decryptor_->OpenSession(config_.key_system(), &property_set,
|
|
kDefaultCdmIdentifier, nullptr, &session_id_);
|
|
std::string key_id = wvutil::a2bs_hex(
|
|
"000000427073736800000000" // blob size and pssh
|
|
"EDEF8BA979D64ACEA3C827DCD51D21ED00000022" // Widevine system id
|
|
"08011a0d7769646576696e655f74657374220f73" // pssh data
|
|
"747265616d696e675f636c6970");
|
|
|
|
const char ch = '3' + i;
|
|
key_id.append(1, ch);
|
|
|
|
GenerateKeyRequest(key_id, kLicenseTypeStreaming, &property_set);
|
|
|
|
// TODO(rfrias): streaming_clip6 is a streaming license without a pst
|
|
if (ch == '6')
|
|
VerifyKeyRequestResponse(config_.license_server(), config_.client_auth(),
|
|
false);
|
|
else
|
|
VerifyUsageKeyRequestResponse(config_.license_server(),
|
|
config_.client_auth());
|
|
|
|
std::vector<uint8_t> decrypt_buffer(data.encrypt_data.size());
|
|
CdmDecryptionParameters decryption_parameters(
|
|
&data.key_id, &data.encrypt_data.front(), data.encrypt_data.size(),
|
|
&data.iv, data.block_offset, decrypt_buffer.data());
|
|
decryption_parameters.is_encrypted = data.is_encrypted;
|
|
decryption_parameters.is_secure = data.is_secure;
|
|
decryption_parameters.subsample_flags = data.subsample_flags;
|
|
EXPECT_EQ(NO_ERROR, decryptor_->Decrypt(session_id_, data.validate_key_id,
|
|
decryption_parameters));
|
|
|
|
EXPECT_TRUE(std::equal(data.decrypt_data.begin(), data.decrypt_data.end(),
|
|
decrypt_buffer.begin()));
|
|
decryptor_->CloseSession(session_id_);
|
|
}
|
|
|
|
CdmUsageReportList usage_reports;
|
|
EXPECT_EQ(KEY_MESSAGE,
|
|
decryptor_->GetUsageInfo(app_id_empty, kDefaultCdmIdentifier,
|
|
&usage_reports));
|
|
EXPECT_FALSE(usage_reports.empty());
|
|
EXPECT_EQ(KEY_MESSAGE,
|
|
decryptor_->GetUsageInfo(app_id_not_empty, kDefaultCdmIdentifier,
|
|
&usage_reports));
|
|
EXPECT_FALSE(usage_reports.empty());
|
|
|
|
// Read in usage info file
|
|
std::string path;
|
|
EXPECT_TRUE(Properties::GetDeviceFilesBasePath(security_level, &path));
|
|
const std::string secure_stop_not_empty_app_id_file_name =
|
|
path + DeviceFiles::GetUsageInfoFileName(app_id_not_empty);
|
|
|
|
ssize_t file_size =
|
|
file_system.FileSize(secure_stop_not_empty_app_id_file_name);
|
|
EXPECT_LT(4, file_size);
|
|
std::unique_ptr<wvutil::File> file = file_system.Open(
|
|
secure_stop_not_empty_app_id_file_name, FileSystem::kReadOnly);
|
|
ASSERT_NE(file, nullptr);
|
|
std::string file_data;
|
|
file_data.resize(file_size);
|
|
ssize_t bytes = file->Read(&file_data[0], file_data.size());
|
|
EXPECT_EQ(file_size, bytes);
|
|
|
|
// Corrupt the hash of the usage info file with non-empty app id and write
|
|
// it back out
|
|
memset(&file_data[0] + bytes - 4, 0, 4);
|
|
file = file_system.Open(secure_stop_not_empty_app_id_file_name,
|
|
FileSystem::kCreate | FileSystem::kTruncate);
|
|
ASSERT_NE(file, nullptr);
|
|
bytes = file->Write(file_data.data(), file_data.size());
|
|
EXPECT_EQ(file_size, bytes);
|
|
|
|
EXPECT_EQ(NO_ERROR, decryptor_->RemoveAllUsageInfo(app_id_not_empty,
|
|
kDefaultCdmIdentifier));
|
|
|
|
EXPECT_EQ(NO_ERROR,
|
|
decryptor_->GetUsageInfo(app_id_not_empty, kDefaultCdmIdentifier,
|
|
&usage_reports));
|
|
EXPECT_TRUE(usage_reports.empty());
|
|
EXPECT_EQ(KEY_MESSAGE,
|
|
decryptor_->GetUsageInfo(app_id_empty, kDefaultCdmIdentifier,
|
|
&usage_reports));
|
|
EXPECT_TRUE(usage_reports.size() > 0);
|
|
|
|
// Read in usage info file
|
|
const std::string secure_stop_empty_app_id_file_name =
|
|
path + DeviceFiles::GetUsageInfoFileName(app_id_empty);
|
|
|
|
file_size = file_system.FileSize(secure_stop_empty_app_id_file_name);
|
|
EXPECT_LT(4, file_size);
|
|
file = file_system.Open(secure_stop_empty_app_id_file_name,
|
|
FileSystem::kReadOnly);
|
|
ASSERT_NE(file, nullptr);
|
|
file_data.resize(file_size);
|
|
bytes = file->Read(&file_data[0], file_data.size());
|
|
EXPECT_EQ(file_size, bytes);
|
|
|
|
// Corrupt the hash of the usage info file with empty app id and write it
|
|
// back out
|
|
memset(&file_data[0] + bytes - 4, 0, 4);
|
|
file = file_system.Open(secure_stop_empty_app_id_file_name,
|
|
FileSystem::kCreate | FileSystem::kTruncate);
|
|
ASSERT_NE(file, nullptr);
|
|
bytes = file->Write(file_data.data(), file_data.size());
|
|
EXPECT_EQ(file_size, bytes);
|
|
|
|
EXPECT_EQ(NO_ERROR, decryptor_->RemoveAllUsageInfo(app_id_empty,
|
|
kDefaultCdmIdentifier));
|
|
|
|
EXPECT_EQ(NO_ERROR,
|
|
decryptor_->GetUsageInfo(app_id_not_empty, kDefaultCdmIdentifier,
|
|
&usage_reports));
|
|
EXPECT_TRUE(usage_reports.empty());
|
|
EXPECT_EQ(NO_ERROR, decryptor_->GetUsageInfo(
|
|
app_id_empty, kDefaultCdmIdentifier, &usage_reports));
|
|
EXPECT_TRUE(usage_reports.empty());
|
|
}
|
|
|
|
TEST_F(WvCdmRequestLicenseTest, SecureStop_RemoveCorruptedTest2) {
|
|
Unprovision();
|
|
|
|
const std::string app_id_empty = "";
|
|
const std::string app_id_not_empty = "not empty";
|
|
|
|
TestWvCdmClientPropertySet property_set;
|
|
Provision();
|
|
|
|
const CdmSecurityLevel security_level = GetDefaultSecurityLevel();
|
|
FileSystem file_system;
|
|
DeviceFiles handle(&file_system);
|
|
EXPECT_TRUE(handle.Init(security_level));
|
|
std::vector<CdmSecureStopId> psts;
|
|
EXPECT_TRUE(handle.DeleteAllUsageInfoForApp(
|
|
DeviceFiles::GetUsageInfoFileName(""), &psts));
|
|
|
|
for (size_t i = 0; i < ArraySize(kSecureStopSubSamplesIcp); ++i) {
|
|
const SubSampleInfo& data = kSecureStopSubSamplesIcp[i];
|
|
property_set.set_app_id(i % 2 == 0 ? app_id_empty : app_id_not_empty);
|
|
decryptor_->OpenSession(config_.key_system(), &property_set,
|
|
kDefaultCdmIdentifier, nullptr, &session_id_);
|
|
std::string key_id = wvutil::a2bs_hex(
|
|
"000000427073736800000000" // blob size and pssh
|
|
"EDEF8BA979D64ACEA3C827DCD51D21ED00000022" // Widevine system id
|
|
"08011a0d7769646576696e655f74657374220f73" // pssh data
|
|
"747265616d696e675f636c6970");
|
|
|
|
const char ch = '3' + i;
|
|
key_id.append(1, ch);
|
|
|
|
GenerateKeyRequest(key_id, kLicenseTypeStreaming, &property_set);
|
|
|
|
// TODO(rfrias): streaming_clip6 is a streaming license without a pst
|
|
if (ch == '6')
|
|
VerifyKeyRequestResponse(config_.license_server(), config_.client_auth(),
|
|
false);
|
|
else
|
|
VerifyUsageKeyRequestResponse(config_.license_server(),
|
|
config_.client_auth());
|
|
|
|
std::vector<uint8_t> decrypt_buffer(data.encrypt_data.size());
|
|
CdmDecryptionParameters decryption_parameters(
|
|
&data.key_id, &data.encrypt_data.front(), data.encrypt_data.size(),
|
|
&data.iv, data.block_offset, decrypt_buffer.data());
|
|
decryption_parameters.is_encrypted = data.is_encrypted;
|
|
decryption_parameters.is_secure = data.is_secure;
|
|
decryption_parameters.subsample_flags = data.subsample_flags;
|
|
EXPECT_EQ(NO_ERROR, decryptor_->Decrypt(session_id_, data.validate_key_id,
|
|
decryption_parameters));
|
|
|
|
EXPECT_TRUE(std::equal(data.decrypt_data.begin(), data.decrypt_data.end(),
|
|
decrypt_buffer.begin()));
|
|
decryptor_->CloseSession(session_id_);
|
|
}
|
|
|
|
CdmUsageReportList usage_reports;
|
|
EXPECT_EQ(KEY_MESSAGE,
|
|
decryptor_->GetUsageInfo(app_id_empty, kDefaultCdmIdentifier,
|
|
&usage_reports));
|
|
EXPECT_FALSE(usage_reports.empty());
|
|
EXPECT_EQ(KEY_MESSAGE,
|
|
decryptor_->GetUsageInfo(app_id_not_empty, kDefaultCdmIdentifier,
|
|
&usage_reports));
|
|
EXPECT_FALSE(usage_reports.empty());
|
|
|
|
// Read in usage info file
|
|
std::string path;
|
|
EXPECT_TRUE(Properties::GetDeviceFilesBasePath(security_level, &path));
|
|
const std::string secure_stop_not_empty_app_id_file_name =
|
|
path + DeviceFiles::GetUsageInfoFileName(app_id_not_empty);
|
|
|
|
ssize_t file_size =
|
|
file_system.FileSize(secure_stop_not_empty_app_id_file_name);
|
|
EXPECT_LT(4, file_size);
|
|
std::unique_ptr<wvutil::File> file = file_system.Open(
|
|
secure_stop_not_empty_app_id_file_name, FileSystem::kReadOnly);
|
|
ASSERT_NE(file, nullptr);
|
|
std::string file_data;
|
|
file_data.resize(file_size);
|
|
ssize_t bytes = file->Read(&file_data[0], file_data.size());
|
|
EXPECT_EQ(file_size, bytes);
|
|
|
|
video_widevine_client::sdk::HashedFile hash_file;
|
|
|
|
EXPECT_TRUE(hash_file.ParseFromString(file_data));
|
|
size_t pos = file_data.find(hash_file.hash());
|
|
EXPECT_NE(std::string::npos, pos);
|
|
EXPECT_NE(0u, pos);
|
|
|
|
// Corrupt the protobuf key field of the hash of the usage info file
|
|
// with non-empty app id and write it back out
|
|
file_data[pos - 1] = 0xff;
|
|
|
|
file = file_system.Open(secure_stop_not_empty_app_id_file_name,
|
|
FileSystem::kCreate | FileSystem::kTruncate);
|
|
ASSERT_NE(file, nullptr);
|
|
bytes = file->Write(file_data.data(), file_data.size());
|
|
EXPECT_EQ(file_size, bytes);
|
|
|
|
EXPECT_EQ(NO_ERROR, decryptor_->RemoveAllUsageInfo(app_id_not_empty,
|
|
kDefaultCdmIdentifier));
|
|
|
|
EXPECT_EQ(NO_ERROR,
|
|
decryptor_->GetUsageInfo(app_id_not_empty, kDefaultCdmIdentifier,
|
|
&usage_reports));
|
|
EXPECT_TRUE(usage_reports.empty());
|
|
EXPECT_EQ(KEY_MESSAGE,
|
|
decryptor_->GetUsageInfo(app_id_empty, kDefaultCdmIdentifier,
|
|
&usage_reports));
|
|
EXPECT_FALSE(usage_reports.empty());
|
|
|
|
// Read in usage info file
|
|
const std::string secure_stop_empty_app_id_file_name =
|
|
path + DeviceFiles::GetUsageInfoFileName(app_id_empty);
|
|
|
|
file_size = file_system.FileSize(secure_stop_empty_app_id_file_name);
|
|
EXPECT_LT(4, file_size);
|
|
file = file_system.Open(secure_stop_empty_app_id_file_name,
|
|
FileSystem::kReadOnly);
|
|
ASSERT_NE(file, nullptr);
|
|
file_data.resize(file_size);
|
|
bytes = file->Read(&file_data[0], file_data.size());
|
|
EXPECT_EQ(file_size, bytes);
|
|
|
|
EXPECT_TRUE(hash_file.ParseFromString(file_data));
|
|
pos = file_data.find(hash_file.hash());
|
|
EXPECT_NE(std::string::npos, pos);
|
|
EXPECT_NE(0u, pos);
|
|
|
|
// Corrupt the protobuf key field of the hash of the usage info file
|
|
// with empty app id and write it back out
|
|
file_data[pos - 1] = 0xff;
|
|
|
|
file = file_system.Open(secure_stop_empty_app_id_file_name,
|
|
FileSystem::kCreate | FileSystem::kTruncate);
|
|
ASSERT_NE(file, nullptr);
|
|
bytes = file->Write(file_data.data(), file_data.size());
|
|
EXPECT_EQ(file_size, bytes);
|
|
|
|
EXPECT_EQ(NO_ERROR, decryptor_->RemoveAllUsageInfo(app_id_empty,
|
|
kDefaultCdmIdentifier));
|
|
|
|
EXPECT_EQ(NO_ERROR,
|
|
decryptor_->GetUsageInfo(app_id_not_empty, kDefaultCdmIdentifier,
|
|
&usage_reports));
|
|
EXPECT_TRUE(usage_reports.empty());
|
|
EXPECT_EQ(NO_ERROR, decryptor_->GetUsageInfo(
|
|
app_id_empty, kDefaultCdmIdentifier, &usage_reports));
|
|
EXPECT_TRUE(usage_reports.empty());
|
|
}
|
|
|
|
TEST_F(WvCdmRequestLicenseTest, SecureStop_GetSecureStopIdsTest) {
|
|
Unprovision();
|
|
|
|
const std::string app_id_empty = "";
|
|
|
|
TestWvCdmClientPropertySet property_set;
|
|
Provision();
|
|
|
|
CdmSecurityLevel security_level = GetDefaultSecurityLevel();
|
|
FileSystem file_system;
|
|
DeviceFiles handle(&file_system);
|
|
EXPECT_TRUE(handle.Init(security_level));
|
|
std::vector<CdmSecureStopId> psts;
|
|
EXPECT_TRUE(handle.DeleteAllUsageInfoForApp(
|
|
DeviceFiles::GetUsageInfoFileName(""), &psts));
|
|
|
|
std::vector<CdmSecureStopId> retrieved_secure_stop_ids;
|
|
EXPECT_EQ(NO_ERROR,
|
|
decryptor_->GetSecureStopIds(app_id_empty, kDefaultCdmIdentifier,
|
|
&retrieved_secure_stop_ids));
|
|
EXPECT_TRUE(retrieved_secure_stop_ids.empty());
|
|
|
|
// First fetch licenses for the default app
|
|
for (size_t i = 0; i < ArraySize(kUsageLicenseTestVector1); ++i) {
|
|
const SubSampleInfo* data = kUsageLicenseTestVector1[i].sub_sample;
|
|
|
|
property_set.set_app_id(app_id_empty);
|
|
decryptor_->OpenSession(config_.key_system(), &property_set,
|
|
kDefaultCdmIdentifier, nullptr, &session_id_);
|
|
GenerateKeyRequest(kUsageLicenseTestVector1[i].pssh, kLicenseTypeStreaming,
|
|
&property_set);
|
|
|
|
VerifyUsageKeyRequestResponse(config_.license_server(),
|
|
config_.client_auth());
|
|
|
|
std::vector<uint8_t> decrypt_buffer(data->encrypt_data.size());
|
|
CdmDecryptionParameters decryption_parameters(
|
|
&data->key_id, &data->encrypt_data.front(), data->encrypt_data.size(),
|
|
&data->iv, data->block_offset, decrypt_buffer.data());
|
|
decryption_parameters.is_encrypted = data->is_encrypted;
|
|
decryption_parameters.is_secure = data->is_secure;
|
|
decryption_parameters.subsample_flags = data->subsample_flags;
|
|
EXPECT_EQ(NO_ERROR, decryptor_->Decrypt(session_id_, data->validate_key_id,
|
|
decryption_parameters));
|
|
|
|
EXPECT_TRUE(std::equal(data->decrypt_data.begin(), data->decrypt_data.end(),
|
|
decrypt_buffer.begin()));
|
|
decryptor_->CloseSession(session_id_);
|
|
}
|
|
|
|
// Provision the other identifier
|
|
Provision(kExampleIdentifier, kLevelDefault);
|
|
|
|
// Verify that there are usage records for the default identifier but
|
|
// none yet for the non-default one
|
|
std::vector<CdmSecureStopId> expected_provider_session_tokens;
|
|
for (size_t i = 0; i < ArraySize(kUsageLicenseTestVector1); ++i) {
|
|
expected_provider_session_tokens.push_back(
|
|
kUsageLicenseTestVector1[i].provider_session_token);
|
|
}
|
|
|
|
EXPECT_EQ(NO_ERROR,
|
|
decryptor_->GetSecureStopIds(app_id_empty, kDefaultCdmIdentifier,
|
|
&retrieved_secure_stop_ids));
|
|
EXPECT_EQ(ArraySize(kUsageLicenseTestVector1),
|
|
retrieved_secure_stop_ids.size());
|
|
EXPECT_THAT(retrieved_secure_stop_ids,
|
|
UnorderedElementsAreArray(expected_provider_session_tokens));
|
|
|
|
EXPECT_EQ(NO_ERROR, decryptor_->GetSecureStopIds(
|
|
kExampleIdentifier.app_package_name,
|
|
kDefaultCdmIdentifier, &retrieved_secure_stop_ids));
|
|
EXPECT_TRUE(retrieved_secure_stop_ids.empty());
|
|
|
|
// Now fetch licenses for the other identifier
|
|
for (size_t i = 0; i < ArraySize(kUsageLicenseTestVector2); ++i) {
|
|
const SubSampleInfo* data = kUsageLicenseTestVector2[i].sub_sample;
|
|
|
|
property_set.set_app_id(kExampleIdentifier.app_package_name);
|
|
EXPECT_EQ(NO_ERROR, decryptor_->OpenSession(
|
|
config_.key_system(), &property_set,
|
|
kExampleIdentifier, nullptr, &session_id_));
|
|
const std::string init_data_type = "video/mp4";
|
|
CdmAppParameterMap app_parameters;
|
|
GenerateKeyRequest(CdmResponseType(wvcdm::KEY_MESSAGE), init_data_type,
|
|
kUsageLicenseTestVector2[i].pssh, app_parameters,
|
|
kLicenseTypeStreaming, kExampleIdentifier,
|
|
&property_set);
|
|
|
|
VerifyUsageKeyRequestResponse(config_.license_server(),
|
|
config_.client_auth());
|
|
|
|
std::vector<uint8_t> decrypt_buffer(data->encrypt_data.size());
|
|
CdmDecryptionParameters decryption_parameters(
|
|
&data->key_id, &data->encrypt_data.front(), data->encrypt_data.size(),
|
|
&data->iv, data->block_offset, decrypt_buffer.data());
|
|
decryption_parameters.is_encrypted = data->is_encrypted;
|
|
decryption_parameters.is_secure = data->is_secure;
|
|
decryption_parameters.subsample_flags = data->subsample_flags;
|
|
EXPECT_EQ(NO_ERROR, decryptor_->Decrypt(session_id_, data->validate_key_id,
|
|
decryption_parameters));
|
|
|
|
EXPECT_TRUE(std::equal(data->decrypt_data.begin(), data->decrypt_data.end(),
|
|
decrypt_buffer.begin()));
|
|
decryptor_->CloseSession(session_id_);
|
|
}
|
|
|
|
// Verify that there are usage records for both the default and
|
|
// non-default identifier.
|
|
EXPECT_EQ(NO_ERROR, decryptor_->GetSecureStopIds(
|
|
kDefaultCdmIdentifier.app_package_name,
|
|
kDefaultCdmIdentifier, &retrieved_secure_stop_ids));
|
|
EXPECT_EQ(ArraySize(kUsageLicenseTestVector1),
|
|
retrieved_secure_stop_ids.size());
|
|
EXPECT_THAT(retrieved_secure_stop_ids,
|
|
UnorderedElementsAreArray(expected_provider_session_tokens));
|
|
|
|
retrieved_secure_stop_ids.clear();
|
|
expected_provider_session_tokens.clear();
|
|
|
|
for (size_t i = 0; i < ArraySize(kUsageLicenseTestVector2); ++i) {
|
|
expected_provider_session_tokens.push_back(
|
|
kUsageLicenseTestVector2[i].provider_session_token);
|
|
}
|
|
|
|
EXPECT_EQ(NO_ERROR, decryptor_->GetSecureStopIds(
|
|
kExampleIdentifier.app_package_name,
|
|
kExampleIdentifier, &retrieved_secure_stop_ids));
|
|
EXPECT_EQ(ArraySize(kUsageLicenseTestVector2),
|
|
retrieved_secure_stop_ids.size());
|
|
EXPECT_THAT(retrieved_secure_stop_ids,
|
|
UnorderedElementsAreArray(expected_provider_session_tokens));
|
|
|
|
EXPECT_EQ(NO_ERROR,
|
|
decryptor_->RemoveAllUsageInfo(
|
|
kDefaultCdmIdentifier.app_package_name, kDefaultCdmIdentifier));
|
|
EXPECT_EQ(NO_ERROR,
|
|
decryptor_->RemoveAllUsageInfo(kExampleIdentifier.app_package_name,
|
|
kExampleIdentifier));
|
|
|
|
EXPECT_EQ(NO_ERROR, decryptor_->GetSecureStopIds(
|
|
kDefaultCdmIdentifier.app_package_name,
|
|
kDefaultCdmIdentifier, &retrieved_secure_stop_ids));
|
|
EXPECT_TRUE(retrieved_secure_stop_ids.empty());
|
|
EXPECT_EQ(NO_ERROR, decryptor_->GetSecureStopIds(
|
|
kExampleIdentifier.app_package_name,
|
|
kExampleIdentifier, &retrieved_secure_stop_ids));
|
|
EXPECT_TRUE(retrieved_secure_stop_ids.empty());
|
|
}
|
|
|
|
TEST_F(WvCdmRequestLicenseTest, SecureStop_RecoveryTest) {
|
|
Unprovision();
|
|
|
|
const std::string app_id_empty = "";
|
|
|
|
TestWvCdmClientPropertySet property_set;
|
|
Provision();
|
|
|
|
const CdmSecurityLevel security_level = GetDefaultSecurityLevel();
|
|
FileSystem file_system;
|
|
DeviceFiles handle(&file_system);
|
|
EXPECT_TRUE(handle.Init(security_level));
|
|
std::vector<CdmSecureStopId> psts;
|
|
EXPECT_TRUE(handle.DeleteAllUsageInfoForApp(
|
|
DeviceFiles::GetUsageInfoFileName(""), &psts));
|
|
|
|
// Fetch a usage license
|
|
const SubSampleInfo* data = kUsageLicenseTestVector1[0].sub_sample;
|
|
|
|
property_set.set_app_id(app_id_empty);
|
|
decryptor_->OpenSession(config_.key_system(), &property_set,
|
|
kDefaultCdmIdentifier, nullptr, &session_id_);
|
|
GenerateKeyRequest(kUsageLicenseTestVector1[0].pssh, kLicenseTypeStreaming,
|
|
&property_set);
|
|
|
|
VerifyUsageKeyRequestResponse(config_.license_server(),
|
|
config_.client_auth());
|
|
|
|
std::vector<uint8_t> decrypt_buffer(data->encrypt_data.size());
|
|
CdmDecryptionParameters decryption_parameters(
|
|
&data->key_id, &data->encrypt_data.front(), data->encrypt_data.size(),
|
|
&data->iv, data->block_offset, decrypt_buffer.data());
|
|
decryption_parameters.is_encrypted = data->is_encrypted;
|
|
decryption_parameters.is_secure = data->is_secure;
|
|
decryption_parameters.subsample_flags = data->subsample_flags;
|
|
EXPECT_EQ(NO_ERROR, decryptor_->Decrypt(session_id_, data->validate_key_id,
|
|
decryption_parameters));
|
|
|
|
EXPECT_TRUE(std::equal(data->decrypt_data.begin(), data->decrypt_data.end(),
|
|
decrypt_buffer.begin()));
|
|
decryptor_->CloseSession(session_id_);
|
|
|
|
std::string path;
|
|
EXPECT_TRUE(Properties::GetDeviceFilesBasePath(security_level, &path));
|
|
const std::string secure_stop_file_name =
|
|
path + DeviceFiles::GetUsageInfoFileName(app_id_empty);
|
|
|
|
// Read in usage info file
|
|
const ssize_t file_size = file_system.FileSize(secure_stop_file_name);
|
|
EXPECT_LT(4, file_size);
|
|
std::unique_ptr<wvutil::File> file =
|
|
file_system.Open(secure_stop_file_name, FileSystem::kReadOnly);
|
|
ASSERT_NE(file, nullptr);
|
|
std::string file_data;
|
|
file_data.resize(file_size);
|
|
ssize_t bytes = file->Read(&file_data[0], file_data.size());
|
|
EXPECT_EQ(file_size, bytes);
|
|
|
|
// Corrupt the hash of the usage info file and write it back out
|
|
memset(&file_data[0] + bytes - 4, 0, 4);
|
|
file = file_system.Open(secure_stop_file_name,
|
|
FileSystem::kCreate | FileSystem::kTruncate);
|
|
ASSERT_NE(file, nullptr);
|
|
bytes = file->Write(file_data.data(), file_data.size());
|
|
EXPECT_EQ(file_size, bytes);
|
|
|
|
// Fetch a second usage license, this should fail as the usage table is
|
|
// corrupt
|
|
decryptor_->OpenSession(config_.key_system(), &property_set,
|
|
kDefaultCdmIdentifier, nullptr, &session_id_);
|
|
GenerateKeyRequest(kUsageLicenseTestVector1[1].pssh, kLicenseTypeStreaming,
|
|
&property_set);
|
|
|
|
std::string response;
|
|
VerifyKeyRequestResponse(CdmResponseType(wvcdm::STORE_USAGE_INFO_ERROR),
|
|
config_.license_server(), config_.client_auth(),
|
|
true, &response);
|
|
|
|
decryptor_->CloseSession(session_id_);
|
|
|
|
// Fetch the second usage license and verify that it is usable
|
|
decryptor_->OpenSession(config_.key_system(), &property_set,
|
|
kDefaultCdmIdentifier, nullptr, &session_id_);
|
|
GenerateKeyRequest(kUsageLicenseTestVector1[1].pssh, kLicenseTypeStreaming,
|
|
&property_set);
|
|
|
|
VerifyUsageKeyRequestResponse(config_.license_server(),
|
|
config_.client_auth());
|
|
|
|
data = kUsageLicenseTestVector1[1].sub_sample;
|
|
|
|
decrypt_buffer.resize(data->encrypt_data.size());
|
|
decryption_parameters.key_id = &data->key_id;
|
|
decryption_parameters.encrypt_buffer = &data->encrypt_data.front();
|
|
decryption_parameters.encrypt_length = data->encrypt_data.size();
|
|
decryption_parameters.iv = &data->iv;
|
|
decryption_parameters.block_offset = data->block_offset;
|
|
decryption_parameters.decrypt_buffer = decrypt_buffer.data();
|
|
decryption_parameters.decrypt_buffer_length = data->encrypt_data.size();
|
|
decryption_parameters.is_encrypted = data->is_encrypted;
|
|
decryption_parameters.is_secure = data->is_secure;
|
|
decryption_parameters.subsample_flags = data->subsample_flags;
|
|
EXPECT_EQ(NO_ERROR, decryptor_->Decrypt(session_id_, data->validate_key_id,
|
|
decryption_parameters));
|
|
|
|
EXPECT_TRUE(std::equal(data->decrypt_data.begin(), data->decrypt_data.end(),
|
|
decrypt_buffer.begin()));
|
|
decryptor_->CloseSession(session_id_);
|
|
}
|
|
|
|
TEST_F(WvCdmRequestLicenseTest, SecureStop_RemoveTest) {
|
|
Unprovision();
|
|
|
|
const std::string app_id_empty = "";
|
|
|
|
TestWvCdmClientPropertySet property_set;
|
|
Provision();
|
|
|
|
const CdmSecurityLevel security_level = GetDefaultSecurityLevel();
|
|
FileSystem file_system;
|
|
DeviceFiles handle(&file_system);
|
|
EXPECT_TRUE(handle.Init(security_level));
|
|
std::vector<CdmSecureStopId> psts;
|
|
EXPECT_TRUE(handle.DeleteAllUsageInfoForApp(
|
|
DeviceFiles::GetUsageInfoFileName(""), &psts));
|
|
|
|
// First fetch licenses for the default app
|
|
for (size_t i = 0; i < ArraySize(kUsageLicenseTestVector1); ++i) {
|
|
const SubSampleInfo* data = kUsageLicenseTestVector1[i].sub_sample;
|
|
|
|
property_set.set_app_id(app_id_empty);
|
|
decryptor_->OpenSession(config_.key_system(), &property_set,
|
|
kDefaultCdmIdentifier, nullptr, &session_id_);
|
|
GenerateKeyRequest(kUsageLicenseTestVector1[i].pssh, kLicenseTypeStreaming,
|
|
&property_set);
|
|
|
|
VerifyUsageKeyRequestResponse(config_.license_server(),
|
|
config_.client_auth());
|
|
|
|
std::vector<uint8_t> decrypt_buffer(data->encrypt_data.size());
|
|
CdmDecryptionParameters decryption_parameters(
|
|
&data->key_id, &data->encrypt_data.front(), data->encrypt_data.size(),
|
|
&data->iv, data->block_offset, decrypt_buffer.data());
|
|
decryption_parameters.is_encrypted = data->is_encrypted;
|
|
decryption_parameters.is_secure = data->is_secure;
|
|
decryption_parameters.subsample_flags = data->subsample_flags;
|
|
EXPECT_EQ(NO_ERROR, decryptor_->Decrypt(session_id_, data->validate_key_id,
|
|
decryption_parameters));
|
|
|
|
EXPECT_TRUE(std::equal(data->decrypt_data.begin(), data->decrypt_data.end(),
|
|
decrypt_buffer.begin()));
|
|
decryptor_->CloseSession(session_id_);
|
|
}
|
|
|
|
// Provision and fetch licenses for the other identifier
|
|
Provision(kExampleIdentifier, kLevelDefault);
|
|
|
|
for (size_t i = 0; i < ArraySize(kUsageLicenseTestVector2); ++i) {
|
|
const SubSampleInfo* data = kUsageLicenseTestVector2[i].sub_sample;
|
|
|
|
property_set.set_app_id(kExampleIdentifier.app_package_name);
|
|
EXPECT_EQ(NO_ERROR, decryptor_->OpenSession(
|
|
config_.key_system(), &property_set,
|
|
kExampleIdentifier, nullptr, &session_id_));
|
|
const std::string init_data_type = "video/mp4";
|
|
CdmAppParameterMap app_parameters;
|
|
GenerateKeyRequest(CdmResponseType(wvcdm::KEY_MESSAGE), init_data_type,
|
|
kUsageLicenseTestVector2[i].pssh, app_parameters,
|
|
kLicenseTypeStreaming, kExampleIdentifier,
|
|
&property_set);
|
|
|
|
VerifyUsageKeyRequestResponse(config_.license_server(),
|
|
config_.client_auth());
|
|
|
|
std::vector<uint8_t> decrypt_buffer(data->encrypt_data.size());
|
|
CdmDecryptionParameters decryption_parameters(
|
|
&data->key_id, &data->encrypt_data.front(), data->encrypt_data.size(),
|
|
&data->iv, data->block_offset, decrypt_buffer.data());
|
|
decryption_parameters.is_encrypted = data->is_encrypted;
|
|
decryption_parameters.is_secure = data->is_secure;
|
|
decryption_parameters.subsample_flags = data->subsample_flags;
|
|
EXPECT_EQ(NO_ERROR, decryptor_->Decrypt(session_id_, data->validate_key_id,
|
|
decryption_parameters));
|
|
|
|
EXPECT_TRUE(std::equal(data->decrypt_data.begin(), data->decrypt_data.end(),
|
|
decrypt_buffer.begin()));
|
|
decryptor_->CloseSession(session_id_);
|
|
}
|
|
|
|
// Release usage records for both the default and non-default identifier.
|
|
std::vector<CdmSecureStopId> secure_stop_ids;
|
|
EXPECT_EQ(NO_ERROR, decryptor_->GetSecureStopIds(
|
|
kDefaultCdmIdentifier.app_package_name,
|
|
kDefaultCdmIdentifier, &secure_stop_ids));
|
|
EXPECT_EQ(ArraySize(kUsageLicenseTestVector1), secure_stop_ids.size());
|
|
|
|
for (size_t i = 0; i < secure_stop_ids.size(); ++i) {
|
|
EXPECT_EQ(NO_ERROR, decryptor_->RemoveUsageInfo(
|
|
kDefaultCdmIdentifier.app_package_name,
|
|
kDefaultCdmIdentifier, secure_stop_ids[i]));
|
|
}
|
|
|
|
EXPECT_EQ(NO_ERROR, decryptor_->GetSecureStopIds(
|
|
kDefaultCdmIdentifier.app_package_name,
|
|
kDefaultCdmIdentifier, &secure_stop_ids));
|
|
EXPECT_TRUE(secure_stop_ids.empty());
|
|
|
|
EXPECT_EQ(NO_ERROR,
|
|
decryptor_->GetSecureStopIds(kExampleIdentifier.app_package_name,
|
|
kExampleIdentifier, &secure_stop_ids));
|
|
EXPECT_EQ(ArraySize(kUsageLicenseTestVector2), secure_stop_ids.size());
|
|
|
|
for (size_t i = 0; i < secure_stop_ids.size(); ++i) {
|
|
EXPECT_EQ(NO_ERROR, decryptor_->RemoveUsageInfo(
|
|
kExampleIdentifier.app_package_name,
|
|
kExampleIdentifier, secure_stop_ids[i]));
|
|
}
|
|
|
|
EXPECT_EQ(NO_ERROR,
|
|
decryptor_->GetSecureStopIds(kExampleIdentifier.app_package_name,
|
|
kExampleIdentifier, &secure_stop_ids));
|
|
EXPECT_TRUE(secure_stop_ids.empty());
|
|
}
|
|
|
|
// TODO(rfrias): Enable when b/123370099 has been addressed
|
|
TEST_F(WvCdmRequestLicenseTest, VerifyProviderClientToken) {
|
|
Unprovision();
|
|
Provision();
|
|
|
|
// The default offline asset "offline_clip2" does not include a
|
|
// provider session token but "offline_clip5" does, so replace the last
|
|
// char in init data with '5'
|
|
std::string key_id;
|
|
std::string client_auth;
|
|
GetOfflineConfiguration(&key_id, &client_auth);
|
|
key_id[key_id.size() - 1] = '5';
|
|
|
|
// Acquire license
|
|
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
|
|
nullptr, &session_id_);
|
|
GenerateKeyRequest(key_id, kLicenseTypeOffline);
|
|
EXPECT_TRUE(!key_msg_.empty());
|
|
std::string key_msg = key_msg_;
|
|
std::string key_response;
|
|
VerifyKeyRequestResponse(config_.license_server(), client_auth, false,
|
|
&key_response);
|
|
|
|
CdmKeySetId key_set_id = key_set_id_;
|
|
EXPECT_TRUE(!key_set_id_.empty());
|
|
EXPECT_TRUE(!key_response.empty());
|
|
|
|
// Validate signed license request
|
|
SignedMessage signed_message;
|
|
EXPECT_TRUE(signed_message.ParseFromString(key_msg));
|
|
EXPECT_EQ(SignedMessage::LICENSE_REQUEST, signed_message.type());
|
|
EXPECT_TRUE(signed_message.has_signature());
|
|
EXPECT_TRUE(!signed_message.msg().empty());
|
|
|
|
// Verify license request
|
|
video_widevine::LicenseRequest license_request;
|
|
EXPECT_TRUE(license_request.ParseFromString(signed_message.msg()));
|
|
|
|
// Verify that the provider client token is absent in the license request
|
|
EXPECT_FALSE(license_request.client_id().has_provider_client_token());
|
|
EXPECT_TRUE(license_request.client_id().provider_client_token().empty());
|
|
|
|
// Validate signed license response
|
|
EXPECT_TRUE(signed_message.ParseFromString(key_response));
|
|
EXPECT_EQ(SignedMessage::LICENSE, signed_message.type());
|
|
EXPECT_TRUE(signed_message.has_signature());
|
|
EXPECT_TRUE(!signed_message.msg().empty());
|
|
|
|
// Verify license
|
|
video_widevine::License license;
|
|
EXPECT_TRUE(license.ParseFromString(signed_message.msg()));
|
|
|
|
// Verify that the provider client token is present in the license
|
|
EXPECT_TRUE(license.has_provider_client_token());
|
|
EXPECT_TRUE(!license.provider_client_token().empty());
|
|
|
|
decryptor_->CloseSession(session_id_);
|
|
session_id_.clear();
|
|
|
|
// Restore offline license and renew it
|
|
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
|
|
nullptr, &session_id_);
|
|
EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_->RestoreKey(session_id_, key_set_id));
|
|
|
|
std::string license_server;
|
|
GenerateRenewalRequest(kLicenseTypeOffline, &license_server);
|
|
EXPECT_TRUE(!key_msg_.empty());
|
|
key_msg = key_msg_;
|
|
EXPECT_FALSE(license_server.empty());
|
|
VerifyKeyRequestResponse(license_server, client_auth);
|
|
|
|
// Validate signed license renewal request
|
|
EXPECT_TRUE(signed_message.ParseFromString(key_msg));
|
|
EXPECT_EQ(SignedMessage::LICENSE_REQUEST, signed_message.type());
|
|
EXPECT_TRUE(signed_message.has_signature());
|
|
EXPECT_TRUE(!signed_message.msg().empty());
|
|
|
|
// Verify license renewal request
|
|
EXPECT_TRUE(license_request.ParseFromString(signed_message.msg()));
|
|
|
|
// Verify provider client token is present in the license renewal request
|
|
EXPECT_TRUE(license_request.client_id().has_provider_client_token());
|
|
EXPECT_TRUE(!license_request.client_id().provider_client_token().empty());
|
|
|
|
decryptor_->CloseSession(session_id_);
|
|
session_id_.clear();
|
|
|
|
// Restore and release offline license
|
|
key_set_id_.clear();
|
|
GenerateKeyRelease(key_set_id);
|
|
key_set_id_ = key_set_id;
|
|
EXPECT_TRUE(!key_msg_.empty());
|
|
key_msg = key_msg_;
|
|
VerifyKeyRequestResponse(config_.license_server(), client_auth);
|
|
|
|
// Validate signed license release request
|
|
EXPECT_TRUE(signed_message.ParseFromString(key_msg));
|
|
EXPECT_EQ(SignedMessage::LICENSE_REQUEST, signed_message.type());
|
|
EXPECT_TRUE(signed_message.has_signature());
|
|
EXPECT_TRUE(!signed_message.msg().empty());
|
|
|
|
// Verify license release request
|
|
EXPECT_TRUE(license_request.ParseFromString(signed_message.msg()));
|
|
|
|
// Verify provider client token is present in the license release request
|
|
EXPECT_TRUE(license_request.client_id().has_provider_client_token());
|
|
EXPECT_TRUE(!license_request.client_id().provider_client_token().empty());
|
|
}
|
|
|
|
TEST_F(WvCdmRequestLicenseTest, QueryUnmodifiedSessionStatus) {
|
|
// Test that the global value is returned when no properties are modifying it.
|
|
std::string security_level;
|
|
ASSERT_EQ(wvcdm::NO_ERROR, decryptor_->QueryStatus(
|
|
kLevelDefault, wvcdm::QUERY_KEY_SECURITY_LEVEL,
|
|
&security_level));
|
|
EXPECT_EQ(GetSecurityLevel(nullptr), security_level);
|
|
}
|
|
|
|
TEST_F(WvCdmRequestLicenseTest, QueryModifiedSessionStatus) {
|
|
// Test that L3 is returned when properties downgrade security.
|
|
Unprovision();
|
|
Provision(kDefaultCdmIdentifier, kLevel3);
|
|
TestWvCdmClientPropertySet property_set_L3;
|
|
property_set_L3.set_security_level(QUERY_VALUE_SECURITY_LEVEL_L3);
|
|
|
|
EXPECT_EQ(QUERY_VALUE_SECURITY_LEVEL_L3, GetSecurityLevel(&property_set_L3));
|
|
}
|
|
|
|
TEST_F(WvCdmRequestLicenseTest, QueryKeyStatus) {
|
|
Unprovision();
|
|
Provision();
|
|
|
|
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
|
|
nullptr, &session_id_);
|
|
GenerateKeyRequest(binary_key_id(), kLicenseTypeStreaming);
|
|
VerifyKeyRequestResponse(config_.license_server(), config_.client_auth());
|
|
|
|
CdmQueryMap query_info;
|
|
CdmQueryMap::iterator itr;
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->QueryKeyStatus(session_id_, &query_info));
|
|
|
|
itr = query_info.find(wvcdm::QUERY_KEY_LICENSE_TYPE);
|
|
ASSERT_TRUE(itr != query_info.end());
|
|
EXPECT_EQ(wvcdm::QUERY_VALUE_STREAMING, itr->second);
|
|
itr = query_info.find(wvcdm::QUERY_KEY_PLAY_ALLOWED);
|
|
ASSERT_TRUE(itr != query_info.end());
|
|
EXPECT_EQ(wvcdm::QUERY_VALUE_TRUE, itr->second);
|
|
itr = query_info.find(wvcdm::QUERY_KEY_PERSIST_ALLOWED);
|
|
ASSERT_TRUE(itr != query_info.end());
|
|
EXPECT_EQ(wvcdm::QUERY_VALUE_FALSE, itr->second);
|
|
itr = query_info.find(wvcdm::QUERY_KEY_RENEW_ALLOWED);
|
|
ASSERT_TRUE(itr != query_info.end());
|
|
EXPECT_EQ(wvcdm::QUERY_VALUE_TRUE, itr->second);
|
|
|
|
int64_t remaining_time;
|
|
std::istringstream ss;
|
|
itr = query_info.find(wvcdm::QUERY_KEY_LICENSE_DURATION_REMAINING);
|
|
ASSERT_TRUE(itr != query_info.end());
|
|
ss.str(itr->second);
|
|
ss >> remaining_time;
|
|
ASSERT_FALSE(ss.fail());
|
|
EXPECT_LT(0, remaining_time);
|
|
itr = query_info.find(wvcdm::QUERY_KEY_RENTAL_DURATION_REMAINING);
|
|
ASSERT_TRUE(itr != query_info.end());
|
|
ss.clear();
|
|
ss.str(itr->second);
|
|
ss >> remaining_time;
|
|
ASSERT_FALSE(ss.fail());
|
|
EXPECT_LT(0, remaining_time);
|
|
itr = query_info.find(wvcdm::QUERY_KEY_PLAYBACK_DURATION_REMAINING);
|
|
ASSERT_TRUE(itr != query_info.end());
|
|
ss.clear();
|
|
ss.str(itr->second);
|
|
ss >> remaining_time;
|
|
ASSERT_FALSE(ss.fail());
|
|
EXPECT_LT(0, remaining_time);
|
|
|
|
itr = query_info.find(wvcdm::QUERY_KEY_RENEWAL_SERVER_URL);
|
|
ASSERT_TRUE(itr != query_info.end());
|
|
EXPECT_LT(0u, itr->second.size());
|
|
|
|
decryptor_->CloseSession(session_id_);
|
|
}
|
|
|
|
TEST_F(WvCdmRequestLicenseTest, QueryStatus) {
|
|
std::string value;
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->QueryStatus(kLevelDefault,
|
|
wvcdm::QUERY_KEY_SECURITY_LEVEL, &value));
|
|
|
|
EXPECT_EQ(2u, value.size());
|
|
EXPECT_TRUE(wvcdm::QUERY_VALUE_SECURITY_LEVEL_L3 == value ||
|
|
wvcdm::QUERY_VALUE_SECURITY_LEVEL_L1 == value);
|
|
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->QueryStatus(kLevelDefault, wvcdm::QUERY_KEY_DEVICE_ID,
|
|
&value));
|
|
EXPECT_LT(0u, value.size());
|
|
EXPECT_LE(value.size(), 64u) << "device id size: " << value.size();
|
|
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->QueryStatus(kLevelDefault, wvcdm::QUERY_KEY_SYSTEM_ID,
|
|
&value));
|
|
std::istringstream ss(value);
|
|
uint32_t system_id;
|
|
ss >> system_id;
|
|
ASSERT_FALSE(ss.fail());
|
|
EXPECT_TRUE(ss.eof());
|
|
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->QueryStatus(kLevelDefault,
|
|
wvcdm::QUERY_KEY_PROVISIONING_ID, &value));
|
|
EXPECT_GT(value.size(), 0u);
|
|
EXPECT_LE(value.size(), 64u) << "provisioning id size: " << value.size();
|
|
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->QueryStatus(
|
|
kLevelDefault, wvcdm::QUERY_KEY_CURRENT_HDCP_LEVEL, &value));
|
|
EXPECT_TRUE(
|
|
value == QUERY_VALUE_HDCP_NONE || value == QUERY_VALUE_HDCP_V1 ||
|
|
value == QUERY_VALUE_HDCP_V2_0 || value == QUERY_VALUE_HDCP_V2_1 ||
|
|
value == QUERY_VALUE_HDCP_V2_2 || value == QUERY_VALUE_HDCP_V2_3 ||
|
|
value == QUERY_VALUE_HDCP_NO_DIGITAL_OUTPUT);
|
|
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->QueryStatus(kLevelDefault,
|
|
wvcdm::QUERY_KEY_MAX_HDCP_LEVEL, &value));
|
|
EXPECT_TRUE(
|
|
value == QUERY_VALUE_HDCP_NONE || value == QUERY_VALUE_HDCP_V1 ||
|
|
value == QUERY_VALUE_HDCP_V2_0 || value == QUERY_VALUE_HDCP_V2_1 ||
|
|
value == QUERY_VALUE_HDCP_V2_2 || value == QUERY_VALUE_HDCP_V2_3 ||
|
|
value == QUERY_VALUE_HDCP_NO_DIGITAL_OUTPUT);
|
|
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->QueryStatus(kLevelDefault,
|
|
wvcdm::QUERY_KEY_USAGE_SUPPORT, &value));
|
|
EXPECT_TRUE(value == QUERY_VALUE_TRUE || value == QUERY_VALUE_FALSE);
|
|
|
|
EXPECT_EQ(
|
|
wvcdm::NO_ERROR,
|
|
decryptor_->QueryStatus(
|
|
kLevelDefault, wvcdm::QUERY_KEY_NUMBER_OF_OPEN_SESSIONS, &value));
|
|
ss.clear();
|
|
ss.str(value);
|
|
uint32_t open_sessions;
|
|
ss >> open_sessions;
|
|
ASSERT_FALSE(ss.fail());
|
|
EXPECT_TRUE(ss.eof());
|
|
|
|
EXPECT_EQ(
|
|
wvcdm::NO_ERROR,
|
|
decryptor_->QueryStatus(kLevelDefault,
|
|
wvcdm::QUERY_KEY_MAX_NUMBER_OF_SESSIONS, &value));
|
|
ss.clear();
|
|
ss.str(value);
|
|
uint32_t max_sessions;
|
|
ss >> max_sessions;
|
|
ASSERT_FALSE(ss.fail());
|
|
EXPECT_TRUE(ss.eof());
|
|
EXPECT_LE(open_sessions, max_sessions);
|
|
EXPECT_LE(8u, max_sessions);
|
|
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->QueryStatus(
|
|
kLevelDefault, wvcdm::QUERY_KEY_OEMCRYPTO_API_VERSION, &value));
|
|
ss.clear();
|
|
ss.str(value);
|
|
uint32_t api_version;
|
|
ss >> api_version;
|
|
ASSERT_FALSE(ss.fail());
|
|
EXPECT_TRUE(ss.eof());
|
|
EXPECT_LE(10u, api_version);
|
|
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->QueryStatus(
|
|
kLevelDefault, wvcdm::QUERY_KEY_RESOURCE_RATING_TIER, &value));
|
|
ss.clear();
|
|
ss.str(value);
|
|
uint32_t resource_rating_tier;
|
|
ss >> resource_rating_tier;
|
|
ASSERT_FALSE(ss.fail());
|
|
EXPECT_TRUE(ss.eof());
|
|
|
|
if (api_version >= OEM_CRYPTO_API_VERSION_SUPPORTS_RESOURCE_RATING_TIER) {
|
|
EXPECT_LE(RESOURCE_RATING_TIER_MIN, resource_rating_tier);
|
|
EXPECT_GE(RESOURCE_RATING_TIER_MAX, resource_rating_tier);
|
|
} else {
|
|
EXPECT_TRUE(resource_rating_tier < RESOURCE_RATING_TIER_MIN ||
|
|
resource_rating_tier > RESOURCE_RATING_TIER_MAX)
|
|
<< "resource rating tier: " << resource_rating_tier;
|
|
}
|
|
|
|
EXPECT_EQ(
|
|
wvcdm::NO_ERROR,
|
|
decryptor_->QueryStatus(
|
|
kLevelDefault, wvcdm::QUERY_KEY_OEMCRYPTO_BUILD_INFORMATION, &value));
|
|
EXPECT_TRUE(!value.empty());
|
|
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->QueryStatus(
|
|
kLevelDefault, wvcdm::QUERY_KEY_CURRENT_SRM_VERSION, &value));
|
|
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->QueryStatus(
|
|
kLevelDefault, wvcdm::QUERY_KEY_PROVISIONING_MODEL, &value));
|
|
// These are the only valid values for Android devices.
|
|
EXPECT_TRUE(value == wvcdm::QUERY_VALUE_KEYBOX ||
|
|
value == wvcdm::QUERY_VALUE_OEM_CERTIFICATE ||
|
|
value == wvcdm::QUERY_VALUE_BOOT_CERTIFICATE_CHAIN);
|
|
|
|
const bool is_prov4 = (value == wvcdm::QUERY_VALUE_BOOT_CERTIFICATE_CHAIN);
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->QueryStatus(
|
|
kLevelDefault, wvcdm::QUERY_KEY_DEBUG_BOOT_CERTIFICATE_CHAIN,
|
|
&value));
|
|
if (is_prov4) {
|
|
// Expect that the BCC is returned for Prov 4; but do not validate the
|
|
// actual value.
|
|
EXPECT_FALSE(value.empty()) << "BCC is empty";
|
|
EXPECT_NE(value, wvcdm::QUERY_VALUE_NONE) << "BCC is none";
|
|
} else {
|
|
EXPECT_EQ(value, wvcdm::QUERY_VALUE_NONE);
|
|
}
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->QueryStatus(
|
|
kLevelDefault, wvcdm::QUERY_KEY_DEVICE_INFORMATION, &value));
|
|
if (is_prov4) {
|
|
// Expect that the device info is returned for Prov 4; but do not validate
|
|
// the actual value.
|
|
EXPECT_FALSE(value.empty()) << "Device info is empty";
|
|
EXPECT_NE(value, wvcdm::QUERY_VALUE_NONE) << "Device info is none";
|
|
} else {
|
|
EXPECT_EQ(value, wvcdm::QUERY_VALUE_NONE);
|
|
}
|
|
|
|
EXPECT_EQ(
|
|
wvcdm::NO_ERROR,
|
|
decryptor_->QueryStatus(
|
|
kLevelDefault, wvcdm::QUERY_KEY_MAX_USAGE_TABLE_ENTRIES, &value));
|
|
|
|
ss.clear();
|
|
ss.str(value);
|
|
uint32_t max_usage_table_entries;
|
|
ss >> max_usage_table_entries;
|
|
ASSERT_FALSE(ss.fail());
|
|
EXPECT_TRUE(ss.eof());
|
|
EXPECT_LE(200u, max_usage_table_entries);
|
|
|
|
EXPECT_EQ(
|
|
wvcdm::NO_ERROR,
|
|
decryptor_->QueryStatus(
|
|
kLevelDefault, wvcdm::QUERY_KEY_OEMCRYPTO_API_MINOR_VERSION, &value));
|
|
|
|
ss.clear();
|
|
ss.str(value);
|
|
uint32_t api_minor_version;
|
|
ss >> api_minor_version;
|
|
ASSERT_FALSE(ss.fail());
|
|
EXPECT_TRUE(ss.eof());
|
|
EXPECT_LE(0u, api_minor_version);
|
|
|
|
EXPECT_EQ(
|
|
wvcdm::NO_ERROR,
|
|
decryptor_->QueryStatus(
|
|
kLevelDefault, wvcdm::QUERY_KEY_ANALOG_OUTPUT_CAPABILITIES, &value));
|
|
EXPECT_THAT(kAnalogOutputCapabilities, Contains(value));
|
|
|
|
EXPECT_EQ(
|
|
wvcdm::NO_ERROR,
|
|
decryptor_->QueryStatus(
|
|
kLevelDefault, wvcdm::QUERY_KEY_CAN_DISABLE_ANALOG_OUTPUT, &value));
|
|
EXPECT_THAT(kCanDisableAnalogOutput, Contains(value));
|
|
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->QueryStatus(
|
|
kLevelDefault, wvcdm::QUERY_KEY_WATERMARKING_SUPPORT, &value));
|
|
EXPECT_TRUE(value == wvcdm::QUERY_VALUE_NOT_SUPPORTED ||
|
|
value == wvcdm::QUERY_VALUE_CONFIGURABLE ||
|
|
value == wvcdm::QUERY_VALUE_ALWAYS_ON)
|
|
<< "Watermarking support";
|
|
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->QueryStatus(kLevelDefault,
|
|
wvcdm::QUERY_KEY_PRODUCTION_READY, &value));
|
|
EXPECT_TRUE(value == wvcdm::QUERY_VALUE_TRUE ||
|
|
value == wvcdm::QUERY_VALUE_FALSE ||
|
|
value == wvcdm::QUERY_VALUE_UNKNOWN)
|
|
<< "Production ready";
|
|
}
|
|
|
|
TEST_F(WvCdmRequestLicenseTest, QueryStatusL3) {
|
|
std::string value;
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->QueryStatus(kLevel3, wvcdm::QUERY_KEY_SECURITY_LEVEL,
|
|
&value));
|
|
EXPECT_EQ(wvcdm::QUERY_VALUE_SECURITY_LEVEL_L3, value);
|
|
|
|
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_->QueryStatus(
|
|
kLevel3, wvcdm::QUERY_KEY_DEVICE_ID, &value));
|
|
EXPECT_LT(0u, value.size());
|
|
EXPECT_LE(value.size(), 64u) << "device id size: " << value.size();
|
|
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->QueryStatus(kLevelDefault, wvcdm::QUERY_KEY_SYSTEM_ID,
|
|
&value));
|
|
std::istringstream ss(value);
|
|
uint32_t default_system_id;
|
|
ss >> default_system_id;
|
|
ASSERT_FALSE(ss.fail());
|
|
EXPECT_TRUE(ss.eof());
|
|
|
|
std::string default_security_level;
|
|
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_->QueryStatus(
|
|
kLevelDefault, wvcdm::QUERY_KEY_SECURITY_LEVEL,
|
|
&default_security_level));
|
|
|
|
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_->QueryStatus(
|
|
kLevel3, wvcdm::QUERY_KEY_SYSTEM_ID, &value));
|
|
ss.clear();
|
|
ss.str(value);
|
|
uint32_t system_id;
|
|
ss >> system_id;
|
|
ASSERT_FALSE(ss.fail());
|
|
EXPECT_TRUE(ss.eof());
|
|
if (default_security_level != wvcdm::QUERY_VALUE_SECURITY_LEVEL_L3) {
|
|
EXPECT_NE(default_system_id, system_id);
|
|
}
|
|
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->QueryStatus(kLevel3, wvcdm::QUERY_KEY_PROVISIONING_ID,
|
|
&value));
|
|
EXPECT_GT(value.size(), 0u);
|
|
EXPECT_LE(value.size(), 64u) << "provisioning id size: " << value.size();
|
|
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->QueryStatus(
|
|
kLevel3, wvcdm::QUERY_KEY_CURRENT_HDCP_LEVEL, &value));
|
|
EXPECT_TRUE(
|
|
value == QUERY_VALUE_HDCP_NONE || value == QUERY_VALUE_HDCP_V1 ||
|
|
value == QUERY_VALUE_HDCP_V2_0 || value == QUERY_VALUE_HDCP_V2_1 ||
|
|
value == QUERY_VALUE_HDCP_V2_2 || value == QUERY_VALUE_HDCP_V2_3 ||
|
|
value == QUERY_VALUE_HDCP_NO_DIGITAL_OUTPUT);
|
|
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->QueryStatus(kLevel3, wvcdm::QUERY_KEY_MAX_HDCP_LEVEL,
|
|
&value));
|
|
EXPECT_TRUE(
|
|
value == QUERY_VALUE_HDCP_NONE || value == QUERY_VALUE_HDCP_V1 ||
|
|
value == QUERY_VALUE_HDCP_V2_0 || value == QUERY_VALUE_HDCP_V2_1 ||
|
|
value == QUERY_VALUE_HDCP_V2_2 || value == QUERY_VALUE_HDCP_V2_3 ||
|
|
value == QUERY_VALUE_HDCP_NO_DIGITAL_OUTPUT);
|
|
|
|
EXPECT_EQ(
|
|
wvcdm::NO_ERROR,
|
|
decryptor_->QueryStatus(kLevel3, wvcdm::QUERY_KEY_USAGE_SUPPORT, &value));
|
|
EXPECT_TRUE(value == QUERY_VALUE_TRUE || value == QUERY_VALUE_FALSE);
|
|
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->QueryStatus(
|
|
kLevel3, wvcdm::QUERY_KEY_NUMBER_OF_OPEN_SESSIONS, &value));
|
|
ss.clear();
|
|
ss.str(value);
|
|
uint32_t open_sessions;
|
|
ss >> open_sessions;
|
|
ASSERT_FALSE(ss.fail());
|
|
EXPECT_TRUE(ss.eof());
|
|
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->QueryStatus(
|
|
kLevel3, wvcdm::QUERY_KEY_MAX_NUMBER_OF_SESSIONS, &value));
|
|
ss.clear();
|
|
ss.str(value);
|
|
uint32_t max_sessions;
|
|
ss >> max_sessions;
|
|
ASSERT_FALSE(ss.fail());
|
|
EXPECT_TRUE(ss.eof());
|
|
EXPECT_LE(open_sessions, max_sessions);
|
|
EXPECT_LE(8u, max_sessions);
|
|
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->QueryStatus(
|
|
kLevel3, wvcdm::QUERY_KEY_OEMCRYPTO_API_VERSION, &value));
|
|
ss.clear();
|
|
ss.str(value);
|
|
uint32_t api_version;
|
|
ss >> api_version;
|
|
ASSERT_FALSE(ss.fail());
|
|
EXPECT_TRUE(ss.eof());
|
|
EXPECT_LE(10u, api_version);
|
|
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->QueryStatus(
|
|
kLevel3, wvcdm::QUERY_KEY_PROVISIONING_MODEL, &value));
|
|
// These are the only valid values for Android devices.
|
|
EXPECT_TRUE(value == wvcdm::QUERY_VALUE_KEYBOX ||
|
|
value == wvcdm::QUERY_VALUE_OEM_CERTIFICATE);
|
|
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->QueryStatus(
|
|
kLevel3, wvcdm::QUERY_KEY_OEMCRYPTO_API_MINOR_VERSION, &value));
|
|
ss.clear();
|
|
ss.str(value);
|
|
uint32_t api_minor_version;
|
|
ss >> api_minor_version;
|
|
ASSERT_FALSE(ss.fail());
|
|
EXPECT_TRUE(ss.eof());
|
|
EXPECT_LE(0u, api_minor_version);
|
|
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->QueryStatus(
|
|
kLevel3, wvcdm::QUERY_KEY_MAX_USAGE_TABLE_ENTRIES, &value));
|
|
ss.clear();
|
|
ss.str(value);
|
|
uint32_t max_usage_table_entries;
|
|
ss >> max_usage_table_entries;
|
|
ASSERT_FALSE(ss.fail());
|
|
EXPECT_TRUE(ss.eof());
|
|
EXPECT_LE(200u, max_usage_table_entries);
|
|
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->QueryStatus(
|
|
kLevel3, wvcdm::QUERY_KEY_ANALOG_OUTPUT_CAPABILITIES, &value));
|
|
EXPECT_THAT(kAnalogOutputCapabilities, Contains(value));
|
|
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->QueryStatus(
|
|
kLevel3, wvcdm::QUERY_KEY_CAN_DISABLE_ANALOG_OUTPUT, &value));
|
|
EXPECT_THAT(kCanDisableAnalogOutput, Contains(value));
|
|
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->QueryStatus(
|
|
kLevel3, wvcdm::QUERY_KEY_WATERMARKING_SUPPORT, &value));
|
|
EXPECT_TRUE(value == wvcdm::QUERY_VALUE_NOT_SUPPORTED ||
|
|
value == wvcdm::QUERY_VALUE_CONFIGURABLE ||
|
|
value == wvcdm::QUERY_VALUE_ALWAYS_ON)
|
|
<< "Watermarking support";
|
|
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->QueryStatus(kLevel3, wvcdm::QUERY_KEY_PRODUCTION_READY,
|
|
&value));
|
|
EXPECT_TRUE(value == wvcdm::QUERY_VALUE_TRUE ||
|
|
value == wvcdm::QUERY_VALUE_FALSE ||
|
|
value == wvcdm::QUERY_VALUE_UNKNOWN)
|
|
<< "Production ready";
|
|
}
|
|
|
|
TEST_F(WvCdmRequestLicenseTest, QueryOemCryptoSessionId) {
|
|
Unprovision();
|
|
Provision();
|
|
|
|
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
|
|
nullptr, &session_id_);
|
|
GenerateKeyRequest(binary_key_id(), kLicenseTypeStreaming);
|
|
VerifyKeyRequestResponse(config_.license_server(), config_.client_auth());
|
|
|
|
CdmQueryMap query_info;
|
|
CdmQueryMap::iterator itr;
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->QueryOemCryptoSessionId(session_id_, &query_info));
|
|
|
|
uint32_t oem_crypto_session_id;
|
|
itr = query_info.find(wvcdm::QUERY_KEY_OEMCRYPTO_SESSION_ID);
|
|
ASSERT_TRUE(itr != query_info.end());
|
|
std::istringstream ss;
|
|
ss.str(itr->second);
|
|
ss >> oem_crypto_session_id;
|
|
ASSERT_FALSE(ss.fail());
|
|
|
|
decryptor_->CloseSession(session_id_);
|
|
}
|
|
|
|
TEST_F(WvCdmRequestLicenseTest, IsSecurityLevelSupported) {
|
|
// Level 1 may either be or not be supported. Invoking the method without
|
|
// imposing any expecations to make sure it completes.
|
|
WvContentDecryptionModule::IsSecurityLevelSupported(wvcdm::kSecurityLevelL1);
|
|
EXPECT_FALSE(WvContentDecryptionModule::IsSecurityLevelSupported(
|
|
wvcdm::kSecurityLevelL2));
|
|
EXPECT_TRUE(WvContentDecryptionModule::IsSecurityLevelSupported(
|
|
wvcdm::kSecurityLevelL3));
|
|
EXPECT_FALSE(WvContentDecryptionModule::IsSecurityLevelSupported(
|
|
wvcdm::kSecurityLevelUnknown));
|
|
EXPECT_FALSE(WvContentDecryptionModule::IsSecurityLevelSupported(
|
|
wvcdm::kSecurityLevelUninitialized));
|
|
}
|
|
|
|
TEST_F(WvCdmRequestLicenseTest, DISABLED_OfflineLicenseDecryptionTest) {
|
|
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
|
|
nullptr, &session_id_);
|
|
GenerateKeyRequest(binary_key_id(), kLicenseTypeOffline);
|
|
VerifyKeyRequestResponse(config_.license_server(), config_.client_auth());
|
|
|
|
/*
|
|
// key 1, encrypted, 256b
|
|
DecryptionData data;
|
|
data.is_encrypted = true;
|
|
data.is_secure = false;
|
|
data.key_id = wvutil::a2bs_hex("30313233343536373839414243444546");
|
|
data.encrypt_data = wvutil::a2b_hex(
|
|
"b6d7d2430aa82b1cb8bd32f02e1f3b2a8d84f9eddf935ced5a6a98022cbb4561"
|
|
"8346a749fdb336858a64d7169fd0aa898a32891d14c24bed17fdc17fd62b8771"
|
|
"a8e22e9f093fa0f2aacd293d471b8e886d5ed8d0998ab2fde2d908580ff88c93"
|
|
"c0f0bbc14867267b3a3955bb6e7d05fca734a3aec3463d786d555cad83536ebe"
|
|
"4496d934d40df2aba5aea98c1145a2890879568ae31bb8a85d74714a4ad75785"
|
|
"7488523e697f5fd370eac746d56990a81cc76a178e3d6d65743520cdbc669412"
|
|
"9e73b86214256c67430cf78662346cab3e2bdd6f095dddf75b7fb3868c5ff5ff"
|
|
"3e1bbf08d456532ffa9df6e21a8bb2664c2d2a6d47ee78f9a6d53b2f2c8c087c");
|
|
data.iv = wvutil::a2b_hex("86856b9409743ca107b043e82068c7b6");
|
|
data.block_offset = 0;
|
|
data.decrypt_data = wvutil::a2b_hex(
|
|
"cc4a7fed8c5ac6e316e45317805c43e6d62a383ad738219c65e7a259dc12b46a"
|
|
"d50a3f8ce2facec8eeadff9cfa6b649212b88602b41f6d4c510c05af07fd523a"
|
|
"e7032634d9f8db5dd652d35f776376c5fc56e7031ed7cb28b72427fd4b367b6d"
|
|
"8c4eb6e46ed1249de5d24a61aeb08ebd60984c10581042ca8b0ef6bc44ec34a0"
|
|
"d4a77d68125c9bb1ace6f650e8716540f5b20d6482f7cfdf1b57a9ee9802160c"
|
|
"a632ce42934347410abc61bb78fba11b093498572de38bca96101ecece455e3b"
|
|
"5fef6805c44a2609cf97ce0dac7f15695c8058c590eda517f845108b90dfb29c"
|
|
"e73f3656000399f2fd196bc6fc225f3a7b8f578237751fd485ff070b5289e5cf");
|
|
|
|
std::vector<uint8_t> decrypt_buffer;
|
|
size_t encrypt_length = data.encrypt_data.size();
|
|
decrypt_buffer.resize(encrypt_length);
|
|
|
|
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_->Decrypt(session_id_,
|
|
data.is_encrypted,
|
|
data.is_secure,
|
|
data.key_id,
|
|
&data.encrypt_data.front(),
|
|
encrypt_length,
|
|
data.iv,
|
|
data.block_offset,
|
|
&decrypt_buffer.front(),
|
|
0));
|
|
|
|
EXPECT_TRUE(std::equal(data.decrypt_data.begin(), data.decrypt_data.end(),
|
|
decrypt_buffer.begin()));
|
|
*/
|
|
decryptor_->CloseSession(session_id_);
|
|
}
|
|
|
|
TEST_F(WvCdmRequestLicenseTest, DISABLED_RestoreOfflineLicenseDecryptionTest) {
|
|
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
|
|
nullptr, &session_id_);
|
|
GenerateKeyRequest(binary_key_id(), kLicenseTypeOffline);
|
|
VerifyKeyRequestResponse(config_.license_server(), config_.client_auth());
|
|
CdmKeySetId key_set_id = key_set_id_;
|
|
EXPECT_FALSE(key_set_id_.empty());
|
|
decryptor_->CloseSession(session_id_);
|
|
|
|
session_id_.clear();
|
|
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
|
|
nullptr, &session_id_);
|
|
EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_->RestoreKey(session_id_, key_set_id));
|
|
/*
|
|
// key 1, encrypted, 256b
|
|
DecryptionData data;
|
|
data.is_encrypted = true;
|
|
data.is_secure = false;
|
|
data.key_id = wvutil::a2bs_hex("30313233343536373839414243444546");
|
|
data.encrypt_data = wvutil::a2b_hex(
|
|
"b6d7d2430aa82b1cb8bd32f02e1f3b2a8d84f9eddf935ced5a6a98022cbb4561"
|
|
"8346a749fdb336858a64d7169fd0aa898a32891d14c24bed17fdc17fd62b8771"
|
|
"a8e22e9f093fa0f2aacd293d471b8e886d5ed8d0998ab2fde2d908580ff88c93"
|
|
"c0f0bbc14867267b3a3955bb6e7d05fca734a3aec3463d786d555cad83536ebe"
|
|
"4496d934d40df2aba5aea98c1145a2890879568ae31bb8a85d74714a4ad75785"
|
|
"7488523e697f5fd370eac746d56990a81cc76a178e3d6d65743520cdbc669412"
|
|
"9e73b86214256c67430cf78662346cab3e2bdd6f095dddf75b7fb3868c5ff5ff"
|
|
"3e1bbf08d456532ffa9df6e21a8bb2664c2d2a6d47ee78f9a6d53b2f2c8c087c");
|
|
data.iv = wvutil::a2b_hex("86856b9409743ca107b043e82068c7b6");
|
|
data.block_offset = 0;
|
|
data.decrypt_data = wvutil::a2b_hex(
|
|
"cc4a7fed8c5ac6e316e45317805c43e6d62a383ad738219c65e7a259dc12b46a"
|
|
"d50a3f8ce2facec8eeadff9cfa6b649212b88602b41f6d4c510c05af07fd523a"
|
|
"e7032634d9f8db5dd652d35f776376c5fc56e7031ed7cb28b72427fd4b367b6d"
|
|
"8c4eb6e46ed1249de5d24a61aeb08ebd60984c10581042ca8b0ef6bc44ec34a0"
|
|
"d4a77d68125c9bb1ace6f650e8716540f5b20d6482f7cfdf1b57a9ee9802160c"
|
|
"a632ce42934347410abc61bb78fba11b093498572de38bca96101ecece455e3b"
|
|
"5fef6805c44a2609cf97ce0dac7f15695c8058c590eda517f845108b90dfb29c"
|
|
"e73f3656000399f2fd196bc6fc225f3a7b8f578237751fd485ff070b5289e5cf");
|
|
|
|
std::vector<uint8_t> decrypt_buffer;
|
|
size_t encrypt_length = data.encrypt_data.size();
|
|
decrypt_buffer.resize(encrypt_length);
|
|
|
|
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_->Decrypt(session_id_,
|
|
data.is_encrypted,
|
|
data.is_secure,
|
|
data.key_id,
|
|
&data.encrypt_data.front(),
|
|
encrypt_length,
|
|
data.iv,
|
|
data.block_offset,
|
|
&decrypt_buffer.front(),
|
|
0));
|
|
|
|
EXPECT_TRUE(std::equal(data.decrypt_data.begin(), data.decrypt_data.end(),
|
|
decrypt_buffer.begin()));
|
|
*/
|
|
decryptor_->CloseSession(session_id_);
|
|
}
|
|
|
|
// TODO(rfrias): pending L1 OEMCrypto due to key block handling
|
|
/*
|
|
TEST_F(WvCdmRequestLicenseTest, KeyControlBlockDecryptionTest) {
|
|
decryptor_->OpenSession(config_.key_system(), &session_id_);
|
|
GenerateKeyRequest(binary_key_id(), kLicenseTypeStreaming);
|
|
VerifyKeyRequestResponse(config_.license_server(), config_.client_auth());
|
|
|
|
DecryptionData data;
|
|
|
|
// block 4, key 2, encrypted
|
|
data.is_encrypted = true;
|
|
data.is_secure = false;
|
|
data.key_id = wvutil::a2bs_hex("0915007CAA9B5931B76A3A85F046523E");
|
|
data.encrypt_data = wvutil::a2b_hex(
|
|
"6758ac1c6ccf5d08479e3bfc62bbc0fd154aff4415aa7ed53d89e3983248d117"
|
|
"ab5137ae7cedd9f9d7321d4cf35a7013237afbcc2d893d1d928efa94e9f7e2ed"
|
|
"1855463cf75ff07ecc0246b90d0734f42d98aeea6a0a6d2618a8339bd0aca368"
|
|
"4fb4a4670c0385e5bd5de9e2d8b9226851b8f8955adfbab968793b46fd152f5e"
|
|
"e608467bb2695836f8f76c32731f5e208176d05e4b07020d58f6282c477f3840"
|
|
"b8079c02e8bd1d03191d190cc505ddfbb2e9bacc794534c91fe409d62f5389b9"
|
|
"35ed66134bd30f09f8da9dbfe6b8cf53d13cae34dae6e89109216e3a02233d5c"
|
|
"2f66aef74313aae4a99b654b485b5cc207b2dc8d44a8b99a4dc196a9820eccef");
|
|
data.iv = wvutil::a2b_hex("c8f2d133ec357fe727cd233b3bfa755f");
|
|
data.block_offset = 0;
|
|
data.decrypt_data = wvutil::a2b_hex(
|
|
"34bab89185f1be990dfc454410c7c9093d008bc783908838b02a65b26db28759"
|
|
"dca9dc5f117b3c8c3898358722d1b4c490e5a5d168ba0f9f8a3d4371b8fd1057"
|
|
"2d6dd65f3f9d1850de8d76dc71bd6dc6c23da4e1223fcc3e47162033a6f82890"
|
|
"e2bd6e9d6ddbe453830afc89064ed18078c786f8f746fcbafd88e83e7160cce5"
|
|
"62fa7a7d699ef8421bda020d242ae4f61a786213b707c3b17b83d77510f9a07e"
|
|
"d9d7e47d8f8fa2aff86eb26d61ddf384a27513e3facf6b1f5fe6c0d063b8856c"
|
|
"c486d930393ea79ba73ba293eda39059e2ce9ee7bd5d31ab11f35e55dc35dfe0"
|
|
"ea5e2ec684014852add6e29ce7d88a1595641ae4c0dd10155526b5a87560ec9d");
|
|
|
|
std::vector<uint8_t> decrypt_buffer;
|
|
size_t encrypt_length = data[i].encrypt_data.size();
|
|
decrypt_buffer.resize(encrypt_length);
|
|
|
|
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_->Decrypt(session_id_,
|
|
data.is_encrypted,
|
|
data.is_secure,
|
|
data.key_id,
|
|
&data.encrypt_data.front(),
|
|
encrypt_length,
|
|
data.iv,
|
|
data.block_offset,
|
|
&decrypt_buffer.front()));
|
|
|
|
EXPECT_TRUE(std::equal(data.decrypt_data.begin(),
|
|
data.decrypt_data.end(),
|
|
decrypt_buffer.begin()));
|
|
}
|
|
decryptor_->CloseSession(session_id_);
|
|
}
|
|
*/
|
|
|
|
class WvCdmSessionSharingTest
|
|
: public WvCdmRequestLicenseTest,
|
|
public ::testing::WithParamInterface<const SessionSharingSubSampleInfo*> {
|
|
};
|
|
|
|
TEST_P(WvCdmSessionSharingTest, SessionSharingTest) {
|
|
const SessionSharingSubSampleInfo* session_sharing_info = GetParam();
|
|
|
|
CdmIdentifier cdm_identifier = session_sharing_info->cdm_identifier;
|
|
if (!cdm_identifier.IsEquivalentToDefault()) {
|
|
Provision(session_sharing_info->cdm_identifier, kLevelDefault);
|
|
}
|
|
|
|
TestWvCdmClientPropertySet property_set;
|
|
property_set.set_session_sharing_mode(
|
|
session_sharing_info->session_sharing_enabled);
|
|
|
|
ASSERT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->OpenSession(config_.key_system(), &property_set,
|
|
cdm_identifier, nullptr, &session_id_));
|
|
CdmSessionId gp_session_id_1 = session_id_;
|
|
GenerateKeyRequest(binary_key_id(), kLicenseTypeStreaming, cdm_identifier);
|
|
VerifyKeyRequestResponse(config_.license_server(), config_.client_auth());
|
|
|
|
// TODO(rfrias): Move content information to ConfigTestEnv
|
|
std::string gp_client_auth2 =
|
|
"?source=YOUTUBE&video_id=z3S_NhwueaM&oauth=ya.gtsqawidevine";
|
|
std::string gp_key_id2 = wvutil::a2bs_hex(
|
|
"000000347073736800000000" // blob size and pssh
|
|
"edef8ba979d64acea3c827dcd51d21ed00000014" // Widevine system id
|
|
"08011210bdf1cb4fffc6506b8b7945b0bd2917fb"); // pssh data
|
|
|
|
ASSERT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->OpenSession(config_.key_system(), &property_set,
|
|
cdm_identifier, nullptr, &session_id_));
|
|
CdmSessionId gp_session_id_2 = session_id_;
|
|
GenerateKeyRequest(gp_key_id2, kLicenseTypeStreaming, cdm_identifier);
|
|
VerifyKeyRequestResponse(config_.license_server(), gp_client_auth2);
|
|
|
|
const SubSampleInfo* data = session_sharing_info->sub_sample;
|
|
std::vector<uint8_t> decrypt_buffer(data->encrypt_data.size());
|
|
CdmDecryptionParameters decryption_parameters(
|
|
&data->key_id, &data->encrypt_data.front(), data->encrypt_data.size(),
|
|
&data->iv, data->block_offset, &decrypt_buffer[0]);
|
|
decryption_parameters.is_encrypted = data->is_encrypted;
|
|
decryption_parameters.is_secure = data->is_secure;
|
|
decryption_parameters.subsample_flags = data->subsample_flags;
|
|
|
|
if (session_sharing_info->session_sharing_enabled || !data->is_encrypted) {
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->Decrypt(gp_session_id_2, data->validate_key_id,
|
|
decryption_parameters))
|
|
<< "session_sharing_info->session_sharing_enabled "
|
|
<< session_sharing_info->session_sharing_enabled << std::endl
|
|
<< "data->is_encrypted " << data->is_encrypted << std::endl;
|
|
EXPECT_TRUE(std::equal(data->decrypt_data.begin(), data->decrypt_data.end(),
|
|
decrypt_buffer.begin()));
|
|
} else {
|
|
EXPECT_EQ(NEED_KEY,
|
|
decryptor_->Decrypt(gp_session_id_2, data->validate_key_id,
|
|
decryption_parameters));
|
|
}
|
|
|
|
if (!cdm_identifier.IsEquivalentToDefault()) {
|
|
// Unprovision both security level certs.
|
|
decryptor_->Unprovision(kSecurityLevelL1, cdm_identifier);
|
|
decryptor_->Unprovision(kSecurityLevelL3, cdm_identifier);
|
|
}
|
|
|
|
decryptor_->CloseSession(gp_session_id_1);
|
|
decryptor_->CloseSession(gp_session_id_2);
|
|
}
|
|
|
|
INSTANTIATE_TEST_CASE_P(Cdm, WvCdmSessionSharingTest,
|
|
::testing::Range(&kSessionSharingSubSamples[0],
|
|
&kSessionSharingSubSamples[7]));
|
|
|
|
TEST_F(WvCdmRequestLicenseTest, SessionSharingTest) {
|
|
TestWvCdmClientPropertySet property_set;
|
|
property_set.set_session_sharing_mode(true);
|
|
|
|
// TODO(rfrias): Move content information to ConfigTestEnv
|
|
const std::string init_data1 = wvutil::a2bs_hex(
|
|
"000000347073736800000000" // blob size and pssh
|
|
"EDEF8BA979D64ACEA3C827DCD51D21ED00000014" // Widevine system id
|
|
"0801121030313233343536373839616263646566"); // pssh data
|
|
|
|
decryptor_->OpenSession(config_.key_system(), &property_set,
|
|
kDefaultCdmIdentifier, nullptr, &session_id_);
|
|
CdmSessionId session_id1 = session_id_;
|
|
GenerateKeyRequest(init_data1, kLicenseTypeStreaming);
|
|
VerifyKeyRequestResponse(config_.license_server(), config_.client_auth());
|
|
|
|
// TODO(rfrias): Move content information to ConfigTestEnv
|
|
std::string gp_client_auth2 =
|
|
"?source=YOUTUBE&video_id=z3S_NhwueaM&oauth=ya.gtsqawidevine";
|
|
std::string init_data2 = wvutil::a2bs_hex(
|
|
"000000347073736800000000" // blob size and pssh
|
|
"edef8ba979d64acea3c827dcd51d21ed00000014" // Widevine system id
|
|
"08011210bdf1cb4fffc6506b8b7945b0bd2917fb"); // pssh data
|
|
|
|
decryptor_->OpenSession(config_.key_system(), &property_set,
|
|
kDefaultCdmIdentifier, nullptr, &session_id_);
|
|
CdmSessionId session_id2 = session_id_;
|
|
GenerateKeyRequest(init_data2, kLicenseTypeStreaming);
|
|
VerifyKeyRequestResponse(config_.license_server(), gp_client_auth2);
|
|
|
|
const SubSampleInfo* data = &kSingleEncryptedSubSampleShortExpiry;
|
|
|
|
std::vector<uint8_t> decrypt_buffer(data->encrypt_data.size());
|
|
CdmDecryptionParameters decryption_parameters(
|
|
&data->key_id, &data->encrypt_data.front(), data->encrypt_data.size(),
|
|
&data->iv, data->block_offset, &decrypt_buffer[0]);
|
|
decryption_parameters.is_encrypted = data->is_encrypted;
|
|
decryption_parameters.is_secure = data->is_secure;
|
|
decryption_parameters.subsample_flags = data->subsample_flags;
|
|
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->Decrypt(session_id1, data->validate_key_id,
|
|
decryption_parameters));
|
|
|
|
sleep(kSingleEncryptedSubSampleIcpLicenseDurationExpiration -
|
|
kSingleEncryptedSubSampleIcpLicenseExpirationWindow);
|
|
|
|
decryptor_->OpenSession(config_.key_system(), &property_set,
|
|
kDefaultCdmIdentifier, nullptr, &session_id_);
|
|
CdmSessionId session_id3 = session_id_;
|
|
GenerateKeyRequest(init_data1, kLicenseTypeStreaming);
|
|
VerifyKeyRequestResponse(config_.license_server(), config_.client_auth());
|
|
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->Decrypt(session_id1, data->validate_key_id,
|
|
decryption_parameters));
|
|
|
|
sleep(kSingleEncryptedSubSampleIcpLicenseExpirationWindow);
|
|
// session 1 will be expired, shared session 3 will be used to decrypt
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->Decrypt(session_id1, data->validate_key_id,
|
|
decryption_parameters));
|
|
|
|
decryptor_->CloseSession(session_id1);
|
|
decryptor_->CloseSession(session_id2);
|
|
decryptor_->CloseSession(session_id3);
|
|
}
|
|
|
|
TEST_F(WvCdmRequestLicenseTest, DecryptionKeyExpiredTest) {
|
|
const std::string kCpKeyId = wvutil::a2bs_hex(
|
|
"000000347073736800000000" // blob size and pssh
|
|
"EDEF8BA979D64ACEA3C827DCD51D21ED00000014" // Widevine system id
|
|
"0801121030313233343536373839616263646566"); // pssh data
|
|
const SubSampleInfo* data = &kSingleEncryptedSubSampleShortExpiry;
|
|
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
|
|
nullptr, &session_id_);
|
|
if (data->retrieve_key) {
|
|
GenerateKeyRequest(kCpKeyId, kLicenseTypeStreaming);
|
|
VerifyKeyRequestResponse(config_.license_server(), config_.client_auth());
|
|
}
|
|
|
|
std::vector<uint8_t> decrypt_buffer(data->encrypt_data.size());
|
|
CdmDecryptionParameters decryption_parameters(
|
|
&data->key_id, &data->encrypt_data.front(), data->encrypt_data.size(),
|
|
&data->iv, data->block_offset, &decrypt_buffer[0]);
|
|
decryption_parameters.is_encrypted = data->is_encrypted;
|
|
decryption_parameters.is_secure = data->is_secure;
|
|
decryption_parameters.subsample_flags = data->subsample_flags;
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->Decrypt(session_id_, data->validate_key_id,
|
|
decryption_parameters));
|
|
sleep(kSingleEncryptedSubSampleIcpLicenseDurationExpiration +
|
|
kSingleEncryptedSubSampleIcpLicenseExpirationWindow);
|
|
EXPECT_EQ(NEED_KEY, decryptor_->Decrypt(session_id_, data->validate_key_id,
|
|
decryption_parameters));
|
|
decryptor_->CloseSession(session_id_);
|
|
}
|
|
|
|
TEST_F(WvCdmRequestLicenseTest, PlaybackExpiry) {
|
|
StrictMock<TestWvCdmEventListener> listener;
|
|
DecryptCallbackTester decrypt_callback(decryptor_,
|
|
&kSecureStopSubSamplesIcp[0]);
|
|
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
|
|
&listener, &session_id_);
|
|
|
|
EXPECT_CALL(listener,
|
|
OnSessionKeysChange(
|
|
session_id_,
|
|
AllOf(Each(Pair(_, kKeyStatusUsable)), Not(IsEmpty())), true))
|
|
.WillOnce(Invoke(&decrypt_callback, &DecryptCallbackTester::Decrypt));
|
|
EXPECT_CALL(listener, OnExpirationUpdate(session_id_, _));
|
|
|
|
GenerateKeyRequest(kPsshStreamingClip21, kLicenseTypeStreaming, nullptr);
|
|
VerifyKeyRequestResponse(config_.license_server(), config_.client_auth());
|
|
|
|
EXPECT_CALL(
|
|
listener,
|
|
OnSessionKeysChange(
|
|
session_id_, AllOf(Each(Pair(_, kKeyStatusExpired)), Not(IsEmpty())),
|
|
false));
|
|
|
|
// Elapse time so that the key should now be considered expired.
|
|
std::this_thread::sleep_for(kExpirationStreamingClip21PlaybackDurationTimeMs);
|
|
}
|
|
|
|
TEST_F(WvCdmRequestLicenseTest, PlaybackExpiry_DecryptBeforeLicense) {
|
|
StrictMock<TestWvCdmEventListener> listener;
|
|
DecryptCallbackTester decrypt_callback(decryptor_,
|
|
&kSecureStopSubSamplesIcp[0]);
|
|
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
|
|
&listener, &session_id_);
|
|
|
|
// Decrypt before license is received is expected to fail but should
|
|
// not start the playback timer
|
|
EXPECT_FALSE(VerifyDecryption(session_id_, kSecureStopSubSamplesIcp[0],
|
|
CdmResponseType(NEED_KEY)));
|
|
std::this_thread::sleep_for(kExpirationStreamingClip21PlaybackDurationTimeMs);
|
|
|
|
EXPECT_CALL(listener,
|
|
OnSessionKeysChange(
|
|
session_id_,
|
|
AllOf(Each(Pair(_, kKeyStatusUsable)), Not(IsEmpty())), true))
|
|
.WillOnce(Invoke(&decrypt_callback, &DecryptCallbackTester::Decrypt));
|
|
EXPECT_CALL(listener, OnExpirationUpdate(session_id_, _));
|
|
|
|
GenerateKeyRequest(kPsshStreamingClip21, kLicenseTypeStreaming, nullptr);
|
|
VerifyKeyRequestResponse(config_.license_server(), config_.client_auth());
|
|
|
|
EXPECT_CALL(
|
|
listener,
|
|
OnSessionKeysChange(
|
|
session_id_, AllOf(Each(Pair(_, kKeyStatusExpired)), Not(IsEmpty())),
|
|
false));
|
|
|
|
// Elapse time so that the key should now be considered expired.
|
|
std::this_thread::sleep_for(kExpirationStreamingClip21PlaybackDurationTimeMs);
|
|
}
|
|
|
|
TEST_F(WvCdmRequestLicenseTest, SessionKeyChangeNotificationTest) {
|
|
StrictMock<TestWvCdmEventListener> listener;
|
|
DecryptCallbackTester decrypt_callback(decryptor_,
|
|
&kSingleEncryptedSubSampleShortExpiry);
|
|
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
|
|
&listener, &session_id_);
|
|
EXPECT_CALL(listener,
|
|
OnSessionKeysChange(
|
|
session_id_,
|
|
AllOf(Each(Pair(_, kKeyStatusUsable)), Not(IsEmpty())), true))
|
|
.WillOnce(Invoke(&decrypt_callback, &DecryptCallbackTester::Decrypt));
|
|
|
|
EXPECT_CALL(listener, OnExpirationUpdate(session_id_, _));
|
|
|
|
const std::string kCpKeyId = wvutil::a2bs_hex(
|
|
"000000347073736800000000" // blob size and pssh
|
|
"EDEF8BA979D64ACEA3C827DCD51D21ED00000014" // Widevine system id
|
|
"0801121030313233343536373839616263646566"); // pssh data
|
|
|
|
GenerateKeyRequest(kCpKeyId, kLicenseTypeStreaming);
|
|
VerifyKeyRequestResponse(config_.license_server(), config_.client_auth());
|
|
|
|
decryptor_->CloseSession(session_id_);
|
|
}
|
|
|
|
class WvCdmDecryptionTest
|
|
: public WvCdmRequestLicenseTest,
|
|
public ::testing::WithParamInterface<const SubSampleInfo*> {};
|
|
|
|
TEST_P(WvCdmDecryptionTest, DecryptionTest) {
|
|
const SubSampleInfo* data = GetParam();
|
|
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
|
|
nullptr, &session_id_);
|
|
if (data->retrieve_key) {
|
|
GenerateKeyRequest(binary_key_id(), kLicenseTypeStreaming);
|
|
VerifyKeyRequestResponse(config_.license_server(), config_.client_auth());
|
|
}
|
|
|
|
uint32_t decrypt_sample_buffer_size = 0;
|
|
uint32_t decrypt_buffer_offset = 0;
|
|
for (size_t i = 0; i < data->num_of_subsamples; i++)
|
|
decrypt_sample_buffer_size += (data + i)->encrypt_data.size();
|
|
|
|
std::vector<uint8_t> decrypt_buffer(decrypt_sample_buffer_size);
|
|
std::vector<uint8_t> expected_decrypt_data;
|
|
|
|
for (size_t i = 0; i < data->num_of_subsamples; i++) {
|
|
CdmDecryptionParameters decryption_parameters(
|
|
&(data + i)->key_id, &(data + i)->encrypt_data.front(),
|
|
(data + i)->encrypt_data.size(), &(data + i)->iv,
|
|
(data + i)->block_offset, &decrypt_buffer[0]);
|
|
decryption_parameters.is_encrypted = (data + i)->is_encrypted;
|
|
decryption_parameters.is_secure = (data + i)->is_secure;
|
|
decryption_parameters.subsample_flags = (data + i)->subsample_flags;
|
|
decryption_parameters.decrypt_buffer_length = decrypt_sample_buffer_size;
|
|
decryption_parameters.decrypt_buffer_offset = decrypt_buffer_offset;
|
|
expected_decrypt_data.insert(expected_decrypt_data.end(),
|
|
(data + i)->decrypt_data.begin(),
|
|
(data + i)->decrypt_data.end());
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->Decrypt(session_id_, (data + i)->validate_key_id,
|
|
decryption_parameters));
|
|
|
|
decrypt_buffer_offset += (data + i)->encrypt_data.size();
|
|
}
|
|
EXPECT_TRUE(std::equal(expected_decrypt_data.begin(),
|
|
expected_decrypt_data.end(), decrypt_buffer.begin()));
|
|
decryptor_->CloseSession(session_id_);
|
|
}
|
|
|
|
INSTANTIATE_TEST_CASE_P(Cdm, WvCdmDecryptionTest,
|
|
::testing::Values(&kClearSubSample,
|
|
&kClearSubSampleNoKey,
|
|
&kSingleEncryptedSubSample,
|
|
&kSwitchKeyEncryptedSubSamples[0],
|
|
&kPartialEncryptedSubSamples[0]));
|
|
|
|
class WvCdmSessionSharingNoKeyTest
|
|
: public WvCdmRequestLicenseTest,
|
|
public ::testing::WithParamInterface<const SubSampleInfo*> {};
|
|
|
|
TEST_P(WvCdmSessionSharingNoKeyTest, DecryptionTest) {
|
|
const SubSampleInfo* data = GetParam();
|
|
|
|
TestWvCdmClientPropertySet property_set;
|
|
property_set.set_session_sharing_mode(true);
|
|
|
|
decryptor_->OpenSession(config_.key_system(), &property_set,
|
|
kDefaultCdmIdentifier, nullptr, &session_id_);
|
|
CdmSessionId gp_session_id_1 = session_id_;
|
|
GenerateKeyRequest(binary_key_id(), kLicenseTypeStreaming);
|
|
|
|
// TODO(rfrias): Move content information to ConfigTestEnv
|
|
std::string gp_client_auth2 =
|
|
"?source=YOUTUBE&video_id=z3S_NhwueaM&oauth=ya.gtsqawidevine";
|
|
std::string gp_key_id2 = wvutil::a2bs_hex(
|
|
"000000347073736800000000" // blob size and pssh
|
|
"edef8ba979d64acea3c827dcd51d21ed00000014" // Widevine system id
|
|
"08011210bdf1cb4fffc6506b8b7945b0bd2917fb"); // pssh data
|
|
|
|
decryptor_->OpenSession(config_.key_system(), &property_set,
|
|
kDefaultCdmIdentifier, nullptr, &session_id_);
|
|
CdmSessionId gp_session_id_2 = session_id_;
|
|
GenerateKeyRequest(gp_key_id2, kLicenseTypeStreaming);
|
|
|
|
std::vector<uint8_t> decrypt_buffer(data->encrypt_data.size());
|
|
CdmDecryptionParameters decryption_parameters(
|
|
&data->key_id, &data->encrypt_data.front(), data->encrypt_data.size(),
|
|
&data->iv, data->block_offset, &decrypt_buffer[0]);
|
|
decryption_parameters.is_encrypted = data->is_encrypted;
|
|
decryption_parameters.is_secure = data->is_secure;
|
|
decryption_parameters.subsample_flags = data->subsample_flags;
|
|
|
|
bool can_decrypt = !data->is_encrypted &&
|
|
data->subsample_flags & OEMCrypto_FirstSubsample &&
|
|
data->subsample_flags & OEMCrypto_LastSubsample;
|
|
EXPECT_EQ(can_decrypt ? wvcdm::NO_ERROR : KEY_NOT_FOUND_IN_SESSION,
|
|
decryptor_->Decrypt(gp_session_id_2, data->validate_key_id,
|
|
decryption_parameters));
|
|
if (can_decrypt) {
|
|
EXPECT_TRUE(std::equal(data->decrypt_data.begin(), data->decrypt_data.end(),
|
|
decrypt_buffer.begin()));
|
|
}
|
|
|
|
decryptor_->CloseSession(gp_session_id_1);
|
|
decryptor_->CloseSession(gp_session_id_2);
|
|
}
|
|
|
|
INSTANTIATE_TEST_CASE_P(Cdm, WvCdmSessionSharingNoKeyTest,
|
|
::testing::Values(&kClearSubSample,
|
|
&kClearSubSamples[0],
|
|
&kClearSubSamples[1],
|
|
&kClearSubSampleNoKey,
|
|
&kSingleEncryptedSubSample));
|
|
|
|
TEST(VersionNumberTest, VersionNumberChangeCanary) {
|
|
std::string release_number =
|
|
android::base::GetProperty("ro.build.version.release", "");
|
|
ASSERT_TRUE(release_number.size() > 0);
|
|
// Reminder to change the Widevine CDM version number.
|
|
EXPECT_EQ("13", release_number)
|
|
<< "The Android version number has changed. You need to update this test "
|
|
"and also possibly update the Widevine version number in "
|
|
"wv_android_constants.h";
|
|
}
|
|
|
|
TEST_F(WvCdmRequestLicenseTest, AddHlsStreamingKeyTest) {
|
|
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
|
|
nullptr, &session_id_);
|
|
CdmAppParameterMap app_parameters;
|
|
GenerateKeyRequest(CdmResponseType(wvcdm::KEY_MESSAGE), HLS_INIT_DATA_FORMAT,
|
|
kAttributeListSampleAes, app_parameters,
|
|
kLicenseTypeStreaming, nullptr);
|
|
// TODO(rfrias): Remove once we switch to git-on-borg
|
|
std::string license_server = "https://proxy.uat.widevine.com/proxy";
|
|
VerifyKeyRequestResponse(license_server, config_.client_auth());
|
|
decryptor_->CloseSession(session_id_);
|
|
}
|
|
|
|
class WvHlsInitDataTest : public WvCdmRequestLicenseTest,
|
|
public ::testing::WithParamInterface<std::string> {};
|
|
|
|
TEST_P(WvHlsInitDataTest, InvalidHlsFormatTest) {
|
|
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
|
|
nullptr, &session_id_);
|
|
CdmAppParameterMap app_parameters;
|
|
std::string init_data = GetParam();
|
|
GenerateKeyRequest(CdmResponseType(INIT_DATA_NOT_FOUND), HLS_INIT_DATA_FORMAT,
|
|
init_data, app_parameters, kLicenseTypeStreaming, nullptr);
|
|
decryptor_->CloseSession(session_id_);
|
|
}
|
|
|
|
INSTANTIATE_TEST_CASE_P(
|
|
Cdm, WvHlsInitDataTest,
|
|
::testing::Values(kAttributeListKeyFormatNonWidevine,
|
|
kAttributeListKeyFormatVersionUnregonized,
|
|
kAttributeListUnspecifiedIv,
|
|
kAttributeListUnspecifiedMethod));
|
|
|
|
class WvHlsDecryptionTest
|
|
: public WvCdmRequestLicenseTest,
|
|
public ::testing::WithParamInterface<const HlsDecryptionInfo*> {};
|
|
|
|
TEST_P(WvHlsDecryptionTest, HlsDecryptionTest) {
|
|
Provision(kDefaultCdmIdentifier, kLevel3);
|
|
TestWvCdmClientPropertySet client_property_set;
|
|
client_property_set.set_security_level(QUERY_VALUE_SECURITY_LEVEL_L3);
|
|
const HlsDecryptionInfo* info = GetParam();
|
|
|
|
TestWvCdmHlsEventListener listener;
|
|
decryptor_->OpenSession(config_.key_system(), &client_property_set,
|
|
kDefaultCdmIdentifier, &listener, &session_id_);
|
|
CdmAppParameterMap app_parameters;
|
|
GenerateKeyRequest(CdmResponseType(KEY_MESSAGE), HLS_INIT_DATA_FORMAT,
|
|
info->attribute_list, app_parameters,
|
|
kLicenseTypeStreaming, nullptr);
|
|
// TODO(rfrias): Remove once we switch to git-on-borg
|
|
std::string license_server = "https://proxy.uat.widevine.com/proxy";
|
|
VerifyKeyRequestResponse(license_server, config_.client_auth());
|
|
CdmKeyStatusMap key_status_map = listener.GetKeyStatusMap();
|
|
EXPECT_EQ(1u, key_status_map.size());
|
|
KeyId key_id = key_status_map.begin()->first;
|
|
EXPECT_EQ(KEY_ID_SIZE, key_id.size());
|
|
|
|
for (size_t i = 0; i < info->number_of_segments; ++i) {
|
|
const HlsSegmentInfo* data = info->segment_info + i;
|
|
std::vector<uint8_t> output_buffer(data->encrypted_data.size(), 0);
|
|
std::vector<uint8_t> iv(data->iv.begin(), data->iv.end());
|
|
CdmDecryptionParameters decryption_parameters(
|
|
&key_id, reinterpret_cast<const uint8_t*>(data->encrypted_data.c_str()),
|
|
data->encrypted_data.size(), &iv, 0, &output_buffer[0]);
|
|
decryption_parameters.is_encrypted = true;
|
|
decryption_parameters.is_secure = false;
|
|
decryption_parameters.cipher_mode = kCipherModeCbc;
|
|
if (info->sample_aes) {
|
|
decryption_parameters.pattern_descriptor.encrypt_blocks = 1;
|
|
decryption_parameters.pattern_descriptor.skip_blocks = 9;
|
|
}
|
|
decryption_parameters.subsample_flags = data->subsample_flags;
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->Decrypt(session_id_, false, decryption_parameters));
|
|
EXPECT_EQ(data->clear_data,
|
|
std::string(reinterpret_cast<const char*>(&output_buffer[0]),
|
|
output_buffer.size()));
|
|
}
|
|
|
|
decryptor_->CloseSession(session_id_);
|
|
}
|
|
|
|
INSTANTIATE_TEST_CASE_P(Cdm, WvHlsDecryptionTest,
|
|
::testing::Range(&kHlsDecryptionTestVectors[0],
|
|
&kHlsDecryptionTestVectors[11]));
|
|
|
|
class WvHlsFourCCBackwardCompatibilityTest
|
|
: public WvCdmRequestLicenseTest,
|
|
public ::testing::WithParamInterface<const HlsDecryptionInfo*> {};
|
|
|
|
TEST_P(WvHlsFourCCBackwardCompatibilityTest, HlsDecryptionTest) {
|
|
Provision(kDefaultCdmIdentifier, kLevel3);
|
|
TestWvCdmClientPropertySet client_property_set;
|
|
client_property_set.set_security_level(QUERY_VALUE_SECURITY_LEVEL_L3);
|
|
const HlsDecryptionInfo* info = GetParam();
|
|
|
|
TestWvCdmHlsEventListener listener;
|
|
decryptor_->OpenSession(config_.key_system(), &client_property_set,
|
|
kDefaultCdmIdentifier, &listener, &session_id_);
|
|
CdmAppParameterMap app_parameters;
|
|
GenerateKeyRequest(CdmResponseType(KEY_MESSAGE), ISO_BMFF_VIDEO_MIME_TYPE,
|
|
info->attribute_list, app_parameters,
|
|
kLicenseTypeStreaming, nullptr);
|
|
// TODO(rfrias): Remove once we switch to git-on-borg
|
|
std::string license_server = "https://proxy.uat.widevine.com/proxy";
|
|
VerifyKeyRequestResponse(license_server, config_.client_auth());
|
|
CdmKeyStatusMap key_status_map = listener.GetKeyStatusMap();
|
|
EXPECT_EQ(1u, key_status_map.size());
|
|
KeyId key_id = key_status_map.begin()->first;
|
|
EXPECT_EQ(KEY_ID_SIZE, key_id.size());
|
|
|
|
for (size_t i = 0; i < info->number_of_segments; ++i) {
|
|
const HlsSegmentInfo* data = info->segment_info + i;
|
|
std::vector<uint8_t> output_buffer(data->encrypted_data.size(), 0);
|
|
std::vector<uint8_t> iv(data->iv.begin(), data->iv.end());
|
|
CdmDecryptionParameters decryption_parameters(
|
|
&key_id, reinterpret_cast<const uint8_t*>(data->encrypted_data.c_str()),
|
|
data->encrypted_data.size(), &iv, 0, &output_buffer[0]);
|
|
decryption_parameters.is_encrypted = true;
|
|
decryption_parameters.is_secure = false;
|
|
decryption_parameters.cipher_mode = kCipherModeCbc;
|
|
if (info->sample_aes) {
|
|
decryption_parameters.pattern_descriptor.encrypt_blocks = 1;
|
|
decryption_parameters.pattern_descriptor.skip_blocks = 9;
|
|
}
|
|
decryption_parameters.subsample_flags = data->subsample_flags;
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->Decrypt(session_id_, false, decryption_parameters));
|
|
EXPECT_EQ(data->clear_data,
|
|
std::string(reinterpret_cast<const char*>(&output_buffer[0]),
|
|
output_buffer.size()));
|
|
}
|
|
|
|
decryptor_->CloseSession(session_id_);
|
|
}
|
|
|
|
INSTANTIATE_TEST_CASE_P(
|
|
Cdm, WvHlsFourCCBackwardCompatibilityTest,
|
|
::testing::Range(&kHlsFourCCBackwardCompatibilityTestVectors[0],
|
|
&kHlsFourCCBackwardCompatibilityTestVectors[4]));
|
|
|
|
class WvCenc30Test
|
|
: public WvCdmRequestLicenseTest,
|
|
public ::testing::WithParamInterface<const SingleSampleDecryptionInfo*> {
|
|
};
|
|
|
|
TEST_P(WvCenc30Test, DecryptionTest) {
|
|
Provision(kDefaultCdmIdentifier, kLevel3);
|
|
TestWvCdmClientPropertySet client_property_set;
|
|
client_property_set.set_security_level(QUERY_VALUE_SECURITY_LEVEL_L3);
|
|
|
|
std::string value;
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->QueryStatus(
|
|
kLevel3, wvcdm::QUERY_KEY_OEMCRYPTO_API_VERSION, &value));
|
|
std::istringstream ss(value);
|
|
uint32_t api_version;
|
|
ss >> api_version;
|
|
ASSERT_FALSE(ss.fail());
|
|
EXPECT_TRUE(ss.eof());
|
|
|
|
// Ability to switch between cipher modes without re-requesting a license
|
|
// was introduced in OEMCrypto v14
|
|
if (api_version < 14u) return;
|
|
|
|
const SingleSampleDecryptionInfo* info = GetParam();
|
|
|
|
TestWvCdmHlsEventListener listener;
|
|
decryptor_->OpenSession(config_.key_system(), &client_property_set,
|
|
kDefaultCdmIdentifier, &listener, &session_id_);
|
|
CdmAppParameterMap app_parameters;
|
|
GenerateKeyRequest(info->pssh, app_parameters, kLicenseTypeStreaming,
|
|
nullptr);
|
|
VerifyKeyRequestResponse(config_.license_server(), config_.client_auth());
|
|
CdmKeyStatusMap key_status_map = listener.GetKeyStatusMap();
|
|
EXPECT_EQ(8u, key_status_map.size());
|
|
|
|
const Cenc30SampleInfo* data = &info->sample_info;
|
|
std::vector<uint8_t> output_buffer(data->encrypted_data.size(), 0);
|
|
std::vector<uint8_t> iv(data->iv.begin(), data->iv.end());
|
|
CdmDecryptionParameters decryption_parameters(
|
|
&data->key_id,
|
|
reinterpret_cast<const uint8_t*>(data->encrypted_data.c_str()),
|
|
data->encrypted_data.size(), &iv, 0, &output_buffer[0]);
|
|
decryption_parameters.is_encrypted = data->is_encrypted;
|
|
decryption_parameters.is_secure = false;
|
|
decryption_parameters.cipher_mode = data->cipher_mode;
|
|
decryption_parameters.subsample_flags = data->subsample_flags;
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->Decrypt(session_id_, false, decryption_parameters));
|
|
EXPECT_EQ(data->clear_data,
|
|
std::string(reinterpret_cast<const char*>(&output_buffer[0]),
|
|
output_buffer.size()));
|
|
|
|
decryptor_->CloseSession(session_id_);
|
|
}
|
|
|
|
INSTANTIATE_TEST_CASE_P(Cdm, WvCenc30Test,
|
|
::testing::Range(&kCenc30DecryptionData[0],
|
|
&kCenc30DecryptionData[2]));
|
|
|
|
class WvCenc30SwitchCipherModeTest
|
|
: public WvCdmRequestLicenseTest,
|
|
public ::testing::WithParamInterface<const FourSampleDecryptionInfo*> {};
|
|
|
|
TEST_P(WvCenc30SwitchCipherModeTest, DecryptionTest) {
|
|
Provision(kDefaultCdmIdentifier, kLevel3);
|
|
TestWvCdmClientPropertySet client_property_set;
|
|
client_property_set.set_security_level(QUERY_VALUE_SECURITY_LEVEL_L3);
|
|
|
|
std::string value;
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->QueryStatus(
|
|
kLevel3, wvcdm::QUERY_KEY_OEMCRYPTO_API_VERSION, &value));
|
|
std::istringstream ss(value);
|
|
uint32_t api_version;
|
|
ss >> api_version;
|
|
ASSERT_FALSE(ss.fail());
|
|
EXPECT_TRUE(ss.eof());
|
|
|
|
// Ability to switch between cipher modes without re-requesting a license
|
|
// was introduced in OEMCrypto v14
|
|
if (api_version < 14) return;
|
|
|
|
const FourSampleDecryptionInfo* info = GetParam();
|
|
|
|
TestWvCdmHlsEventListener listener;
|
|
decryptor_->OpenSession(config_.key_system(), &client_property_set,
|
|
kDefaultCdmIdentifier, &listener, &session_id_);
|
|
CdmAppParameterMap app_parameters;
|
|
GenerateKeyRequest(info->pssh, app_parameters, kLicenseTypeStreaming,
|
|
nullptr);
|
|
VerifyKeyRequestResponse(config_.license_server(), config_.client_auth());
|
|
CdmKeyStatusMap key_status_map = listener.GetKeyStatusMap();
|
|
EXPECT_EQ(8u, key_status_map.size());
|
|
|
|
for (size_t i = 0; i < ArraySize(info->sample_info); ++i) {
|
|
const Cenc30SampleInfo* data = &info->sample_info[i];
|
|
std::vector<uint8_t> output_buffer(data->encrypted_data.size(), 0);
|
|
std::vector<uint8_t> iv(data->iv.begin(), data->iv.end());
|
|
CdmDecryptionParameters decryption_parameters(
|
|
&data->key_id,
|
|
reinterpret_cast<const uint8_t*>(data->encrypted_data.c_str()),
|
|
data->encrypted_data.size(), &iv, 0, &output_buffer[0]);
|
|
decryption_parameters.is_encrypted = data->is_encrypted;
|
|
decryption_parameters.is_secure = false;
|
|
decryption_parameters.cipher_mode = data->cipher_mode;
|
|
decryption_parameters.subsample_flags = data->subsample_flags;
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->Decrypt(session_id_, false, decryption_parameters));
|
|
EXPECT_EQ(data->clear_data,
|
|
std::string(reinterpret_cast<const char*>(&output_buffer[0]),
|
|
output_buffer.size()));
|
|
}
|
|
|
|
decryptor_->CloseSession(session_id_);
|
|
}
|
|
|
|
INSTANTIATE_TEST_CASE_P(Cdm, WvCenc30SwitchCipherModeTest,
|
|
::testing::Range(&kCenc30SwitchCipherData[0],
|
|
&kCenc30SwitchCipherData[8]));
|
|
|
|
TEST_F(WvCdmRequestLicenseTest, CloseCdmReleaseResourcesTest) {
|
|
Provision(kAlternateCdmIdentifier1, kLevelDefault);
|
|
|
|
// Retrieve a streaming license
|
|
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_->OpenSession(
|
|
config_.key_system(), nullptr,
|
|
kDefaultCdmIdentifier, nullptr, &session_id_));
|
|
GenerateKeyRequest(binary_key_id(), kLicenseTypeStreaming);
|
|
VerifyKeyRequestResponse(config_.license_server(), config_.client_auth());
|
|
|
|
// Open a few more sessions
|
|
CdmSessionId session_id1;
|
|
CdmSessionId session_id2;
|
|
CdmSessionId session_id3;
|
|
CdmSessionId session_id4;
|
|
CdmSessionId alternate_session_id;
|
|
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_->OpenSession(
|
|
config_.key_system(), nullptr,
|
|
kDefaultCdmIdentifier, nullptr, &session_id1));
|
|
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_->OpenSession(
|
|
config_.key_system(), nullptr,
|
|
kDefaultCdmIdentifier, nullptr, &session_id2));
|
|
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_->OpenSession(
|
|
config_.key_system(), nullptr,
|
|
kDefaultCdmIdentifier, nullptr, &session_id3));
|
|
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_->OpenSession(
|
|
config_.key_system(), nullptr,
|
|
kDefaultCdmIdentifier, nullptr, &session_id4));
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->OpenSession(config_.key_system(), nullptr,
|
|
kAlternateCdmIdentifier1, nullptr,
|
|
&alternate_session_id));
|
|
|
|
// Close all open sessions and disable the timer running for |session_id_|
|
|
decryptor_->CloseCdm(kDefaultCdmIdentifier);
|
|
|
|
EXPECT_FALSE(decryptor_->IsOpenSession(session_id_));
|
|
EXPECT_FALSE(decryptor_->IsOpenSession(session_id1));
|
|
EXPECT_FALSE(decryptor_->IsOpenSession(session_id2));
|
|
EXPECT_FALSE(decryptor_->IsOpenSession(session_id3));
|
|
EXPECT_FALSE(decryptor_->IsOpenSession(session_id4));
|
|
EXPECT_TRUE(decryptor_->IsOpenSession(alternate_session_id));
|
|
}
|
|
|
|
// Enable when OEMCrypto v15 has been deployed. Currently setting a decrypt
|
|
// hash returns OEMCrypto_ERROR_NOT_IMPLEMENTED
|
|
TEST_F(WvCdmRequestLicenseTest, DISABLED_DecryptPathTest) {
|
|
Provision();
|
|
|
|
// Retrieve a streaming license
|
|
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_->OpenSession(
|
|
config_.key_system(), nullptr,
|
|
kDefaultCdmIdentifier, nullptr, &session_id_));
|
|
|
|
CdmSessionId invalid_session_id = session_id_ + "more";
|
|
std::string frame_number = std::to_string(5);
|
|
std::vector<uint8_t> binary_hash{0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38};
|
|
const std::string hash = wvutil::Base64Encode(binary_hash);
|
|
|
|
// Invalid session id
|
|
std::string hash_data =
|
|
invalid_session_id + kComma + std::to_string(5) + kComma + hash;
|
|
|
|
CdmSessionId session_id;
|
|
EXPECT_NE(wvcdm::NO_ERROR,
|
|
decryptor_->SetDecryptHash(hash_data, &session_id));
|
|
|
|
// Valid hash data
|
|
hash_data = session_id_ + kComma + std::to_string(5) + kComma + hash;
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->SetDecryptHash(hash_data, &session_id));
|
|
EXPECT_EQ(session_id_, session_id);
|
|
|
|
decryptor_->CloseSession(session_id_);
|
|
}
|
|
|
|
// This tests checks that if a valid offline license file is found on
|
|
// the device but is missing the usage entry associated with it, that
|
|
// the CDM can still remove the license without issuing an error back
|
|
// to the calling app.
|
|
// This checks two cases:
|
|
// 1) The license's entry is outside the range of the table
|
|
// 2) The entry in the usage table that the license points to does
|
|
// not match the license's key set ID (possible for entry to have
|
|
// been overwritten).
|
|
TEST_F(WvCdmRequestLicenseTest, RemoveOfflineLicenseWithMissingUsageEntry) {
|
|
Unprovision();
|
|
Provision();
|
|
const CdmSecurityLevel security_level = GetDefaultSecurityLevel();
|
|
|
|
FileSystem file_system;
|
|
DeviceFiles handle(&file_system);
|
|
EXPECT_TRUE(handle.Init(security_level));
|
|
|
|
std::string key_id;
|
|
std::string client_auth;
|
|
GetOfflineConfiguration(&key_id, &client_auth);
|
|
|
|
// Setup: Request an offline license to create a valid license file.
|
|
// This license will be used to test how the CDM handles request to
|
|
// remove it when its entry has been deleted.
|
|
|
|
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
|
|
nullptr, &session_id_);
|
|
GenerateKeyRequest(key_id, kLicenseTypeOffline);
|
|
VerifyKeyRequestResponse(config_.license_server(), client_auth);
|
|
// Save the key set ID for check below.
|
|
const std::string original_key_set_id(key_set_id_);
|
|
decryptor_->CloseSession(session_id_);
|
|
|
|
// Retrieve license from storage for later.
|
|
DeviceFiles::CdmLicenseData license_data;
|
|
DeviceFiles::ResponseType sub_result = DeviceFiles::kNoError;
|
|
EXPECT_TRUE(
|
|
handle.RetrieveLicense(original_key_set_id, &license_data, &sub_result));
|
|
EXPECT_EQ(DeviceFiles::kNoError, sub_result);
|
|
|
|
// Re-provision.
|
|
Unprovision();
|
|
handle.DeleteAllFiles();
|
|
Provision();
|
|
|
|
// Part 1: Test when usage entry is out of range of the table.
|
|
|
|
// Store license from earlier, this will cause ListStoredLicenses() to
|
|
// return the key set ID of the setup license.
|
|
EXPECT_TRUE(handle.StoreLicense(license_data, &sub_result));
|
|
EXPECT_EQ(DeviceFiles::kNoError, sub_result);
|
|
|
|
std::vector<CdmKeySetId> key_set_ids;
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->ListStoredLicenses(
|
|
security_level, kDefaultCdmIdentifier, &key_set_ids));
|
|
// Note: It is possible that future changes to the CDM will cause this
|
|
// check to fail (such by filtering results from ListStoreLicenses).
|
|
EXPECT_THAT(key_set_ids, Contains(original_key_set_id));
|
|
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->RemoveOfflineLicense(
|
|
original_key_set_id, security_level, kDefaultCdmIdentifier));
|
|
|
|
// Verify license has been removed.
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->ListStoredLicenses(
|
|
security_level, kDefaultCdmIdentifier, &key_set_ids));
|
|
EXPECT_THAT(key_set_ids, Not(Contains(original_key_set_id)));
|
|
|
|
// Re-provision.
|
|
Unprovision();
|
|
handle.DeleteAllFiles();
|
|
Provision();
|
|
|
|
// Part 2: Test when the entry does not match the license's key set ID.
|
|
|
|
EXPECT_TRUE(handle.StoreLicense(license_data, &sub_result));
|
|
EXPECT_EQ(DeviceFiles::kNoError, sub_result);
|
|
|
|
// Request another license so that the usage table is not empty.
|
|
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
|
|
nullptr, &session_id_);
|
|
GenerateKeyRequest(key_id, kLicenseTypeOffline);
|
|
VerifyKeyRequestResponse(config_.license_server(), client_auth);
|
|
decryptor_->CloseSession(session_id_);
|
|
|
|
// Get list of existing offline licenses.
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->ListStoredLicenses(
|
|
security_level, kDefaultCdmIdentifier, &key_set_ids));
|
|
EXPECT_THAT(key_set_ids, Contains(original_key_set_id));
|
|
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->RemoveOfflineLicense(
|
|
original_key_set_id, security_level, kDefaultCdmIdentifier));
|
|
|
|
// Verify license has been removed.
|
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
|
decryptor_->ListStoredLicenses(
|
|
security_level, kDefaultCdmIdentifier, &key_set_ids));
|
|
EXPECT_THAT(key_set_ids, Not(Contains(original_key_set_id)));
|
|
}
|
|
|
|
class WvCdmRequestLicenseRollbackTest
|
|
: public WvCdmRequestLicenseTest,
|
|
public ::testing::WithParamInterface<const SubSampleInfo*> {
|
|
public:
|
|
WvCdmRequestLicenseRollbackTest() {
|
|
const SubSampleInfo* data = &kSingleEncryptedSubSampleShortExpiry;
|
|
decrypt_buffer_.resize(data->encrypt_data.size());
|
|
decryption_parameters_ = CdmDecryptionParameters(
|
|
&data->key_id, &data->encrypt_data.front(), data->encrypt_data.size(),
|
|
&data->iv, data->block_offset, &decrypt_buffer_[0]);
|
|
decryption_parameters_.is_encrypted = data->is_encrypted;
|
|
decryption_parameters_.is_secure = data->is_secure;
|
|
decryption_parameters_.subsample_flags = data->subsample_flags;
|
|
validate_key_id_ = data->validate_key_id;
|
|
}
|
|
~WvCdmRequestLicenseRollbackTest() {}
|
|
|
|
protected:
|
|
void RollbackSystemTime(time_t rollback_time_ms) {
|
|
if (!in_rollback_state_) {
|
|
LOGW("Rolling back system time %ld ms.", rollback_time_ms);
|
|
wall_time_before_rollback_ = std::chrono::system_clock::now();
|
|
monotonic_time_before_rollback_ = std::chrono::steady_clock::now();
|
|
auto modified_wall_time = wall_time_before_rollback_ -
|
|
std::chrono::milliseconds(rollback_time_ms);
|
|
timespec modified_wall_time_spec;
|
|
modified_wall_time_spec.tv_sec =
|
|
std::chrono::duration_cast<std::chrono::seconds>(
|
|
modified_wall_time.time_since_epoch())
|
|
.count();
|
|
modified_wall_time_spec.tv_nsec =
|
|
std::chrono::duration_cast<std::chrono::nanoseconds>(
|
|
modified_wall_time.time_since_epoch())
|
|
.count() %
|
|
(1000 * 1000 * 1000);
|
|
ASSERT_EQ(0, clock_settime(CLOCK_REALTIME, &modified_wall_time_spec));
|
|
in_rollback_state_ = true;
|
|
} else {
|
|
LOGE("Can't rollback system time more than once without restoring.");
|
|
}
|
|
}
|
|
|
|
void RestoreSystemTime() {
|
|
if (in_rollback_state_) {
|
|
LOGW("Restoring the system time.");
|
|
auto monotonic_time_after_rollback = std::chrono::steady_clock::now();
|
|
auto monotonic_time_diff =
|
|
monotonic_time_after_rollback - monotonic_time_before_rollback_;
|
|
auto real_time = wall_time_before_rollback_ + monotonic_time_diff;
|
|
timespec real_time_spec;
|
|
real_time_spec.tv_sec = std::chrono::duration_cast<std::chrono::seconds>(
|
|
real_time.time_since_epoch())
|
|
.count();
|
|
real_time_spec.tv_nsec =
|
|
std::chrono::duration_cast<std::chrono::nanoseconds>(
|
|
real_time.time_since_epoch())
|
|
.count() %
|
|
(1000 * 1000 * 1000);
|
|
ASSERT_EQ(0, clock_settime(CLOCK_REALTIME, &real_time_spec));
|
|
in_rollback_state_ = false;
|
|
} else {
|
|
LOGW("System time has already been restored.");
|
|
}
|
|
}
|
|
|
|
CdmResponseType Decrypt(CdmSessionId session_id) {
|
|
std::fill(decrypt_buffer_.begin(), decrypt_buffer_.end(), 0);
|
|
return decryptor_->Decrypt(session_id, validate_key_id_,
|
|
decryption_parameters_);
|
|
}
|
|
|
|
std::chrono::time_point<std::chrono::steady_clock>
|
|
monotonic_time_before_rollback_;
|
|
std::chrono::time_point<std::chrono::system_clock> wall_time_before_rollback_;
|
|
bool in_rollback_state_ = false;
|
|
const std::string init_data_with_expiry_ = wvutil::a2bs_hex(
|
|
"000000347073736800000000" // blob size and pssh
|
|
"EDEF8BA979D64ACEA3C827DCD51D21ED00000014" // Widevine system id
|
|
"0801121030313233343536373839616263646566"); // pssh data
|
|
// Expiration of the key corresponding to init_data_with_expiry_ with a window
|
|
// to ensure expiration.
|
|
const time_t kExpirationTimeMs_ =
|
|
kSingleEncryptedSubSampleIcpLicenseDurationExpiration * 1000;
|
|
const time_t kExpirationWindowMs_ =
|
|
kSingleEncryptedSubSampleIcpLicenseExpirationWindow * 1000;
|
|
const time_t kExpirationWithWindowMs_ =
|
|
(kSingleEncryptedSubSampleIcpLicenseDurationExpiration +
|
|
kSingleEncryptedSubSampleIcpLicenseExpirationWindow) *
|
|
1000;
|
|
CdmDecryptionParameters decryption_parameters_;
|
|
std::vector<uint8_t> decrypt_buffer_;
|
|
bool validate_key_id_;
|
|
};
|
|
|
|
TEST_F(WvCdmRequestLicenseRollbackTest, Streaming_ExpireAfterRollback) {
|
|
Unprovision();
|
|
Provision();
|
|
|
|
ASSERT_EQ(wvcdm::NO_ERROR, decryptor_->OpenSession(
|
|
config_.key_system(), nullptr,
|
|
kDefaultCdmIdentifier, nullptr, &session_id_));
|
|
GenerateKeyRequest(init_data_with_expiry_, kLicenseTypeStreaming);
|
|
VerifyKeyRequestResponse(config_.license_server(), config_.client_auth());
|
|
|
|
// Verify that we can decrypt a subsample to begin with.
|
|
EXPECT_EQ(wvcdm::NO_ERROR, Decrypt(session_id_));
|
|
|
|
RollbackSystemTime(kExpirationWithWindowMs_);
|
|
|
|
// Elapse time so that the key should now be considered expired.
|
|
std::this_thread::sleep_for(
|
|
std::chrono::milliseconds(kExpirationWithWindowMs_));
|
|
|
|
// Verify that we can no longer decrypt a subsample due to key expiration.
|
|
EXPECT_EQ(NEED_KEY, Decrypt(session_id_));
|
|
|
|
RestoreSystemTime();
|
|
|
|
ASSERT_EQ(wvcdm::NO_ERROR, decryptor_->CloseSession(session_id_));
|
|
}
|
|
|
|
TEST_F(WvCdmRequestLicenseRollbackTest, Streaming_ExpireBeforeRollback) {
|
|
Unprovision();
|
|
Provision();
|
|
|
|
ASSERT_EQ(wvcdm::NO_ERROR, decryptor_->OpenSession(
|
|
config_.key_system(), nullptr,
|
|
kDefaultCdmIdentifier, nullptr, &session_id_));
|
|
GenerateKeyRequest(init_data_with_expiry_, kLicenseTypeStreaming);
|
|
VerifyKeyRequestResponse(config_.license_server(), config_.client_auth());
|
|
|
|
// Start playback timer.
|
|
EXPECT_EQ(wvcdm::NO_ERROR, Decrypt(session_id_));
|
|
|
|
// Elapse time so that the key should now be considered expired.
|
|
std::this_thread::sleep_for(
|
|
std::chrono::milliseconds(kExpirationWithWindowMs_));
|
|
|
|
EXPECT_EQ(NEED_KEY, Decrypt(session_id_));
|
|
|
|
RollbackSystemTime(kExpirationWithWindowMs_);
|
|
|
|
// Verify that we still can't decrypt even if we rollbacked the clock.
|
|
EXPECT_EQ(NEED_KEY, Decrypt(session_id_));
|
|
|
|
RestoreSystemTime();
|
|
|
|
ASSERT_EQ(wvcdm::NO_ERROR, decryptor_->CloseSession(session_id_));
|
|
}
|
|
|
|
TEST_F(WvCdmRequestLicenseRollbackTest, Offline_RollbackBeforeRestoreKey) {
|
|
Unprovision();
|
|
Provision();
|
|
|
|
std::string unused_key_id;
|
|
std::string client_auth;
|
|
GetOfflineConfiguration(&unused_key_id, &client_auth);
|
|
|
|
ASSERT_EQ(wvcdm::NO_ERROR, decryptor_->OpenSession(
|
|
config_.key_system(), nullptr,
|
|
kDefaultCdmIdentifier, nullptr, &session_id_));
|
|
GenerateKeyRequest(init_data_with_expiry_, kLicenseTypeOffline);
|
|
VerifyKeyRequestResponse(config_.license_server(), client_auth);
|
|
|
|
// Verify that we can decrypt a subsample to begin with.
|
|
EXPECT_EQ(wvcdm::NO_ERROR, Decrypt(session_id_));
|
|
|
|
CdmKeySetId key_set_id = key_set_id_;
|
|
EXPECT_FALSE(key_set_id_.empty());
|
|
decryptor_->CloseSession(session_id_);
|
|
|
|
// This number must be > the time between GenerateKeyRequest and this call.
|
|
RollbackSystemTime(10 * 1000);
|
|
|
|
session_id_.clear();
|
|
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
|
|
nullptr, &session_id_);
|
|
|
|
decryptor_->RestoreKey(session_id_, key_set_id);
|
|
|
|
// Verify we can't decrypt. The license start time is in the future.
|
|
EXPECT_EQ(DECRYPT_NOT_READY, Decrypt(session_id_));
|
|
|
|
RestoreSystemTime();
|
|
|
|
// Sleep for a little bit to account for the execution time of OpenSession and
|
|
// RestoreKey.
|
|
std::this_thread::sleep_for(
|
|
std::chrono::milliseconds(kExpirationTimeMs_ / 2));
|
|
|
|
// Verify we can't decrypt. The playback duration is short, half of the
|
|
// rollback time. The first playback time gets set to the rollback time
|
|
// when the license is restored. The license has expired by the time the
|
|
// clock is restored/rolled forward.
|
|
EXPECT_EQ(NEED_KEY, Decrypt(session_id_));
|
|
|
|
ASSERT_EQ(wvcdm::NO_ERROR, decryptor_->CloseSession(session_id_));
|
|
}
|
|
|
|
// The difference between this test and Offline_RollbackBeforeRestoreKey is
|
|
// that this test has a 15 second expiration window.
|
|
// Offline_RollbackBeforeRestoreKey has a 5 second window. The rollback
|
|
// before restore causes the expiration timer to run from the rollback time.
|
|
// The license in Offline_RollbackBeforeRestoreKey is expired by the time
|
|
// the rollback is undone, while this one has not yet expired.
|
|
TEST_F(WvCdmRequestLicenseRollbackTest,
|
|
Offline_LongerDurationRollbackBeforeRestoreKey) {
|
|
Unprovision();
|
|
Provision();
|
|
|
|
// The default offline asset is "offline_clip2". Substitute '2' for '8'.
|
|
// "offline_clip8" has a 15 second expiration.
|
|
std::string key_id;
|
|
std::string client_auth;
|
|
GetOfflineConfiguration(&key_id, &client_auth);
|
|
key_id[key_id.size() - 1] = '8';
|
|
|
|
ASSERT_EQ(wvcdm::NO_ERROR, decryptor_->OpenSession(
|
|
config_.key_system(), nullptr,
|
|
kDefaultCdmIdentifier, nullptr, &session_id_));
|
|
GenerateKeyRequest(key_id, kLicenseTypeOffline);
|
|
VerifyKeyRequestResponse(config_.license_server(), client_auth);
|
|
|
|
// Verify that we can decrypt a subsample to begin with.
|
|
EXPECT_EQ(wvcdm::NO_ERROR, Decrypt(session_id_));
|
|
|
|
CdmKeySetId key_set_id = key_set_id_;
|
|
EXPECT_FALSE(key_set_id_.empty());
|
|
decryptor_->CloseSession(session_id_);
|
|
|
|
// This number must be > the time between GenerateKeyRequest and this call.
|
|
RollbackSystemTime(10 * 1000);
|
|
|
|
session_id_.clear();
|
|
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
|
|
nullptr, &session_id_);
|
|
|
|
decryptor_->RestoreKey(session_id_, key_set_id);
|
|
|
|
// Verify we can't decrypt. The license start time is in the future.
|
|
EXPECT_EQ(DECRYPT_NOT_READY, Decrypt(session_id_));
|
|
|
|
RestoreSystemTime();
|
|
|
|
// Sleep for a little bit to account for the execution time of OpenSession and
|
|
// RestoreKey.
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(kExpirationWindowMs_));
|
|
|
|
// Verify we can decrypt.
|
|
EXPECT_EQ(wvcdm::NO_ERROR, Decrypt(session_id_));
|
|
|
|
// Sleep for a while more.
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(kExpirationTimeMs_));
|
|
|
|
// Rollback time + kExpirationWindowMs_ + kExpirationTimeMs_ (~17s) will have
|
|
// elapsed. This is greater than the expiration window for longer duration
|
|
// licenses (15s). The license should have expired.
|
|
EXPECT_EQ(NEED_KEY, Decrypt(session_id_));
|
|
|
|
ASSERT_EQ(wvcdm::NO_ERROR, decryptor_->CloseSession(session_id_));
|
|
}
|
|
|
|
TEST_F(WvCdmRequestLicenseRollbackTest,
|
|
Offline_RollbackAndExpireAfterRestoreKey) {
|
|
Unprovision();
|
|
Provision();
|
|
|
|
std::string unused_key_id;
|
|
std::string client_auth;
|
|
GetOfflineConfiguration(&unused_key_id, &client_auth);
|
|
|
|
ASSERT_EQ(wvcdm::NO_ERROR, decryptor_->OpenSession(
|
|
config_.key_system(), nullptr,
|
|
kDefaultCdmIdentifier, nullptr, &session_id_));
|
|
GenerateKeyRequest(init_data_with_expiry_, kLicenseTypeOffline);
|
|
VerifyKeyRequestResponse(config_.license_server(), client_auth);
|
|
|
|
CdmKeySetId key_set_id = key_set_id_;
|
|
EXPECT_FALSE(key_set_id_.empty());
|
|
decryptor_->CloseSession(session_id_);
|
|
|
|
session_id_.clear();
|
|
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
|
|
nullptr, &session_id_);
|
|
EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_->RestoreKey(session_id_, key_set_id));
|
|
|
|
// Start playback timer.
|
|
EXPECT_EQ(wvcdm::NO_ERROR, Decrypt(session_id_));
|
|
|
|
RollbackSystemTime(kExpirationWithWindowMs_);
|
|
|
|
// Elapse time so that the key should now be considered expired.
|
|
std::this_thread::sleep_for(
|
|
std::chrono::milliseconds(kExpirationWithWindowMs_));
|
|
|
|
// Verify that we can no longer decrypt a subsample due to key expiration.
|
|
EXPECT_EQ(NEED_KEY, Decrypt(session_id_));
|
|
|
|
RestoreSystemTime();
|
|
|
|
EXPECT_EQ(NEED_KEY, Decrypt(session_id_));
|
|
|
|
ASSERT_EQ(wvcdm::NO_ERROR, decryptor_->CloseSession(session_id_));
|
|
}
|
|
|
|
TEST_F(WvCdmRequestLicenseRollbackTest,
|
|
Offline_ExpireAndRollbackAfterRestoreKey) {
|
|
Unprovision();
|
|
Provision();
|
|
|
|
std::string unused_key_id;
|
|
std::string client_auth;
|
|
GetOfflineConfiguration(&unused_key_id, &client_auth);
|
|
|
|
ASSERT_EQ(wvcdm::NO_ERROR, decryptor_->OpenSession(
|
|
config_.key_system(), nullptr,
|
|
kDefaultCdmIdentifier, nullptr, &session_id_));
|
|
GenerateKeyRequest(init_data_with_expiry_, kLicenseTypeOffline);
|
|
VerifyKeyRequestResponse(config_.license_server(), client_auth);
|
|
|
|
CdmKeySetId key_set_id = key_set_id_;
|
|
EXPECT_FALSE(key_set_id_.empty());
|
|
decryptor_->CloseSession(session_id_);
|
|
|
|
session_id_.clear();
|
|
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
|
|
nullptr, &session_id_);
|
|
EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_->RestoreKey(session_id_, key_set_id));
|
|
|
|
// Start playback timer.
|
|
EXPECT_EQ(wvcdm::NO_ERROR, Decrypt(session_id_));
|
|
|
|
// Elapse time so that the key should now be considered expired.
|
|
std::this_thread::sleep_for(
|
|
std::chrono::milliseconds(kExpirationWithWindowMs_));
|
|
|
|
// Verify that we can no longer decrypt a subsample due to key expiration.
|
|
EXPECT_EQ(NEED_KEY, Decrypt(session_id_));
|
|
|
|
RollbackSystemTime(kExpirationWithWindowMs_);
|
|
|
|
EXPECT_EQ(NEED_KEY, Decrypt(session_id_));
|
|
|
|
RestoreSystemTime();
|
|
|
|
ASSERT_EQ(wvcdm::NO_ERROR, decryptor_->CloseSession(session_id_));
|
|
}
|
|
|
|
} // namespace wvcdm
|