// Copyright 2020 Google LLC. All Rights Reserved. #include "api/license_test_helper.h" #include #include "base/logging.h" #include "cdm/keys/certs.h" #include "cdm/protos/license_protocol.pb.h" #include "crypto_utils/aes_cbc_encryptor.h" #include "crypto_utils/crypto_util.h" namespace widevine { namespace { struct KeyData { video_widevine::License_KeyContainer_SecurityLevel level; std::vector key_id; std::vector iv; std::vector content_key; }; // clang-format off KeyData SoftwareCryptoKey() { return { video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_CRYPTO, { // key_id: '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', }, { // iv: 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, }, { // content_key: 0xeb, 0xdd, 0x62, 0xf1, 0x68, 0x14, 0xd2, 0x7b, 0x68, 0xef, 0x12, 0x2a, 0xfc, 0xe4, 0xae, 0x3c, } }; } KeyData SoftwareDecodeKey() { return { video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_DECODE, { // key_id: '9', '8', '7', '6', '5', '4', '3', '2', '1', '0', '9', '8', '7', '6', '5', '4', }, { // iv: 0x17, 0x18, 0x19, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x30, 0x31, 0x32, }, { // content_key: 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, } }; } KeyData HardwareCryptoKey() { return { video_widevine::License_KeyContainer_SecurityLevel_HW_SECURE_CRYPTO, { // key_id: 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', }, { // iv: 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, }, { // content_key: 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, } }; } // clang-format on // Returns |buffer| converted into a string. std::string AsString(const uint8_t* buffer, size_t buffer_size) { return std::string(reinterpret_cast(buffer), buffer_size); } // Returns the encryption key based on |license_request| and |session_key|. std::string GetEncryptionKey(const std::string& license_request, const std::string& session_key) { return widevine::crypto_util::DeriveKey( session_key, widevine::crypto_util::kWrappingKeyLabel, license_request, widevine::crypto_util::kWrappingKeySizeBits); } std::string AesEncrypt(const std::string& key, const uint8_t* iv, size_t iv_size, const uint8_t* plain_text, size_t plain_text_size) { widevine::AesCbcEncryptor encryptor; encryptor.SetKey(reinterpret_cast(key.data()), key.size()); std::vector cipher_text(plain_text_size); CHECK(encryptor.Encrypt(iv, iv_size, plain_text, plain_text_size, cipher_text.data())); return AsString(cipher_text.data(), cipher_text.size()); } video_widevine::License::KeyContainer CreateContentKeyContainer( const KeyData& key_data, const std::string& key_container_encryption_key) { video_widevine::License::KeyContainer key; key.set_id(key_data.key_id.data(), key_data.key_id.size()); key.set_iv(key_data.iv.data(), key_data.iv.size()); key.set_type(video_widevine::License_KeyContainer_KeyType_CONTENT); key.set_level(key_data.level); key.set_key(AesEncrypt(key_container_encryption_key, key_data.iv.data(), key_data.iv.size(), key_data.content_key.data(), key_data.content_key.size())); return key; } video_widevine::License::KeyContainer CreateSigningKeyContainer( const std::string& key_container_encryption_key) { const std::vector signing_iv = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', }; // 512 bits of key material for dual server/client signing key. const std::vector signing_key = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, }; video_widevine::License::KeyContainer key; key.set_iv(signing_iv.data(), signing_iv.size()); key.set_type(video_widevine::License_KeyContainer_KeyType_SIGNING); key.set_key(AesEncrypt(key_container_encryption_key, signing_iv.data(), signing_iv.size(), signing_key.data(), signing_key.size())); return key; } } // namespace std::vector GetSoftwareCryptoKeyId() { const auto key = SoftwareCryptoKey(); return key.key_id; } std::vector GetSoftwareDecodeKeyId() { const auto key = SoftwareDecodeKey(); return key.key_id; } std::vector GetHardwareCryptoKeyId() { const auto key = HardwareCryptoKey(); return key.key_id; } std::vector GetUnusedKeyId() { return {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; } // Returns a serialized license request with the following data: // { // "requestTime": , // "clientId": { // "clientCapabilities": { // "videoResolutionConstraints": true, // "clientToken": true, // "sessionToken": false, // "maxHdcpVersion": "HDCP_V1" // }, // "clientInfo": [{ // "name": "architecture_name", // "value": "x86-64" // },{ // "name": "company_name", // "value": "Google" // },{ // "name": "model_name", // "value": "ChromeCDM" // },{ // "name": "platform_name", // "value": "Windows" // },{ // "name": "widevine_cdm_version", // "value": "4.10.1686.29" // }], // "type": "DRM_DEVICE_CERTIFICATE", // "token": { // // }" // }", // "contentId": { // "webmKeyId": { // "licenseType": "STREAMING", // "requestId": "REQUEST_ID", // "header": "01234567890123456" // } // }, // "protocolVersion": "VERSION_2_1", // "type": "NEW" // } std::string CreateLicenseRequest() { video_widevine::LicenseRequest license_request; // Request time is now. license_request.set_request_time(std::time(nullptr)); auto* client_id = license_request.mutable_client_id(); auto* capabilities = client_id->mutable_client_capabilities(); capabilities->set_video_resolution_constraints(true); capabilities->set_client_token(true); capabilities->set_session_token(false); capabilities->set_max_hdcp_version( video_widevine::ClientIdentification::ClientCapabilities::HDCP_V1); auto* client_info = client_id->add_client_info(); client_info->set_name("architecture_name"); client_info->set_value("x86-64"); client_info = client_id->add_client_info(); client_info->set_name("company_name"); client_info->set_value("Google"); client_info = client_id->add_client_info(); client_info->set_name("model_name"); client_info->set_value("ChromeCDM"); client_info = client_id->add_client_info(); client_info->set_name("platform_name"); client_info->set_value("Windows"); client_info = client_id->add_client_info(); client_info->set_name("widevine_cdm_version"); client_info->set_value("4.10.1686.29"); client_id->set_type( video_widevine::ClientIdentification_TokenType_DRM_DEVICE_CERTIFICATE); client_id->set_token(wvcdm::kRsaDrmCertificate, wvcdm::kRsaDrmCertificateSize); auto* content_id = license_request.mutable_content_id(); auto* webm_key_id = content_id->mutable_webm_key_id(); webm_key_id->set_license_type(video_widevine::STREAMING); webm_key_id->set_request_id("REQUEST_ID"); webm_key_id->set_header("01234567890123456"); license_request.set_protocol_version(video_widevine::VERSION_2_1); license_request.set_type(video_widevine::LicenseRequest::NEW); return license_request.SerializeAsString(); } // Returns a serialized license using values from |serialized_license_request| // as well as |session_key|. Some of the values match what is in the license // request, but as they are not checked it really doesn't matter. The license // will contain all 3 keys |kKeyId1|, |kKeyId2|, and |kKeyId3| with different // security levels. The license is of the form: // { // "id": { // "requestId": "REQUEST_ID", // "sessionId": "SESSION_ID", // "type": "STREAMING", // "version": 0 // }, // "policy": { // "canPlay": true, // "canPersist": false, // "canRenew": true, // "licenseDurationSeconds": "600", // "renewalDelaySeconds": "30", // "renewalRetryIntervalSeconds": "10", // "renewWithUsage": false // }, // "key": [{ // "iv": <|kInitializationVector|>, // "type": "SIGNING", // "key": // },{ // "id": <|kKeyId1|>, // "iv": <|kInitializationVector|>, // "type": "CONTENT", // "level": "SW_SECURE_CRYPTO", // "key": <|kContentKey1|> // },{ // "id": <|kKeyId2|>, // "iv": <|kInitializationVector|>, // "type": "CONTENT", // "level": "SW_SECURE_DECODE", // "key": <|kContentKey2|> // },{ // "id": <|kKeyId3|>, // "iv": <|kInitializationVector|>, // "type": "CONTENT", // "level": "HW_SECURE_CRYPTO", // "key": <|kContentKey3|> // }], // "licenseStartTime": , // "remoteAttestationVerified": false, // "platformVerificationStatus": "PLATFORM_UNVERIFIED" // } std::string CreateLicense(const std::string& serialized_license_request, const std::string& session_key) { DCHECK_EQ(session_key.size(), 16u); // Extract values needed for the license. video_widevine::LicenseRequest license_request; CHECK(license_request.ParseFromString(serialized_license_request)); const auto request_time = license_request.request_time(); const std::string key_container_encryption_key = GetEncryptionKey(serialized_license_request, session_key); // Now create the license. video_widevine::License license; auto* license_id = license.mutable_id(); license_id->set_request_id("REQUEST_ID"); license_id->set_session_id("SESSION_ID"); license_id->set_type(video_widevine::STREAMING); license_id->set_version(0); auto* policy = license.mutable_policy(); policy->set_can_play(true); policy->set_can_persist(false); policy->set_can_renew(true); policy->set_license_duration_seconds(600); policy->set_renewal_delay_seconds(30); policy->set_renewal_retry_interval_seconds(10); policy->set_renew_with_usage(false); license.add_key()->CopyFrom( CreateSigningKeyContainer(key_container_encryption_key)); license.add_key()->CopyFrom(CreateContentKeyContainer( SoftwareCryptoKey(), key_container_encryption_key)); license.add_key()->CopyFrom(CreateContentKeyContainer( SoftwareDecodeKey(), key_container_encryption_key)); license.add_key()->CopyFrom(CreateContentKeyContainer( HardwareCryptoKey(), key_container_encryption_key)); license.set_license_start_time(request_time); license.set_remote_attestation_verified(false); license.set_platform_verification_status( video_widevine::PlatformVerificationStatus::PLATFORM_UNVERIFIED); return license.SerializeAsString(); } } // namespace widevine