ODK and Shared Libraries
In this code drop we introduce the ODK dependency. The reference implementation has been updated to make use of the ODK and the related tests have been included. In addition, we have included an example of how a shared libraries can be created. This will allow make it easier to test and verify different implementations of the API. Most other changes introduce by this code drop were made to clean-up the reference implementation and limit dependencies.
This commit is contained in:
14
WORKSPACE
14
WORKSPACE
@@ -3,6 +3,7 @@
|
||||
workspace(name = "whitebox")
|
||||
|
||||
load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
|
||||
load("@bazel_tools//tools/build_defs/repo:git.bzl", "new_git_repository")
|
||||
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
|
||||
|
||||
git_repository(
|
||||
@@ -60,6 +61,14 @@ http_archive(
|
||||
urls = ["https://zlib.net/zlib-1.2.11.tar.gz"],
|
||||
)
|
||||
|
||||
# ODK
|
||||
new_git_repository(
|
||||
name = "odk_repo",
|
||||
build_file = "//external:odk.BUILD",
|
||||
commit = "bee799748752fac84d9c3ecd549aa54f72c88d02",
|
||||
remote = "https://widevine-partner.googlesource.com/oemcrypto_core_message.git",
|
||||
)
|
||||
|
||||
bind(
|
||||
name = "glog",
|
||||
actual = "@glog_repo//:glog",
|
||||
@@ -89,3 +98,8 @@ bind(
|
||||
name = "protobuf",
|
||||
actual = "@com_google_protobuf//:protobuf",
|
||||
)
|
||||
|
||||
bind(
|
||||
name = "odk",
|
||||
actual = "@odk_repo//:odk",
|
||||
)
|
||||
|
||||
26
api/BUILD
26
api/BUILD
@@ -10,6 +10,12 @@ cc_library(
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "export",
|
||||
hdrs = ["export.h"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "aead_whitebox",
|
||||
hdrs = [
|
||||
@@ -17,6 +23,7 @@ cc_library(
|
||||
],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
":export",
|
||||
":result",
|
||||
],
|
||||
)
|
||||
@@ -28,6 +35,7 @@ cc_library(
|
||||
],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
":export",
|
||||
":result",
|
||||
],
|
||||
)
|
||||
@@ -67,6 +75,7 @@ cc_library(
|
||||
"//crypto_utils:aes_cbc_encryptor",
|
||||
"//crypto_utils:crypto_util",
|
||||
"//crypto_utils:rsa_key",
|
||||
"//external:odk",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -135,6 +144,23 @@ cc_library(
|
||||
],
|
||||
)
|
||||
|
||||
# TODO(jrummell): Move sources into "license_whitebox_test" once all
|
||||
# implementations support core_message.
|
||||
cc_library(
|
||||
name = "license_whitebox_core_message_test",
|
||||
testonly = True,
|
||||
srcs = [
|
||||
"license_whitebox_process_license_response_core_message_test.cc",
|
||||
],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
":license_whitebox",
|
||||
":license_whitebox_test",
|
||||
":test_license_builder",
|
||||
"//chromium_deps/testing",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "license_whitebox_benchmark",
|
||||
testonly = True,
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "api/export.h"
|
||||
#include "api/result.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -42,11 +43,11 @@ typedef struct WB_Aead_Whitebox WB_Aead_Whitebox;
|
||||
// |context| was null, if |context_size| was zero, or if |whitebox| was null.
|
||||
//
|
||||
// WB_RESULT_OUT_OF_MEMORY if the necessary memory could not be allocated.
|
||||
WB_Result WB_Aead_Create(const uint8_t* whitebox_init_data,
|
||||
size_t whitebox_init_data_size,
|
||||
const uint8_t* context,
|
||||
size_t context_size,
|
||||
WB_Aead_Whitebox** whitebox);
|
||||
WB_API WB_Result WB_Aead_Create(const uint8_t* whitebox_init_data,
|
||||
size_t whitebox_init_data_size,
|
||||
const uint8_t* context,
|
||||
size_t context_size,
|
||||
WB_Aead_Whitebox** whitebox);
|
||||
|
||||
// Releases all resources used by the white-box instance pointed to by
|
||||
// |whitebox|.
|
||||
@@ -54,7 +55,7 @@ WB_Result WB_Aead_Create(const uint8_t* whitebox_init_data,
|
||||
// Args:
|
||||
// whitebox (in) : A pointer to a white-box instance. Passing in null will
|
||||
// result in a no-op.
|
||||
void WB_Aead_Delete(WB_Aead_Whitebox* whitebox);
|
||||
WB_API void WB_Aead_Delete(WB_Aead_Whitebox* whitebox);
|
||||
|
||||
// Encrypts |input_data| and writes the cipher data, nonce and the data
|
||||
// verification tag to |output_data|. The implementation should generate and use
|
||||
@@ -85,11 +86,11 @@ void WB_Aead_Delete(WB_Aead_Whitebox* whitebox);
|
||||
//
|
||||
// WB_RESULT_BUFFER_TOO_SMALL if |output_data_size| (as input) was less than
|
||||
// the required size.
|
||||
WB_Result WB_Aead_Encrypt(const WB_Aead_Whitebox* whitebox,
|
||||
const uint8_t* input_data,
|
||||
size_t input_data_size,
|
||||
uint8_t* output_data,
|
||||
size_t* output_data_size);
|
||||
WB_API WB_Result WB_Aead_Encrypt(const WB_Aead_Whitebox* whitebox,
|
||||
const uint8_t* input_data,
|
||||
size_t input_data_size,
|
||||
uint8_t* output_data,
|
||||
size_t* output_data_size);
|
||||
|
||||
// Decrypts |input_data| and writes the plaintext to |output_data|. |input_data|
|
||||
// must have been encrypted using WB_Aead_Encrypt() with the same |whitebox|.
|
||||
@@ -119,11 +120,11 @@ WB_Result WB_Aead_Encrypt(const WB_Aead_Whitebox* whitebox,
|
||||
//
|
||||
// WB_RESULT_DATA_VERIFICATION_ERROR if |input_data| failed data verification.
|
||||
// The state of |output_data| is undefined.
|
||||
WB_Result WB_Aead_Decrypt(const WB_Aead_Whitebox* whitebox,
|
||||
const uint8_t* input_data,
|
||||
size_t input_data_size,
|
||||
uint8_t* output_data,
|
||||
size_t* output_data_size);
|
||||
WB_API WB_Result WB_Aead_Decrypt(const WB_Aead_Whitebox* whitebox,
|
||||
const uint8_t* input_data,
|
||||
size_t input_data_size,
|
||||
uint8_t* output_data,
|
||||
size_t* output_data_size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
12
api/export.h
Normal file
12
api/export.h
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
#ifndef WHITEBOX_API_EXPORT_H_
|
||||
#define WHITEBOX_API_EXPORT_H_
|
||||
|
||||
#if defined(_WIN32)
|
||||
#define WB_API _declspec(dllexport)
|
||||
#else
|
||||
#define WB_API __attribute__((visibility("default")))
|
||||
#endif
|
||||
|
||||
#endif // WHITEBOX_API_EXPORT_H_
|
||||
@@ -1,345 +0,0 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
#include "api/license_test_helper.h"
|
||||
|
||||
#include <ctime>
|
||||
|
||||
#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<uint8_t> key_id;
|
||||
std::vector<uint8_t> iv;
|
||||
std::vector<uint8_t> 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<const char*>(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<const uint8_t*>(key.data()), key.size());
|
||||
std::vector<uint8_t> 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<uint8_t> 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<uint8_t> 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<uint8_t> GetSoftwareCryptoKeyId() {
|
||||
const auto key = SoftwareCryptoKey();
|
||||
return key.key_id;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> GetSoftwareDecodeKeyId() {
|
||||
const auto key = SoftwareDecodeKey();
|
||||
return key.key_id;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> GetHardwareCryptoKeyId() {
|
||||
const auto key = HardwareCryptoKey();
|
||||
return key.key_id;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> 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": <current time>,
|
||||
// "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": {
|
||||
// <test device certificate>
|
||||
// }"
|
||||
// }",
|
||||
// "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": <computed>
|
||||
// },{
|
||||
// "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": <from license_request.requestTime>,
|
||||
// "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
|
||||
@@ -1,33 +0,0 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
#ifndef WHITEBOX_API_LICENSE_TEST_HELPER_H_
|
||||
#define WHITEBOX_API_LICENSE_TEST_HELPER_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace widevine {
|
||||
|
||||
// Returns a serialized LicenseRequest.
|
||||
std::string CreateLicenseRequest();
|
||||
|
||||
// Returns a serialized License based on |serialized_license_request| and
|
||||
// |session_key|. The license contains 3 content keys (key1, key2, key3)
|
||||
// with levels SW_SECURE_CRYPTO, SW_SECURE_DECODE, and HW_SECURE_CRYPTO,
|
||||
// respectively.
|
||||
std::string CreateLicense(const std::string& serialized_license_request,
|
||||
const std::string& session_key);
|
||||
|
||||
// Returns the key ID for each key.
|
||||
std::vector<uint8_t> GetSoftwareCryptoKeyId();
|
||||
std::vector<uint8_t> GetSoftwareDecodeKeyId();
|
||||
std::vector<uint8_t> GetHardwareCryptoKeyId();
|
||||
|
||||
// Returns a key ID which is not included in the license.
|
||||
std::vector<uint8_t> GetUnusedKeyId();
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
#endif // WHITEBOX_API_LICENSE_TEST_HELPER_H_
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "api/export.h"
|
||||
#include "api/result.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -38,9 +39,9 @@ typedef enum {
|
||||
// WB_RESULT_INVALID_PARAMETER if |whitebox_init_data| was null or invalid.
|
||||
//
|
||||
// WB_RESULT_OUT_OF_MEMORY if the necessary memory could not be allocated.
|
||||
WB_Result WB_License_Create(const uint8_t* whitebox_init_data,
|
||||
size_t whitebox_init_data_size,
|
||||
WB_License_Whitebox** whitebox);
|
||||
WB_API WB_Result WB_License_Create(const uint8_t* whitebox_init_data,
|
||||
size_t whitebox_init_data_size,
|
||||
WB_License_Whitebox** whitebox);
|
||||
|
||||
// Releases all resources used by the white-box instance pointed to by
|
||||
// |whitebox|.
|
||||
@@ -48,7 +49,7 @@ WB_Result WB_License_Create(const uint8_t* whitebox_init_data,
|
||||
// Args:
|
||||
// whitebox (in) : A pointer to a white-box instance. Passing in null will
|
||||
// result in a no-op.
|
||||
void WB_License_Delete(WB_License_Whitebox* whitebox);
|
||||
WB_API void WB_License_Delete(WB_License_Whitebox* whitebox);
|
||||
|
||||
// Signs a license request using the CDM's private signing key.
|
||||
//
|
||||
@@ -74,11 +75,12 @@ void WB_License_Delete(WB_License_Whitebox* whitebox);
|
||||
//
|
||||
// WB_RESULT_BUFFER_TOO_SMALL if |signature_size| (as input) was less than the
|
||||
// required size.
|
||||
WB_Result WB_License_SignLicenseRequest(const WB_License_Whitebox* whitebox,
|
||||
const uint8_t* license_request,
|
||||
size_t license_request_size,
|
||||
uint8_t* signature,
|
||||
size_t* signature_size);
|
||||
WB_API WB_Result
|
||||
WB_License_SignLicenseRequest(const WB_License_Whitebox* whitebox,
|
||||
const uint8_t* license_request,
|
||||
size_t license_request_size,
|
||||
uint8_t* signature,
|
||||
size_t* signature_size);
|
||||
|
||||
// Verifies a license response using HMAC and the server signing key.
|
||||
//
|
||||
@@ -93,6 +95,13 @@ WB_Result WB_License_SignLicenseRequest(const WB_License_Whitebox* whitebox,
|
||||
// Args:
|
||||
// whitebox (in/out) : The white-box instance that will load the keys.
|
||||
//
|
||||
// core_message (in) : Serialized information communicating the structure of
|
||||
// |message|. Signature verification should be done on |core_message| +
|
||||
// |message|.
|
||||
//
|
||||
// core_message_size (in) : The number of bytes in |core_message|. If this is
|
||||
// zero, it means that there was no meta message provided for the message.
|
||||
//
|
||||
// message (in) : The message field of the license response.
|
||||
//
|
||||
// message_size (in) : The number of bytes in |message|.
|
||||
@@ -114,26 +123,30 @@ WB_Result WB_License_SignLicenseRequest(const WB_License_Whitebox* whitebox,
|
||||
// WB_RESULT_OK if the response was verified and the keys were loaded into
|
||||
// |whitebox|.
|
||||
//
|
||||
// WB_RESULT_INVALID_PARAMETER if |whitebox| was null, if |message| was null,
|
||||
// if |message_size| was zero, if |message| did not conform to the expected
|
||||
// format, if |signature| was null, if |signature_size| was incorrect, if
|
||||
// |session_key| was null, if |session_key_size| was incorrect, if
|
||||
// |session_key| could not be unwrapped correctly, if |license_request| was
|
||||
// null, or if |license_request_size| was zero.
|
||||
// WB_RESULT_INVALID_PARAMETER if |whitebox| was null, if |core_message| was
|
||||
// null, if |message| was null, if |message_size| was zero, if |message| did
|
||||
// not conform to the expected format, if |signature| was null, if
|
||||
// |signature_size| was incorrect, if |session_key| was null, if
|
||||
// |session_key_size| was incorrect, if |session_key| could not be unwrapped
|
||||
// correctly, if |license_request| was null, or if |license_request_size| was
|
||||
// zero.
|
||||
//
|
||||
// WB_RESULT_INVALID_SIGNATURE if |message|'s signature does not match
|
||||
// |signature|.
|
||||
//
|
||||
// WB_RESULT_INVALID_STATE if a license has already been loaded.
|
||||
WB_Result WB_License_ProcessLicenseResponse(WB_License_Whitebox* whitebox,
|
||||
const uint8_t* message,
|
||||
size_t message_size,
|
||||
const uint8_t* signature,
|
||||
size_t signature_size,
|
||||
const uint8_t* session_key,
|
||||
size_t session_key_size,
|
||||
const uint8_t* license_request,
|
||||
size_t license_request_size);
|
||||
WB_API WB_Result
|
||||
WB_License_ProcessLicenseResponse(WB_License_Whitebox* whitebox,
|
||||
const uint8_t* core_message,
|
||||
size_t core_message_size,
|
||||
const uint8_t* message,
|
||||
size_t message_size,
|
||||
const uint8_t* signature,
|
||||
size_t signature_size,
|
||||
const uint8_t* session_key,
|
||||
size_t session_key_size,
|
||||
const uint8_t* license_request,
|
||||
size_t license_request_size);
|
||||
|
||||
// Signs |message| and return the signature via |signature| using HMAC and the
|
||||
// client renewal signing key
|
||||
@@ -162,11 +175,12 @@ WB_Result WB_License_ProcessLicenseResponse(WB_License_Whitebox* whitebox,
|
||||
// required size.
|
||||
//
|
||||
// WB_RESULT_INVALID_STATE if |whitebox| had no signing keys.
|
||||
WB_Result WB_License_SignRenewalRequest(const WB_License_Whitebox* whitebox,
|
||||
const uint8_t* message,
|
||||
size_t message_size,
|
||||
uint8_t* signature,
|
||||
size_t* signature_size);
|
||||
WB_API WB_Result
|
||||
WB_License_SignRenewalRequest(const WB_License_Whitebox* whitebox,
|
||||
const uint8_t* message,
|
||||
size_t message_size,
|
||||
uint8_t* signature,
|
||||
size_t* signature_size);
|
||||
|
||||
// Verifies the renewal response using HMAC and the server signing key.
|
||||
//
|
||||
@@ -192,11 +206,12 @@ WB_Result WB_License_SignRenewalRequest(const WB_License_Whitebox* whitebox,
|
||||
// |signature|.
|
||||
//
|
||||
// WB_RESULT_INVALID_STATE if |whitebox| had not loaded a license.
|
||||
WB_Result WB_License_VerifyRenewalResponse(const WB_License_Whitebox* whitebox,
|
||||
const uint8_t* message,
|
||||
size_t message_size,
|
||||
const uint8_t* signature,
|
||||
size_t signature_size);
|
||||
WB_API WB_Result
|
||||
WB_License_VerifyRenewalResponse(const WB_License_Whitebox* whitebox,
|
||||
const uint8_t* message,
|
||||
size_t message_size,
|
||||
const uint8_t* signature,
|
||||
size_t signature_size);
|
||||
|
||||
// Gets the secret string needed by WB_License_Unmask() in order to unmask the
|
||||
// masked decrypted content returned by WB_License_MaskedDecrypt().
|
||||
@@ -240,12 +255,12 @@ WB_Result WB_License_VerifyRenewalResponse(const WB_License_Whitebox* whitebox,
|
||||
// the required size.
|
||||
//
|
||||
// WB_RESULT_INVALID_STATE if |whitebox| had not loaded a license.
|
||||
WB_Result WB_License_GetSecretString(const WB_License_Whitebox* whitebox,
|
||||
WB_CipherMode mode,
|
||||
const uint8_t* key_id,
|
||||
size_t key_id_size,
|
||||
uint8_t* secret_string,
|
||||
size_t* secret_string_size);
|
||||
WB_API WB_Result WB_License_GetSecretString(const WB_License_Whitebox* whitebox,
|
||||
WB_CipherMode mode,
|
||||
const uint8_t* key_id,
|
||||
size_t key_id_size,
|
||||
uint8_t* secret_string,
|
||||
size_t* secret_string_size);
|
||||
|
||||
// Decrypts |input_data| and writes the plaintext to |output_data|.
|
||||
//
|
||||
@@ -295,16 +310,16 @@ WB_Result WB_License_GetSecretString(const WB_License_Whitebox* whitebox,
|
||||
// the required size.
|
||||
//
|
||||
// WB_RESULT_INVALID_STATE if |whitebox| had not loaded a license.
|
||||
WB_Result WB_License_Decrypt(const WB_License_Whitebox* whitebox,
|
||||
WB_CipherMode mode,
|
||||
const uint8_t* key_id,
|
||||
size_t key_id_size,
|
||||
const uint8_t* input_data,
|
||||
size_t input_data_size,
|
||||
const uint8_t* iv,
|
||||
size_t iv_size,
|
||||
uint8_t* output_data,
|
||||
size_t* output_data_size);
|
||||
WB_API WB_Result WB_License_Decrypt(const WB_License_Whitebox* whitebox,
|
||||
WB_CipherMode mode,
|
||||
const uint8_t* key_id,
|
||||
size_t key_id_size,
|
||||
const uint8_t* input_data,
|
||||
size_t input_data_size,
|
||||
const uint8_t* iv,
|
||||
size_t iv_size,
|
||||
uint8_t* output_data,
|
||||
size_t* output_data_size);
|
||||
|
||||
// Decrypts |input_data| and write the obfuscated plaintext to
|
||||
// |masked_output_data|. The obfuscated plaintext can be deobfuscated using
|
||||
@@ -359,16 +374,16 @@ WB_Result WB_License_Decrypt(const WB_License_Whitebox* whitebox,
|
||||
// than the required size.
|
||||
//
|
||||
// WB_RESULT_INVALID_STATE if |whitebox| had not loaded a license.
|
||||
WB_Result WB_License_MaskedDecrypt(const WB_License_Whitebox* whitebox,
|
||||
WB_CipherMode mode,
|
||||
const uint8_t* key_id,
|
||||
size_t key_id_size,
|
||||
const uint8_t* input_data,
|
||||
size_t input_data_size,
|
||||
const uint8_t* iv,
|
||||
size_t iv_size,
|
||||
uint8_t* masked_output_data,
|
||||
size_t* masked_output_data_size);
|
||||
WB_API WB_Result WB_License_MaskedDecrypt(const WB_License_Whitebox* whitebox,
|
||||
WB_CipherMode mode,
|
||||
const uint8_t* key_id,
|
||||
size_t key_id_size,
|
||||
const uint8_t* input_data,
|
||||
size_t input_data_size,
|
||||
const uint8_t* iv,
|
||||
size_t iv_size,
|
||||
uint8_t* masked_output_data,
|
||||
size_t* masked_output_data_size);
|
||||
|
||||
// Unmasks a subset of the data in |masked_data| using |secret_string| and
|
||||
// writes it to |unmasked_data|.
|
||||
@@ -380,6 +395,9 @@ WB_Result WB_License_MaskedDecrypt(const WB_License_Whitebox* whitebox,
|
||||
// It is assumed that indexes between 0 and |size - 1| (inclusive) are all valid
|
||||
// indexes into |unmasked_data|.
|
||||
//
|
||||
// The memory range used for |masked_data| must not overlap with
|
||||
// |unmasked_data|.
|
||||
//
|
||||
// Args:
|
||||
// masked_data (in) : The masked data to read from.
|
||||
//
|
||||
@@ -393,12 +411,12 @@ WB_Result WB_License_MaskedDecrypt(const WB_License_Whitebox* whitebox,
|
||||
// secret_string_size (in) : The number of bytes in |secret_string|.
|
||||
//
|
||||
// unmasked_data (out) : The output buffer to write the unmasked data to.
|
||||
void WB_License_Unmask(const uint8_t* masked_data,
|
||||
size_t offset,
|
||||
size_t size,
|
||||
const uint8_t* secret_string,
|
||||
size_t secret_string_size,
|
||||
uint8_t* unmasked_data);
|
||||
WB_API void WB_License_Unmask(const uint8_t* masked_data,
|
||||
size_t offset,
|
||||
size_t size,
|
||||
const uint8_t* secret_string,
|
||||
size_t secret_string_size,
|
||||
uint8_t* unmasked_data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -72,12 +72,14 @@ class LicenseWhiteboxChromeOSTest
|
||||
License license;
|
||||
builder.Build(*public_key_, &license);
|
||||
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license.message.data(), license.message.size(),
|
||||
license.signature.data(), license.signature.size(),
|
||||
license.session_key.data(), license.session_key.size(),
|
||||
license.request.data(), license.request.size()),
|
||||
WB_RESULT_OK);
|
||||
ASSERT_EQ(
|
||||
WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license.core_message.data(), license.core_message.size(),
|
||||
license.message.data(), license.message.size(),
|
||||
license.signature.data(), license.signature.size(),
|
||||
license.session_key.data(), license.session_key.size(),
|
||||
license.request.data(), license.request.size()),
|
||||
WB_RESULT_OK);
|
||||
}
|
||||
|
||||
// This is the strictest level of enforcement. It will be the last one
|
||||
|
||||
@@ -50,12 +50,14 @@ class LicenseWhiteboxDecryptBenchmark
|
||||
WB_RESULT_OK);
|
||||
|
||||
const auto license = CreateLicense();
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license.message.data(), license.message.size(),
|
||||
license.signature.data(), license.signature.size(),
|
||||
license.session_key.data(), license.session_key.size(),
|
||||
license.request.data(), license.request.size()),
|
||||
WB_RESULT_OK);
|
||||
ASSERT_EQ(
|
||||
WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license.core_message.data(), license.core_message.size(),
|
||||
license.message.data(), license.message.size(),
|
||||
license.signature.data(), license.signature.size(),
|
||||
license.session_key.data(), license.session_key.size(),
|
||||
license.request.data(), license.request.size()),
|
||||
WB_RESULT_OK);
|
||||
}
|
||||
|
||||
void TearDown() override { WB_License_Delete(whitebox_); }
|
||||
|
||||
@@ -54,12 +54,14 @@ class LicenseWhiteboxDecryptTest : public LicenseWhiteboxTestBase {
|
||||
License license;
|
||||
builder.Build(*public_key_, &license);
|
||||
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license.message.data(), license.message.size(),
|
||||
license.signature.data(), license.signature.size(),
|
||||
license.session_key.data(), license.session_key.size(),
|
||||
license.request.data(), license.request.size()),
|
||||
WB_RESULT_OK);
|
||||
ASSERT_EQ(
|
||||
WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license.core_message.data(), license.core_message.size(),
|
||||
license.message.data(), license.message.size(),
|
||||
license.signature.data(), license.signature.size(),
|
||||
license.session_key.data(), license.session_key.size(),
|
||||
license.request.data(), license.request.size()),
|
||||
WB_RESULT_OK);
|
||||
}
|
||||
|
||||
// We need two special keys for this test, one that will be used for a
|
||||
|
||||
@@ -48,12 +48,14 @@ class LicenseWhiteboxGetSecretStringTest : public LicenseWhiteboxTestBase {
|
||||
License license;
|
||||
builder.Build(*public_key_, &license);
|
||||
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license.message.data(), license.message.size(),
|
||||
license.signature.data(), license.signature.size(),
|
||||
license.session_key.data(), license.session_key.size(),
|
||||
license.request.data(), license.request.size()),
|
||||
WB_RESULT_OK);
|
||||
ASSERT_EQ(
|
||||
WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license.core_message.data(), license.core_message.size(),
|
||||
license.message.data(), license.message.size(),
|
||||
license.signature.data(), license.signature.size(),
|
||||
license.session_key.data(), license.session_key.size(),
|
||||
license.request.data(), license.request.size()),
|
||||
WB_RESULT_OK);
|
||||
}
|
||||
|
||||
// We need two special keys for this test, one that will be used for a
|
||||
|
||||
@@ -66,12 +66,14 @@ class LicenseWhiteboxMaskedDecryptTest : public LicenseWhiteboxTestBase {
|
||||
License license;
|
||||
builder.Build(*public_key_, &license);
|
||||
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license.message.data(), license.message.size(),
|
||||
license.signature.data(), license.signature.size(),
|
||||
license.session_key.data(), license.session_key.size(),
|
||||
license.request.data(), license.request.size()),
|
||||
WB_RESULT_OK);
|
||||
ASSERT_EQ(
|
||||
WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license.core_message.data(), license.core_message.size(),
|
||||
license.message.data(), license.message.size(),
|
||||
license.signature.data(), license.signature.size(),
|
||||
license.session_key.data(), license.session_key.size(),
|
||||
license.request.data(), license.request.size()),
|
||||
WB_RESULT_OK);
|
||||
}
|
||||
|
||||
// We need two special keys for this test, one that will be used for a
|
||||
|
||||
@@ -50,10 +50,12 @@ TEST_F(LicenseWhiteboxProcessLicenseResponseBenchmark,
|
||||
WB_RESULT_OK);
|
||||
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license_.message.data(), license_.message.size(),
|
||||
license_.signature.data(), license_.signature.size(),
|
||||
license_.session_key.data(), license_.session_key.size(),
|
||||
license_.request.data(), license_.request.size()),
|
||||
whitebox_, license_.core_message.data(),
|
||||
license_.core_message.size(), license_.message.data(),
|
||||
license_.message.size(), license_.signature.data(),
|
||||
license_.signature.size(), license_.session_key.data(),
|
||||
license_.session_key.size(), license_.request.data(),
|
||||
license_.request.size()),
|
||||
WB_RESULT_OK);
|
||||
|
||||
WB_License_Delete(whitebox_);
|
||||
@@ -84,10 +86,12 @@ TEST_F(LicenseWhiteboxProcessLicenseResponseBenchmark,
|
||||
WB_RESULT_OK);
|
||||
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license_.message.data(), license_.message.size(),
|
||||
license_.signature.data(), license_.signature.size(),
|
||||
license_.session_key.data(), license_.session_key.size(),
|
||||
license_.request.data(), license_.request.size()),
|
||||
whitebox_, license_.core_message.data(),
|
||||
license_.core_message.size(), license_.message.data(),
|
||||
license_.message.size(), license_.signature.data(),
|
||||
license_.signature.size(), license_.session_key.data(),
|
||||
license_.session_key.size(), license_.request.data(),
|
||||
license_.request.size()),
|
||||
WB_RESULT_OK);
|
||||
|
||||
sampler.Push(timer.Get());
|
||||
@@ -115,10 +119,12 @@ TEST_F(LicenseWhiteboxProcessLicenseResponseBenchmark, ProcessLicenseResponse) {
|
||||
timer.Reset();
|
||||
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license_.message.data(), license_.message.size(),
|
||||
license_.signature.data(), license_.signature.size(),
|
||||
license_.session_key.data(), license_.session_key.size(),
|
||||
license_.request.data(), license_.request.size()),
|
||||
whitebox_, license_.core_message.data(),
|
||||
license_.core_message.size(), license_.message.data(),
|
||||
license_.message.size(), license_.signature.data(),
|
||||
license_.signature.size(), license_.session_key.data(),
|
||||
license_.session_key.size(), license_.request.data(),
|
||||
license_.request.size()),
|
||||
WB_RESULT_OK);
|
||||
|
||||
sampler.Push(timer.Get());
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
#include "api/license_whitebox.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "api/license_whitebox_test_base.h"
|
||||
#include "api/test_license_builder.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
namespace widevine {
|
||||
class LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest
|
||||
: public LicenseWhiteboxTestBase {
|
||||
protected:
|
||||
void UseLicenseWithoutSigningKey(bool use_odk) {
|
||||
TestLicenseBuilder builder;
|
||||
builder.AddStubbedContentKey();
|
||||
builder.SetUseODK(use_odk);
|
||||
builder.Build(*public_key_, &license_);
|
||||
}
|
||||
|
||||
void UseLicenseWithSigningKey(const std::vector<uint8_t>& padding,
|
||||
bool use_odk) {
|
||||
TestLicenseBuilder builder;
|
||||
builder.AddSigningKey(TestLicenseBuilder::DefaultSigningKey(), padding);
|
||||
builder.AddStubbedContentKey();
|
||||
builder.SetUseODK(use_odk);
|
||||
builder.Build(*public_key_, &license_);
|
||||
}
|
||||
|
||||
License license_;
|
||||
};
|
||||
|
||||
// Success tests below test with and without the ODK.
|
||||
|
||||
TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest,
|
||||
SuccessWithoutOdkAndWithoutSigningKey) {
|
||||
UseLicenseWithoutSigningKey(/* use_odk = */ false);
|
||||
|
||||
ASSERT_EQ(
|
||||
WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license_.core_message.data(), license_.core_message.size(),
|
||||
license_.message.data(), license_.message.size(),
|
||||
license_.signature.data(), license_.signature.size(),
|
||||
license_.session_key.data(), license_.session_key.size(),
|
||||
license_.request.data(), license_.request.size()),
|
||||
WB_RESULT_OK);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest,
|
||||
SuccessWithOdkAndWithoutSigningKey) {
|
||||
UseLicenseWithoutSigningKey(/* use_odk = */ true);
|
||||
|
||||
ASSERT_EQ(
|
||||
WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license_.core_message.data(), license_.core_message.size(),
|
||||
license_.message.data(), license_.message.size(),
|
||||
license_.signature.data(), license_.signature.size(),
|
||||
license_.session_key.data(), license_.session_key.size(),
|
||||
license_.request.data(), license_.request.size()),
|
||||
WB_RESULT_OK);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest,
|
||||
SuccessWithoutOdkAndWithSigningKeyNoPadding) {
|
||||
UseLicenseWithSigningKey(TestLicenseBuilder::NoPadding(),
|
||||
/* use_odk = */ false);
|
||||
|
||||
ASSERT_EQ(
|
||||
WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license_.core_message.data(), license_.core_message.size(),
|
||||
license_.message.data(), license_.message.size(),
|
||||
license_.signature.data(), license_.signature.size(),
|
||||
license_.session_key.data(), license_.session_key.size(),
|
||||
license_.request.data(), license_.request.size()),
|
||||
WB_RESULT_OK);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest,
|
||||
SuccessWithOdkAndWithSigningKeyNoPadding) {
|
||||
UseLicenseWithSigningKey(TestLicenseBuilder::NoPadding(),
|
||||
/* use_odk = */ true);
|
||||
|
||||
ASSERT_EQ(
|
||||
WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license_.core_message.data(), license_.core_message.size(),
|
||||
license_.message.data(), license_.message.size(),
|
||||
license_.signature.data(), license_.signature.size(),
|
||||
license_.session_key.data(), license_.session_key.size(),
|
||||
license_.request.data(), license_.request.size()),
|
||||
WB_RESULT_OK);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest,
|
||||
SuccessWithoutOdkAndWithSigningKeyPKSC8Padding) {
|
||||
UseLicenseWithSigningKey(TestLicenseBuilder::PKSC8Padding(),
|
||||
/* use_odk = */ false);
|
||||
|
||||
ASSERT_EQ(
|
||||
WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license_.core_message.data(), license_.core_message.size(),
|
||||
license_.message.data(), license_.message.size(),
|
||||
license_.signature.data(), license_.signature.size(),
|
||||
license_.session_key.data(), license_.session_key.size(),
|
||||
license_.request.data(), license_.request.size()),
|
||||
WB_RESULT_OK);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest,
|
||||
SuccessWithOdkAndWithSigningKeyPKSC8Padding) {
|
||||
UseLicenseWithSigningKey(TestLicenseBuilder::PKSC8Padding(),
|
||||
/* use_odk = */ true);
|
||||
|
||||
ASSERT_EQ(
|
||||
WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license_.core_message.data(), license_.core_message.size(),
|
||||
license_.message.data(), license_.message.size(),
|
||||
license_.signature.data(), license_.signature.size(),
|
||||
license_.session_key.data(), license_.session_key.size(),
|
||||
license_.request.data(), license_.request.size()),
|
||||
WB_RESULT_OK);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest,
|
||||
InvalidParameterForNullCoreMessage) {
|
||||
UseLicenseWithoutSigningKey(/* use_odk = */ true);
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, nullptr, license_.core_message.size(),
|
||||
license_.message.data(), license_.message.size(),
|
||||
license_.signature.data(), license_.signature.size(),
|
||||
license_.session_key.data(), license_.session_key.size(),
|
||||
license_.request.data(), license_.request.size()),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest,
|
||||
InvalidSignatureWithZeroCoreMessageSize) {
|
||||
UseLicenseWithoutSigningKey(/* use_odk = */ true);
|
||||
|
||||
// |core_message_size| = 0 means the core message is not provided, so
|
||||
// no parameter check will be done. However, since the license was created
|
||||
// with one, signature checking will fail as there is no core message
|
||||
// to be included when checking the signature.
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license_.core_message.data(), 0,
|
||||
license_.message.data(), license_.message.size(),
|
||||
license_.signature.data(), license_.signature.size(),
|
||||
license_.session_key.data(), license_.session_key.size(),
|
||||
license_.request.data(), license_.request.size()),
|
||||
WB_RESULT_INVALID_SIGNATURE);
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
@@ -14,7 +14,6 @@
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
class LicenseWhiteboxProcessLicenseResponseTest
|
||||
: public LicenseWhiteboxTestBase {
|
||||
protected:
|
||||
@@ -37,36 +36,42 @@ class LicenseWhiteboxProcessLicenseResponseTest
|
||||
TEST_F(LicenseWhiteboxProcessLicenseResponseTest, SuccessWithoutSigningKey) {
|
||||
UseLicenseWithoutSigningKey();
|
||||
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license_.message.data(), license_.message.size(),
|
||||
license_.signature.data(), license_.signature.size(),
|
||||
license_.session_key.data(), license_.session_key.size(),
|
||||
license_.request.data(), license_.request.size()),
|
||||
WB_RESULT_OK);
|
||||
ASSERT_EQ(
|
||||
WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license_.core_message.data(), license_.core_message.size(),
|
||||
license_.message.data(), license_.message.size(),
|
||||
license_.signature.data(), license_.signature.size(),
|
||||
license_.session_key.data(), license_.session_key.size(),
|
||||
license_.request.data(), license_.request.size()),
|
||||
WB_RESULT_OK);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxProcessLicenseResponseTest,
|
||||
SuccessWithSigningKeyNoPadding) {
|
||||
UseLicenseWithSigningKey(TestLicenseBuilder::NoPadding());
|
||||
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license_.message.data(), license_.message.size(),
|
||||
license_.signature.data(), license_.signature.size(),
|
||||
license_.session_key.data(), license_.session_key.size(),
|
||||
license_.request.data(), license_.request.size()),
|
||||
WB_RESULT_OK);
|
||||
ASSERT_EQ(
|
||||
WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license_.core_message.data(), license_.core_message.size(),
|
||||
license_.message.data(), license_.message.size(),
|
||||
license_.signature.data(), license_.signature.size(),
|
||||
license_.session_key.data(), license_.session_key.size(),
|
||||
license_.request.data(), license_.request.size()),
|
||||
WB_RESULT_OK);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxProcessLicenseResponseTest,
|
||||
SuccessWithSigningKeyPKSC8Padding) {
|
||||
UseLicenseWithSigningKey(TestLicenseBuilder::PKSC8Padding());
|
||||
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license_.message.data(), license_.message.size(),
|
||||
license_.signature.data(), license_.signature.size(),
|
||||
license_.session_key.data(), license_.session_key.size(),
|
||||
license_.request.data(), license_.request.size()),
|
||||
WB_RESULT_OK);
|
||||
ASSERT_EQ(
|
||||
WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license_.core_message.data(), license_.core_message.size(),
|
||||
license_.message.data(), license_.message.size(),
|
||||
license_.signature.data(), license_.signature.size(),
|
||||
license_.session_key.data(), license_.session_key.size(),
|
||||
license_.request.data(), license_.request.size()),
|
||||
WB_RESULT_OK);
|
||||
}
|
||||
|
||||
class LicenseWhiteboxProcessLicenseResponseErrorTest
|
||||
@@ -85,24 +90,28 @@ TEST_F(LicenseWhiteboxProcessLicenseResponseErrorTest,
|
||||
InvalidSignatureForModifedMessage) {
|
||||
Modify(&license_.message);
|
||||
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license_.message.data(), license_.message.size(),
|
||||
license_.signature.data(), license_.signature.size(),
|
||||
license_.session_key.data(), license_.session_key.size(),
|
||||
license_.request.data(), license_.request.size()),
|
||||
WB_RESULT_INVALID_SIGNATURE);
|
||||
ASSERT_EQ(
|
||||
WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license_.core_message.data(), license_.core_message.size(),
|
||||
license_.message.data(), license_.message.size(),
|
||||
license_.signature.data(), license_.signature.size(),
|
||||
license_.session_key.data(), license_.session_key.size(),
|
||||
license_.request.data(), license_.request.size()),
|
||||
WB_RESULT_INVALID_SIGNATURE);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxProcessLicenseResponseErrorTest,
|
||||
InvalidSignatureForModifedSignature) {
|
||||
Modify(&license_.signature);
|
||||
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license_.message.data(), license_.message.size(),
|
||||
license_.signature.data(), license_.signature.size(),
|
||||
license_.session_key.data(), license_.session_key.size(),
|
||||
license_.request.data(), license_.request.size()),
|
||||
WB_RESULT_INVALID_SIGNATURE);
|
||||
ASSERT_EQ(
|
||||
WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license_.core_message.data(), license_.core_message.size(),
|
||||
license_.message.data(), license_.message.size(),
|
||||
license_.signature.data(), license_.signature.size(),
|
||||
license_.session_key.data(), license_.session_key.size(),
|
||||
license_.request.data(), license_.request.size()),
|
||||
WB_RESULT_INVALID_SIGNATURE);
|
||||
}
|
||||
|
||||
// The license request is used to derive the signing key. If the request was
|
||||
@@ -111,28 +120,33 @@ TEST_F(LicenseWhiteboxProcessLicenseResponseErrorTest,
|
||||
InvalidSignatureForModifedLicenseRequest) {
|
||||
Modify(&license_.request);
|
||||
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license_.message.data(), license_.message.size(),
|
||||
license_.signature.data(), license_.signature.size(),
|
||||
license_.session_key.data(), license_.session_key.size(),
|
||||
license_.request.data(), license_.request.size()),
|
||||
WB_RESULT_INVALID_SIGNATURE);
|
||||
ASSERT_EQ(
|
||||
WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license_.core_message.data(), license_.core_message.size(),
|
||||
license_.message.data(), license_.message.size(),
|
||||
license_.signature.data(), license_.signature.size(),
|
||||
license_.session_key.data(), license_.session_key.size(),
|
||||
license_.request.data(), license_.request.size()),
|
||||
WB_RESULT_INVALID_SIGNATURE);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxProcessLicenseResponseErrorTest,
|
||||
InvalidParameterForNullWhitebox) {
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
nullptr, license_.message.data(), license_.message.size(),
|
||||
license_.signature.data(), license_.signature.size(),
|
||||
license_.session_key.data(), license_.session_key.size(),
|
||||
license_.request.data(), license_.request.size()),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
ASSERT_EQ(
|
||||
WB_License_ProcessLicenseResponse(
|
||||
nullptr, license_.core_message.data(), license_.core_message.size(),
|
||||
license_.message.data(), license_.message.size(),
|
||||
license_.signature.data(), license_.signature.size(),
|
||||
license_.session_key.data(), license_.session_key.size(),
|
||||
license_.request.data(), license_.request.size()),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxProcessLicenseResponseErrorTest,
|
||||
InvalidParameterForNullMessage) {
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, nullptr, license_.message.size(),
|
||||
whitebox_, license_.core_message.data(),
|
||||
license_.core_message.size(), nullptr, license_.message.size(),
|
||||
license_.signature.data(), license_.signature.size(),
|
||||
license_.session_key.data(), license_.session_key.size(),
|
||||
license_.request.data(), license_.request.size()),
|
||||
@@ -142,7 +156,8 @@ TEST_F(LicenseWhiteboxProcessLicenseResponseErrorTest,
|
||||
TEST_F(LicenseWhiteboxProcessLicenseResponseErrorTest,
|
||||
InvalidParameterForZeroMessageSize) {
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license_.message.data(), 0,
|
||||
whitebox_, license_.core_message.data(),
|
||||
license_.core_message.size(), license_.message.data(), 0,
|
||||
license_.signature.data(), license_.signature.size(),
|
||||
license_.session_key.data(), license_.session_key.size(),
|
||||
license_.request.data(), license_.request.size()),
|
||||
@@ -152,40 +167,44 @@ TEST_F(LicenseWhiteboxProcessLicenseResponseErrorTest,
|
||||
TEST_F(LicenseWhiteboxProcessLicenseResponseErrorTest,
|
||||
InvalidParameterForNullSignature) {
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license_.message.data(), license_.message.size(),
|
||||
nullptr, license_.signature.size(), license_.session_key.data(),
|
||||
license_.session_key.size(), license_.request.data(),
|
||||
license_.request.size()),
|
||||
whitebox_, license_.core_message.data(),
|
||||
license_.core_message.size(), license_.message.data(),
|
||||
license_.message.size(), nullptr, license_.signature.size(),
|
||||
license_.session_key.data(), license_.session_key.size(),
|
||||
license_.request.data(), license_.request.size()),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxProcessLicenseResponseErrorTest,
|
||||
InvalidParameterForInvalidSignatureSize) {
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license_.message.data(), license_.message.size(),
|
||||
license_.signature.data(), 5, license_.session_key.data(),
|
||||
license_.session_key.size(), license_.request.data(),
|
||||
license_.request.size()),
|
||||
whitebox_, license_.core_message.data(),
|
||||
license_.core_message.size(), license_.message.data(),
|
||||
license_.message.size(), license_.signature.data(), 5,
|
||||
license_.session_key.data(), license_.session_key.size(),
|
||||
license_.request.data(), license_.request.size()),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxProcessLicenseResponseErrorTest,
|
||||
InvalidParameterForNullSessionKey) {
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license_.message.data(), license_.message.size(),
|
||||
license_.signature.data(), license_.signature.size(), nullptr,
|
||||
license_.session_key.size(), license_.request.data(),
|
||||
license_.request.size()),
|
||||
whitebox_, license_.core_message.data(),
|
||||
license_.core_message.size(), license_.message.data(),
|
||||
license_.message.size(), license_.signature.data(),
|
||||
license_.signature.size(), nullptr, license_.session_key.size(),
|
||||
license_.request.data(), license_.request.size()),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxProcessLicenseResponseErrorTest,
|
||||
InvalidParameterForInvalidSessionKeySize) {
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license_.message.data(), license_.message.size(),
|
||||
license_.signature.data(), license_.signature.size(),
|
||||
license_.session_key.data(), 5, license_.request.data(),
|
||||
license_.request.size()),
|
||||
whitebox_, license_.core_message.data(),
|
||||
license_.core_message.size(), license_.message.data(),
|
||||
license_.message.size(), license_.signature.data(),
|
||||
license_.signature.size(), license_.session_key.data(), 5,
|
||||
license_.request.data(), license_.request.size()),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
@@ -195,31 +214,35 @@ TEST_F(LicenseWhiteboxProcessLicenseResponseErrorTest,
|
||||
InvalidParameterForModifedSessionKey) {
|
||||
Modify(&license_.session_key);
|
||||
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license_.message.data(), license_.message.size(),
|
||||
license_.signature.data(), license_.signature.size(),
|
||||
license_.session_key.data(), license_.session_key.size(),
|
||||
license_.request.data(), license_.request.size()),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
ASSERT_EQ(
|
||||
WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license_.core_message.data(), license_.core_message.size(),
|
||||
license_.message.data(), license_.message.size(),
|
||||
license_.signature.data(), license_.signature.size(),
|
||||
license_.session_key.data(), license_.session_key.size(),
|
||||
license_.request.data(), license_.request.size()),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxProcessLicenseResponseErrorTest,
|
||||
InvalidParameterForNullLicenseRequest) {
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license_.message.data(), license_.message.size(),
|
||||
license_.signature.data(), license_.signature.size(),
|
||||
license_.session_key.data(), license_.session_key.size(),
|
||||
nullptr, license_.request.size()),
|
||||
whitebox_, license_.core_message.data(),
|
||||
license_.core_message.size(), license_.message.data(),
|
||||
license_.message.size(), license_.signature.data(),
|
||||
license_.signature.size(), license_.session_key.data(),
|
||||
license_.session_key.size(), nullptr, license_.request.size()),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxProcessLicenseResponseErrorTest,
|
||||
InvalidParameterForZeroLienseRequestSize) {
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license_.message.data(), license_.message.size(),
|
||||
license_.signature.data(), license_.signature.size(),
|
||||
license_.session_key.data(), license_.session_key.size(),
|
||||
license_.request.data(), 0),
|
||||
whitebox_, license_.core_message.data(),
|
||||
license_.core_message.size(), license_.message.data(),
|
||||
license_.message.size(), license_.signature.data(),
|
||||
license_.signature.size(), license_.session_key.data(),
|
||||
license_.session_key.size(), license_.request.data(), 0),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
@@ -240,21 +263,25 @@ class LicenseWhiteboxMultiLicenseTest
|
||||
TEST_F(LicenseWhiteboxMultiLicenseTest, InvalidState) {
|
||||
// Load the first license. This one is expected to succeed as the whitebox has
|
||||
// not loaded a license yet.
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license_.message.data(), license_.message.size(),
|
||||
license_.signature.data(), license_.signature.size(),
|
||||
license_.session_key.data(), license_.session_key.size(),
|
||||
license_.request.data(), license_.request.size()),
|
||||
WB_RESULT_OK);
|
||||
ASSERT_EQ(
|
||||
WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license_.core_message.data(), license_.core_message.size(),
|
||||
license_.message.data(), license_.message.size(),
|
||||
license_.signature.data(), license_.signature.size(),
|
||||
license_.session_key.data(), license_.session_key.size(),
|
||||
license_.request.data(), license_.request.size()),
|
||||
WB_RESULT_OK);
|
||||
|
||||
// Attempt to load the same license again. This should fail as it already has
|
||||
// a license (even though it is the same license).
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license_.message.data(), license_.message.size(),
|
||||
license_.signature.data(), license_.signature.size(),
|
||||
license_.session_key.data(), license_.session_key.size(),
|
||||
license_.request.data(), license_.request.size()),
|
||||
WB_RESULT_INVALID_STATE);
|
||||
ASSERT_EQ(
|
||||
WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license_.core_message.data(), license_.core_message.size(),
|
||||
license_.message.data(), license_.message.size(),
|
||||
license_.signature.data(), license_.signature.size(),
|
||||
license_.session_key.data(), license_.session_key.size(),
|
||||
license_.request.data(), license_.request.size()),
|
||||
WB_RESULT_INVALID_STATE);
|
||||
}
|
||||
|
||||
// Even though a whitebox can only load a license once, if it fails to load a
|
||||
@@ -264,21 +291,25 @@ TEST_F(LicenseWhiteboxMultiLicenseTest, SuccessAfterFailure) {
|
||||
// in key derivation which is a later step of license parsing.
|
||||
std::vector<uint8_t> bad_request = license_.request;
|
||||
Modify(&bad_request);
|
||||
ASSERT_NE(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license_.message.data(), license_.message.size(),
|
||||
license_.signature.data(), license_.signature.size(),
|
||||
license_.session_key.data(), license_.session_key.size(),
|
||||
bad_request.data(), bad_request.size()),
|
||||
WB_RESULT_OK);
|
||||
ASSERT_NE(
|
||||
WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license_.core_message.data(), license_.core_message.size(),
|
||||
license_.message.data(), license_.message.size(),
|
||||
license_.signature.data(), license_.signature.size(),
|
||||
license_.session_key.data(), license_.session_key.size(),
|
||||
bad_request.data(), bad_request.size()),
|
||||
WB_RESULT_OK);
|
||||
|
||||
// Attempt to load the license again, but use the correct (unmodified)
|
||||
// request.
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license_.message.data(), license_.message.size(),
|
||||
license_.signature.data(), license_.signature.size(),
|
||||
license_.session_key.data(), license_.session_key.size(),
|
||||
license_.request.data(), license_.request.size()),
|
||||
WB_RESULT_OK);
|
||||
ASSERT_EQ(
|
||||
WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license_.core_message.data(), license_.core_message.size(),
|
||||
license_.message.data(), license_.message.size(),
|
||||
license_.signature.data(), license_.signature.size(),
|
||||
license_.session_key.data(), license_.session_key.size(),
|
||||
license_.request.data(), license_.request.size()),
|
||||
WB_RESULT_OK);
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
@@ -38,12 +38,14 @@ class LicenseWhiteboxSignBenchmark : public LicenseWhiteboxBenchmark {
|
||||
WB_RESULT_OK);
|
||||
|
||||
const auto license = CreateLicense();
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license.message.data(), license.message.size(),
|
||||
license.signature.data(), license.signature.size(),
|
||||
license.session_key.data(), license.session_key.size(),
|
||||
license.request.data(), license.request.size()),
|
||||
WB_RESULT_OK);
|
||||
ASSERT_EQ(
|
||||
WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license.core_message.data(), license.core_message.size(),
|
||||
license.message.data(), license.message.size(),
|
||||
license.signature.data(), license.signature.size(),
|
||||
license.session_key.data(), license.session_key.size(),
|
||||
license.request.data(), license.request.size()),
|
||||
WB_RESULT_OK);
|
||||
}
|
||||
|
||||
void TearDown() override { WB_License_Delete(whitebox_); }
|
||||
|
||||
@@ -36,12 +36,14 @@ class LicenseWhiteboxSignRenewalRequestTest : public LicenseWhiteboxTestBase {
|
||||
License license;
|
||||
builder.Build(*public_key_, &license);
|
||||
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license.message.data(), license.message.size(),
|
||||
license.signature.data(), license.signature.size(),
|
||||
license.session_key.data(), license.session_key.size(),
|
||||
license.request.data(), license.request.size()),
|
||||
WB_RESULT_OK);
|
||||
ASSERT_EQ(
|
||||
WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license.core_message.data(), license.core_message.size(),
|
||||
license.message.data(), license.message.size(),
|
||||
license.signature.data(), license.signature.size(),
|
||||
license.session_key.data(), license.session_key.size(),
|
||||
license.request.data(), license.request.size()),
|
||||
WB_RESULT_OK);
|
||||
}
|
||||
|
||||
// Get the expected signature for |message|. By returning the message, this
|
||||
@@ -186,12 +188,14 @@ TEST_F(LicenseWhiteboxSignRenewalRequestTest, InvalidStateForNoSigningKey) {
|
||||
License license;
|
||||
builder.Build(*public_key_, &license);
|
||||
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license.message.data(), license.message.size(),
|
||||
license.signature.data(), license.signature.size(),
|
||||
license.session_key.data(), license.session_key.size(),
|
||||
license.request.data(), license.request.size()),
|
||||
WB_RESULT_OK);
|
||||
ASSERT_EQ(
|
||||
WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license.core_message.data(), license.core_message.size(),
|
||||
license.message.data(), license.message.size(),
|
||||
license.signature.data(), license.signature.size(),
|
||||
license.session_key.data(), license.session_key.size(),
|
||||
license.request.data(), license.request.size()),
|
||||
WB_RESULT_OK);
|
||||
|
||||
ASSERT_EQ(WB_License_SignRenewalRequest(whitebox_, garbage_request_.data(),
|
||||
garbage_request_.size(),
|
||||
|
||||
@@ -1,257 +0,0 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
#include "api/license_whitebox.h"
|
||||
|
||||
#include <ctime>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "api/license_test_helper.h"
|
||||
#include "api/test_data.h"
|
||||
#include "crypto_utils/rsa_key.h"
|
||||
#include "testing/include/gtest/gtest.h"
|
||||
|
||||
using RsaPublicKey = widevine::RsaPublicKey;
|
||||
|
||||
namespace {
|
||||
|
||||
const uint8_t kDummyMessage[] = {
|
||||
0x1e, 0x70, 0xbd, 0xeb, 0x24, 0xf2, 0x9d, 0x05, 0xc5, 0xb5,
|
||||
0xf4, 0xca, 0xe6, 0x1d, 0x01, 0x97, 0x29, 0xf4, 0xe0, 0x7c,
|
||||
0xfd, 0xcc, 0x97, 0x8d, 0xc2, 0xbb, 0x2d, 0x9b, 0x6b, 0x45,
|
||||
0x06, 0xbd, 0x2c, 0x66, 0x10, 0x42, 0x73, 0x8d, 0x88, 0x9b,
|
||||
0x18, 0xcc, 0xcb, 0x7e, 0x43, 0x23, 0x06, 0xe9, 0x8f, 0x8f,
|
||||
};
|
||||
const size_t kDummyMessageSize = sizeof(kDummyMessage);
|
||||
|
||||
// Size of a license signature. This must be big enough to hold the signature
|
||||
// returned by WB_License_SignLicenseRequest().
|
||||
constexpr size_t kSignatureSize = 256;
|
||||
|
||||
std::string AsString(const uint8_t* buffer, size_t buffer_size) {
|
||||
return std::string(reinterpret_cast<const char*>(buffer), buffer_size);
|
||||
}
|
||||
|
||||
// Used to test the rest of the whitebox API as this class creates |whitebox_|
|
||||
// during setup, so it is available for all tests. It also creates |public_key_|
|
||||
// which is the matching RSA public key that can be used to verify signing
|
||||
// operations inside the whitebox.
|
||||
class LicenseWhiteboxTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
const std::vector<uint8_t> init_data = GetLicenseInitData();
|
||||
ASSERT_EQ(WB_License_Create(init_data.data(), init_data.size(), &whitebox_),
|
||||
WB_RESULT_OK);
|
||||
|
||||
std::vector<uint8_t> public_key = GetMatchingLicensePublicKey();
|
||||
public_key_.reset(
|
||||
RsaPublicKey::Create(AsString(public_key.data(), public_key.size())));
|
||||
ASSERT_TRUE(public_key_);
|
||||
}
|
||||
|
||||
void TearDown() override { WB_License_Delete(whitebox_); }
|
||||
|
||||
// Public key as an usable object.
|
||||
std::unique_ptr<RsaPublicKey> public_key_;
|
||||
|
||||
WB_License_Whitebox* whitebox_;
|
||||
};
|
||||
|
||||
TEST_F(LicenseWhiteboxTest, SignAndVerifySucceedsWithRandomData) {
|
||||
uint8_t signature[kSignatureSize];
|
||||
size_t signature_size = kSignatureSize;
|
||||
ASSERT_EQ(
|
||||
WB_License_SignLicenseRequest(whitebox_, kDummyMessage, kDummyMessageSize,
|
||||
signature, &signature_size),
|
||||
WB_RESULT_OK);
|
||||
|
||||
// Verify the signature.
|
||||
ASSERT_TRUE(
|
||||
public_key_->VerifySignature(AsString(kDummyMessage, kDummyMessageSize),
|
||||
AsString(signature, signature_size)));
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxTest, SignAndVerifySucceedsWithLicenseRequest) {
|
||||
const std::string license_request = widevine::CreateLicenseRequest();
|
||||
|
||||
uint8_t signature[kSignatureSize];
|
||||
size_t signature_size = kSignatureSize;
|
||||
ASSERT_EQ(
|
||||
WB_License_SignLicenseRequest(
|
||||
whitebox_, reinterpret_cast<const uint8_t*>(license_request.data()),
|
||||
license_request.size(), signature, &signature_size),
|
||||
WB_RESULT_OK);
|
||||
|
||||
ASSERT_TRUE(public_key_->VerifySignature(
|
||||
license_request, AsString(signature, signature_size)));
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxTest, SignLicenseRequestFailsWithWhiteboxNull) {
|
||||
uint8_t signature[kSignatureSize];
|
||||
size_t signature_size = kSignatureSize;
|
||||
ASSERT_EQ(
|
||||
WB_License_SignLicenseRequest(nullptr, kDummyMessage, kDummyMessageSize,
|
||||
signature, &signature_size),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxTest, SignLicenseRequestFailsWithMessageNull) {
|
||||
uint8_t signature[kSignatureSize];
|
||||
size_t signature_size = kSignatureSize;
|
||||
ASSERT_EQ(WB_License_SignLicenseRequest(whitebox_, nullptr, kDummyMessageSize,
|
||||
signature, &signature_size),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxTest, SignLicenseRequestFailsWithMessageSizeZero) {
|
||||
uint8_t signature[kSignatureSize];
|
||||
size_t signature_size = kSignatureSize;
|
||||
ASSERT_EQ(WB_License_SignLicenseRequest(whitebox_, kDummyMessage, 0,
|
||||
signature, &signature_size),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxTest, SignLicenseRequestFailsWithSignatureNull) {
|
||||
size_t signature_size = kSignatureSize;
|
||||
ASSERT_EQ(
|
||||
WB_License_SignLicenseRequest(whitebox_, kDummyMessage, kDummyMessageSize,
|
||||
nullptr, &signature_size),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxTest, SignLicenseRequestFailsWithSignatureSizeNull) {
|
||||
uint8_t signature[kSignatureSize];
|
||||
ASSERT_EQ(
|
||||
WB_License_SignLicenseRequest(whitebox_, kDummyMessage, kDummyMessageSize,
|
||||
signature, nullptr),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxTest, SignLicenseRequestFailsWithSignatureSizeZero) {
|
||||
uint8_t signature[kSignatureSize];
|
||||
size_t signature_size = 0;
|
||||
ASSERT_EQ(
|
||||
WB_License_SignLicenseRequest(whitebox_, kDummyMessage, kDummyMessageSize,
|
||||
signature, &signature_size),
|
||||
WB_RESULT_BUFFER_TOO_SMALL);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxTest, SignLicenseRequestFailsWithSignatureTooSmall) {
|
||||
uint8_t signature[10];
|
||||
size_t signature_size = sizeof(signature);
|
||||
ASSERT_EQ(
|
||||
WB_License_SignLicenseRequest(whitebox_, kDummyMessage, kDummyMessageSize,
|
||||
signature, &signature_size),
|
||||
WB_RESULT_BUFFER_TOO_SMALL);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxTest, ProcessLicenseResponseFailsWithWhiteboxNull) {
|
||||
std::vector<uint8_t> message{1, 2, 3};
|
||||
std::vector<uint8_t> signature{4, 5, 6, 7};
|
||||
std::vector<uint8_t> session_key{1, 2, 3, 4, 5, 6, 7, 8};
|
||||
std::vector<uint8_t> license_request{10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
nullptr, message.data(), message.size(), signature.data(),
|
||||
signature.size(), session_key.data(), session_key.size(),
|
||||
license_request.data(), license_request.size()),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxTest, ProcessLicenseResponseFailsWithMessageNull) {
|
||||
std::vector<uint8_t> message{1, 2, 3};
|
||||
std::vector<uint8_t> signature{4, 5, 6, 7};
|
||||
std::vector<uint8_t> session_key{1, 2, 3, 4, 5, 6, 7, 8};
|
||||
std::vector<uint8_t> license_request{10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, nullptr, message.size(), signature.data(),
|
||||
signature.size(), session_key.data(), session_key.size(),
|
||||
license_request.data(), license_request.size()),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxTest, ProcessLicenseResponseFailsWithMessageSizeZero) {
|
||||
std::vector<uint8_t> message{/* empty */};
|
||||
std::vector<uint8_t> signature{4, 5, 6, 7};
|
||||
std::vector<uint8_t> session_key{1, 2, 3, 4, 5, 6, 7, 8};
|
||||
std::vector<uint8_t> license_request{10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, message.data(), message.size(), signature.data(),
|
||||
signature.size(), session_key.data(), session_key.size(),
|
||||
license_request.data(), license_request.size()),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxTest, ProcessLicenseResponseFailsWithSignatureNull) {
|
||||
std::vector<uint8_t> message{1, 2, 3};
|
||||
std::vector<uint8_t> signature{4, 5, 6, 7};
|
||||
std::vector<uint8_t> session_key{1, 2, 3, 4, 5, 6, 7, 8};
|
||||
std::vector<uint8_t> license_request{10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, nullptr, message.size(), nullptr, signature.size(),
|
||||
session_key.data(), session_key.size(), license_request.data(),
|
||||
license_request.size()),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxTest, ProcessLicenseResponseFailsWithSignatureSizeZero) {
|
||||
std::vector<uint8_t> message{1, 2, 3};
|
||||
std::vector<uint8_t> signature{/* empty */};
|
||||
std::vector<uint8_t> session_key{1, 2, 3, 4, 5, 6, 7, 8};
|
||||
std::vector<uint8_t> license_request{10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, message.data(), message.size(), signature.data(),
|
||||
signature.size(), session_key.data(), session_key.size(),
|
||||
license_request.data(), license_request.size()),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxTest, ProcessLicenseResponseFailsWithSessionKeyNull) {
|
||||
std::vector<uint8_t> message{1, 2, 3};
|
||||
std::vector<uint8_t> signature{4, 5, 6, 7};
|
||||
std::vector<uint8_t> session_key{1, 2, 3, 4, 5, 6, 7, 8};
|
||||
std::vector<uint8_t> license_request{10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, message.data(), message.size(), signature.data(),
|
||||
signature.size(), nullptr, session_key.size(),
|
||||
license_request.data(), license_request.size()),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxTest, ProcessLicenseResponseFailsWithSessionKeySizeZero) {
|
||||
std::vector<uint8_t> message{1, 2, 3};
|
||||
std::vector<uint8_t> signature{4, 5, 6, 7};
|
||||
std::vector<uint8_t> session_key{/* empty */};
|
||||
std::vector<uint8_t> license_request{10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, message.data(), message.size(), signature.data(),
|
||||
signature.size(), session_key.data(), session_key.size(),
|
||||
license_request.data(), license_request.size()),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxTest, ProcessLicenseResponseFailsWithRequestNull) {
|
||||
std::vector<uint8_t> message{1, 2, 3};
|
||||
std::vector<uint8_t> signature{4, 5, 6, 7};
|
||||
std::vector<uint8_t> session_key{1, 2, 3, 4, 5, 6, 7, 8};
|
||||
std::vector<uint8_t> license_request{10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, message.data(), message.size(), signature.data(),
|
||||
signature.size(), session_key.data(), session_key.size(),
|
||||
nullptr, license_request.size()),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxTest, ProcessLicenseResponseFailsWithRequestSizeZero) {
|
||||
std::vector<uint8_t> message{1, 2, 3};
|
||||
std::vector<uint8_t> signature{4, 5, 6, 7};
|
||||
std::vector<uint8_t> session_key{1, 2, 3, 4, 5, 6, 7, 8};
|
||||
std::vector<uint8_t> license_request{/* empty */};
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, message.data(), message.size(), signature.data(),
|
||||
signature.size(), session_key.data(), session_key.size(),
|
||||
license_request.data(), license_request.size()),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -34,12 +34,14 @@ class LicenseWhiteboxVerifyBenchmark : public LicenseWhiteboxBenchmark {
|
||||
WB_RESULT_OK);
|
||||
|
||||
const auto license = CreateLicense();
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license.message.data(), license.message.size(),
|
||||
license.signature.data(), license.signature.size(),
|
||||
license.session_key.data(), license.session_key.size(),
|
||||
license.request.data(), license.request.size()),
|
||||
WB_RESULT_OK);
|
||||
ASSERT_EQ(
|
||||
WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license.core_message.data(), license.core_message.size(),
|
||||
license.message.data(), license.message.size(),
|
||||
license.signature.data(), license.signature.size(),
|
||||
license.session_key.data(), license.session_key.size(),
|
||||
license.request.data(), license.request.size()),
|
||||
WB_RESULT_OK);
|
||||
}
|
||||
|
||||
void TearDown() override { WB_License_Delete(whitebox_); }
|
||||
|
||||
@@ -36,12 +36,14 @@ class LicenseWhiteboxVerifyRenewalResponseTest
|
||||
License license;
|
||||
builder.Build(*public_key_, &license);
|
||||
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license.message.data(), license.message.size(),
|
||||
license.signature.data(), license.signature.size(),
|
||||
license.session_key.data(), license.session_key.size(),
|
||||
license.request.data(), license.request.size()),
|
||||
WB_RESULT_OK);
|
||||
ASSERT_EQ(
|
||||
WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license.core_message.data(), license.core_message.size(),
|
||||
license.message.data(), license.message.size(),
|
||||
license.signature.data(), license.signature.size(),
|
||||
license.session_key.data(), license.session_key.size(),
|
||||
license.request.data(), license.request.size()),
|
||||
WB_RESULT_OK);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> Sign(const std::vector<uint8_t>& message) {
|
||||
@@ -210,12 +212,14 @@ TEST_F(LicenseWhiteboxVerifyRenewalResponseTest, InvalidStateForNoSigningKey) {
|
||||
License license;
|
||||
builder.Build(*public_key_, &license);
|
||||
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license.message.data(), license.message.size(),
|
||||
license.signature.data(), license.signature.size(),
|
||||
license.session_key.data(), license.session_key.size(),
|
||||
license.request.data(), license.request.size()),
|
||||
WB_RESULT_OK);
|
||||
ASSERT_EQ(
|
||||
WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license.core_message.data(), license.core_message.size(),
|
||||
license.message.data(), license.message.size(),
|
||||
license.signature.data(), license.signature.size(),
|
||||
license.session_key.data(), license.session_key.size(),
|
||||
license.request.data(), license.request.size()),
|
||||
WB_RESULT_OK);
|
||||
|
||||
ASSERT_EQ(WB_License_VerifyRenewalResponse(whitebox_,
|
||||
garbage_renewal_message_.data(),
|
||||
|
||||
@@ -9,6 +9,10 @@
|
||||
#include "crypto_utils/aes_cbc_encryptor.h"
|
||||
#include "crypto_utils/crypto_util.h"
|
||||
#include "crypto_utils/sha_util.h"
|
||||
#include "oemcrypto/odk/include/core_message_deserialize.h"
|
||||
#include "oemcrypto/odk/include/core_message_serialize_proto.h"
|
||||
#include "oemcrypto/odk/include/odk.h"
|
||||
#include "oemcrypto/odk/include/odk_structs.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
@@ -98,6 +102,95 @@ std::string DeriveIV(const std::vector<uint8_t>& context) {
|
||||
return crypto_util::DeriveIv(context_str);
|
||||
}
|
||||
|
||||
void UpdateKeyControlBlock(
|
||||
video_widevine::License_KeyContainer_SecurityLevel level,
|
||||
video_widevine::License_KeyContainer_KeyControl* key_control) {
|
||||
// The key control block is an 128 bit structure containing the following
|
||||
// fields. The fields are defined to be in big-endian byte order.
|
||||
//
|
||||
// Bytes 0..3: Verification.
|
||||
// Constant bytes “kctl”, “kc09”, “kc10”, “kc11”, ... “kc15”.
|
||||
// Bytes 4..7: Obsolete.
|
||||
// Bytes 8..11: Nonce.
|
||||
// Bytes 12..15: Control Bits
|
||||
// Bits 27..26: Security_Level (Only for L3 white-box implementations)
|
||||
// 0 = SW_SECURE_CRYPTO
|
||||
// 1 = SW_SECURE_DECODE
|
||||
// 2 = HW_SECURE_CRYPTO
|
||||
// 3 = HW_SECURE_DECODE or HW_SECURE_ALL
|
||||
std::vector<uint8_t> key_control_block = {
|
||||
'k', 'c', 't', 'l', 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
switch (level) {
|
||||
case video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_CRYPTO:
|
||||
key_control_block[12] = 0x00 << 2;
|
||||
break;
|
||||
case video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_DECODE:
|
||||
key_control_block[12] = 0x01 << 2;
|
||||
break;
|
||||
case video_widevine::License_KeyContainer_SecurityLevel_HW_SECURE_CRYPTO:
|
||||
key_control_block[12] = 0x02 << 2;
|
||||
break;
|
||||
default:
|
||||
key_control_block[12] = 0x03 << 2;
|
||||
break;
|
||||
}
|
||||
|
||||
// Key Control Block is no longer encrypted, so no need to set IV.
|
||||
key_control->set_key_control_block(key_control_block.data(),
|
||||
key_control_block.size());
|
||||
}
|
||||
|
||||
std::string GenerateCoreMessage(
|
||||
const std::string& serialized_request,
|
||||
const std::string& serialized_license_response) {
|
||||
constexpr uint16_t api_major_version = 16;
|
||||
constexpr uint16_t api_minor_version = 5;
|
||||
static_assert(api_major_version == ODK_MAJOR_VERSION,
|
||||
"Verify ODK library is compatible.");
|
||||
constexpr uint32_t session_id = 0xcafebabe;
|
||||
constexpr uint32_t nonce = 0xdeadbeef;
|
||||
ODK_NonceValues nonce_values{api_minor_version, api_major_version, nonce,
|
||||
session_id};
|
||||
|
||||
// Start by making a call to determine how big the core_message for the
|
||||
// request needs to be.
|
||||
size_t core_message_length = 0;
|
||||
auto odk_result = ODK_PrepareCoreLicenseRequest(
|
||||
reinterpret_cast<uint8_t*>(const_cast<char*>(serialized_request.data())),
|
||||
serialized_request.size(), &core_message_length, &nonce_values);
|
||||
CHECK_EQ(odk_result, OEMCrypto_ERROR_SHORT_BUFFER);
|
||||
|
||||
// Now that we know the size, create |combined_request_message| with room
|
||||
// for the core message and append |serialized_request_|, as the combined
|
||||
// buffer is needed by ODK_PrepareCoreLicenseRequest().
|
||||
std::string combined_request_message;
|
||||
combined_request_message.resize(core_message_length);
|
||||
combined_request_message.append(serialized_request);
|
||||
odk_result = ODK_PrepareCoreLicenseRequest(
|
||||
reinterpret_cast<uint8_t*>(
|
||||
const_cast<char*>(combined_request_message.data())),
|
||||
combined_request_message.size(), &core_message_length, &nonce_values);
|
||||
CHECK_EQ(odk_result, OEMCrypto_SUCCESS);
|
||||
|
||||
// As the core_message is the first part of |combined_request_message|,
|
||||
// extract it.
|
||||
const std::string request_core_message =
|
||||
combined_request_message.substr(0, core_message_length);
|
||||
std::string core_message_hash = widevine::Sha256_Hash(request_core_message);
|
||||
|
||||
oemcrypto_core_message::ODK_LicenseRequest core_request{};
|
||||
CHECK(oemcrypto_core_message::deserialize::CoreLicenseRequestFromMessage(
|
||||
request_core_message, &core_request));
|
||||
|
||||
std::string oemcrypto_core_message;
|
||||
CHECK(oemcrypto_core_message::serialize::CreateCoreLicenseResponseFromProto(
|
||||
serialized_license_response, core_request, core_message_hash,
|
||||
/* nonce_required= */ true, &oemcrypto_core_message));
|
||||
return oemcrypto_core_message;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
@@ -132,6 +225,7 @@ TestLicenseBuilder::TestLicenseBuilder() {
|
||||
InitializeResponse(request_, &response_);
|
||||
|
||||
serialized_request_ = request_.SerializeAsString();
|
||||
|
||||
container_key_ = crypto_util::DeriveKey(
|
||||
session_key_, crypto_util::kWrappingKeyLabel, serialized_request_,
|
||||
crypto_util::kWrappingKeySizeBits);
|
||||
@@ -156,17 +250,20 @@ void TestLicenseBuilder::AddSigningKey(const std::vector<uint8_t>& key,
|
||||
}
|
||||
|
||||
void TestLicenseBuilder::AddStubbedContentKey() {
|
||||
const video_widevine::License_KeyContainer_SecurityLevel kLevel =
|
||||
video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_CRYPTO;
|
||||
auto* container = response_.add_key();
|
||||
|
||||
container->set_type(video_widevine::License_KeyContainer_KeyType_CONTENT);
|
||||
container->set_id("stubbed-content-key");
|
||||
container->set_level(
|
||||
video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_CRYPTO);
|
||||
container->set_level(kLevel);
|
||||
container->set_iv("0000000000000000");
|
||||
|
||||
// We don't bother encrypting the key, it should never be used and there is no
|
||||
// way to verify it.
|
||||
container->set_key("0000000000000000");
|
||||
// way to verify it. Note that the ODK automatically strips padding, so
|
||||
// this key needs to include 16 bytes of padding.
|
||||
container->set_key("00000000000000000000000000000000");
|
||||
UpdateKeyControlBlock(kLevel, container->mutable_key_control());
|
||||
}
|
||||
|
||||
void TestLicenseBuilder::AddContentKey(
|
||||
@@ -192,6 +289,7 @@ void TestLicenseBuilder::AddContentKey(
|
||||
std::vector<uint8_t> final_key = key;
|
||||
final_key.insert(final_key.end(), padding.begin(), padding.end());
|
||||
container->set_key(EncryptKey(container_key_, key_iv, final_key));
|
||||
UpdateKeyControlBlock(level, container->mutable_key_control());
|
||||
}
|
||||
|
||||
void TestLicenseBuilder::AddOperatorSessionKey(
|
||||
@@ -235,6 +333,10 @@ void TestLicenseBuilder::SetVerificationStatus(VerificationStatus setting) {
|
||||
}
|
||||
}
|
||||
|
||||
void TestLicenseBuilder::SetUseODK(bool setting) {
|
||||
use_odk_ = setting;
|
||||
}
|
||||
|
||||
void TestLicenseBuilder::Build(const RsaPublicKey& public_key,
|
||||
License* license) const {
|
||||
DCHECK(license);
|
||||
@@ -246,19 +348,26 @@ void TestLicenseBuilder::Build(const RsaPublicKey& public_key,
|
||||
crypto_util::kSigningKeySizeBits * 2);
|
||||
signing_key.resize(crypto_util::kSigningKeySizeBytes);
|
||||
|
||||
const std::string signature_str =
|
||||
crypto_util::CreateSignatureHmacSha256(signing_key, message_str);
|
||||
|
||||
std::string session_key_str;
|
||||
CHECK(public_key.Encrypt(session_key_, &session_key_str));
|
||||
|
||||
license->request = std::vector<uint8_t>(serialized_request_.begin(),
|
||||
serialized_request_.end());
|
||||
license->message =
|
||||
std::vector<uint8_t>(message_str.begin(), message_str.end());
|
||||
license->signature =
|
||||
std::vector<uint8_t>(signature_str.begin(), signature_str.end());
|
||||
license->session_key =
|
||||
std::vector<uint8_t>(session_key_str.begin(), session_key_str.end());
|
||||
const std::string oemcrypto_core_message =
|
||||
use_odk_ ? GenerateCoreMessage(serialized_request_, message_str)
|
||||
: std::string();
|
||||
|
||||
// If |use_odk_| is false, |oemcrypto_core_message| will be empty.
|
||||
const std::string signature_str = crypto_util::CreateSignatureHmacSha256(
|
||||
signing_key, oemcrypto_core_message + message_str);
|
||||
|
||||
license->request.assign(serialized_request_.begin(),
|
||||
serialized_request_.end());
|
||||
if (!oemcrypto_core_message.empty()) {
|
||||
license->core_message.assign(oemcrypto_core_message.begin(),
|
||||
oemcrypto_core_message.end());
|
||||
}
|
||||
license->message.assign(message_str.begin(), message_str.end());
|
||||
license->signature.assign(signature_str.begin(), signature_str.end());
|
||||
license->session_key.assign(session_key_str.begin(), session_key_str.end());
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
@@ -14,6 +14,8 @@ namespace widevine {
|
||||
|
||||
struct License {
|
||||
std::vector<uint8_t> request;
|
||||
|
||||
std::vector<uint8_t> core_message;
|
||||
std::vector<uint8_t> message;
|
||||
std::vector<uint8_t> signature;
|
||||
|
||||
@@ -68,6 +70,10 @@ class TestLicenseBuilder {
|
||||
|
||||
void SetVerificationStatus(VerificationStatus setting);
|
||||
|
||||
// If set, then Build() will populate |core_message| in License with the
|
||||
// matching ODK core message. If not set, then |core_message| will be empty.
|
||||
void SetUseODK(bool setting);
|
||||
|
||||
// Gets the serialized license request and response (in components) that would
|
||||
// have been used in the license exchange.
|
||||
void Build(const RsaPublicKey& public_key, License* license) const;
|
||||
@@ -79,6 +85,7 @@ class TestLicenseBuilder {
|
||||
video_widevine::License response_;
|
||||
std::string serialized_request_;
|
||||
std::string container_key_;
|
||||
bool use_odk_ = false;
|
||||
};
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
#include "base/base64.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace base {
|
||||
|
||||
namespace {
|
||||
|
||||
static const char kBase64Codes[] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
||||
|
||||
// Calculates a/b using round-up division (only works for numbers
|
||||
// greater than 0).
|
||||
constexpr size_t CeilDivide(size_t a, size_t b) {
|
||||
return ((a - 1) / b) + 1;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void Base64Encode(const std::string& input, std::string* output) {
|
||||
if (input.empty()) {
|
||||
output->clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// |temp| stores a 24-bit block that is treated as an array where insertions
|
||||
// occur from high to low.
|
||||
uint32_t temp = 0;
|
||||
size_t out_index = 0;
|
||||
const size_t out_size = CeilDivide(input.size(), 3) * 4;
|
||||
std::string result(out_size, '\0');
|
||||
for (size_t i = 0; i < input.size(); i++) {
|
||||
// "insert" 8-bits of data
|
||||
temp = (temp << 8) | (input[i] & 0x0FF);
|
||||
|
||||
if (i % 3 == 2) {
|
||||
result[out_index++] = kBase64Codes[(temp >> 18) & 0x3f];
|
||||
result[out_index++] = kBase64Codes[(temp >> 12) & 0x3f];
|
||||
result[out_index++] = kBase64Codes[(temp >> 6) & 0x3f];
|
||||
result[out_index++] = kBase64Codes[temp & 0x3f];
|
||||
temp = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (input.size() % 3 == 1) {
|
||||
result[out_index++] = kBase64Codes[(temp >> 18) & 0x3f];
|
||||
result[out_index++] = kBase64Codes[(temp >> 12) & 0x3f];
|
||||
result[out_index++] = '=';
|
||||
result[out_index++] = '=';
|
||||
} else if (input.size() % 3 == 2) {
|
||||
result[out_index++] = kBase64Codes[(temp >> 18) & 0x3f];
|
||||
result[out_index++] = kBase64Codes[(temp >> 12) & 0x3f];
|
||||
result[out_index++] = kBase64Codes[(temp >> 6) & 0x3f];
|
||||
result[out_index++] = '=';
|
||||
}
|
||||
|
||||
output->swap(result);
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
@@ -1,18 +0,0 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
#ifndef BASE_BASE64_H_
|
||||
#define BASE_BASE64_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace base {
|
||||
|
||||
// Encodes the input string in base64.
|
||||
// Note that the Chromium code uses base::StringPiece for |input|, but to
|
||||
// avoid dragging in too much code, use std::string instead (which is
|
||||
// convertable to base::StringPiece automatically).
|
||||
void Base64Encode(const std::string& input, std::string* output);
|
||||
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_BASE64_H_
|
||||
@@ -1,14 +0,0 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
|
||||
#include "absl/strings/escaping.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
std::string HexEncode(const void* bytes, size_t size) {
|
||||
return absl::BytesToHexString(
|
||||
std::string(reinterpret_cast<const char*>(bytes), size));
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
@@ -1,17 +0,0 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
#ifndef BASE_STRINGS_STRING_NUMBER_CONVERSIONS_H_
|
||||
#define BASE_STRINGS_STRING_NUMBER_CONVERSIONS_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
|
||||
namespace base {
|
||||
|
||||
// Returns a hex string representation of a binary buffer.
|
||||
std::string HexEncode(const void* bytes, size_t size);
|
||||
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_STRINGS_STRING_NUMBER_CONVERSIONS_H_
|
||||
@@ -5,6 +5,13 @@ package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load("@com_google_protobuf//:protobuf.bzl", "cc_proto_library")
|
||||
|
||||
cc_proto_library(
|
||||
name = "certificate_provisioning_proto",
|
||||
srcs = ["certificate_provisioning.proto"],
|
||||
default_runtime = "@com_google_protobuf//:protobuf",
|
||||
protoc = "@com_google_protobuf//:protoc",
|
||||
)
|
||||
|
||||
cc_proto_library(
|
||||
name = "client_identification_proto",
|
||||
srcs = ["client_identification.proto"],
|
||||
|
||||
43
chromium_deps/cdm/protos/defs/certificate_provisioning.proto
Normal file
43
chromium_deps/cdm/protos/defs/certificate_provisioning.proto
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
// This file is a reduced copy from the Google3 code and is only here to allow
|
||||
// the ODK code to compile. This is not used in the CDM.
|
||||
|
||||
syntax = "proto2";
|
||||
|
||||
package video_widevine;
|
||||
|
||||
// Provisioning response sent by the provisioning server to client devices.
|
||||
// This message is used for both regular Widevine DRM certificates and for
|
||||
// application-specific X.509 certificates.
|
||||
message ProvisioningResponse {
|
||||
message OtaKeybox {
|
||||
// Iv used along with SessionKeys.encryption_key for encrypting device key.
|
||||
optional bytes device_key_encryption_iv = 1;
|
||||
// Device key component of the keybox, encrypted using the
|
||||
// SessionKeys.encryption_key in the request and |device_key_encryption_iv|
|
||||
// above.
|
||||
optional bytes encrypted_device_key = 2;
|
||||
// Device CA token component of the keybox.
|
||||
optional bytes device_ca_token = 3;
|
||||
}
|
||||
// AES-128 encrypted device private RSA key. PKCS#1 ASN.1 DER-encoded.
|
||||
// Required. For X.509 certificates, the private RSA key may also include
|
||||
// a prefix as specified by private_key_prefix in the X509CertificateMetadata
|
||||
// proto message.
|
||||
optional bytes device_rsa_key = 1;
|
||||
// Initialization vector used to encrypt device_rsa_key. Required.
|
||||
optional bytes device_rsa_key_iv = 2;
|
||||
// For Widevine DRM certificates, this contains the serialized
|
||||
// SignedDrmCertificate. For X.509 certificates, this contains the PEM
|
||||
// encoded X.509 certificate. Required.
|
||||
optional bytes device_certificate = 3;
|
||||
// Nonce value matching nonce in ProvisioningRequest. Required.
|
||||
optional bytes nonce = 4;
|
||||
// Key used to wrap device_rsa_key when DRM provisioning an OEM factory
|
||||
// provisioned device. Encrypted with the device OEM public key using
|
||||
// RSA-OAEP.
|
||||
optional bytes wrapping_key = 5;
|
||||
// Only populated in OTA keybox provisioning response.
|
||||
optional OtaKeybox ota_keybox = 6;
|
||||
}
|
||||
@@ -111,6 +111,13 @@ message License {
|
||||
// Enables "soft enforcement" of playback_duration_seconds, letting the user
|
||||
// finish playback even if short window expires. Optional.
|
||||
optional bool soft_enforce_playback_duration = 14 [default = false];
|
||||
|
||||
// Enables "soft enforcement" of rental_duration_seconds. Initial playback
|
||||
// must always start before rental duration expires. In order to allow
|
||||
// subsequent playbacks to start after the rental duration expires,
|
||||
// soft_enforce_playback_duration must be true. Otherwise, subsequent
|
||||
// playbacks will not be allowed once rental duration expires. Optional.
|
||||
optional bool soft_enforce_rental_duration = 15 [default = true];
|
||||
}
|
||||
// LINT.ThenChange(//depot/google3/google/chrome/widevine/licensedata/v1/license_policy.proto)
|
||||
|
||||
|
||||
2
external/BUILD
vendored
Normal file
2
external/BUILD
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# An empty BUILD file. This is needed for the "*.BUILD" files to be loaded by
|
||||
# bazel.
|
||||
70
external/odk.BUILD
vendored
Normal file
70
external/odk.BUILD
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
# Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
filegroup(
|
||||
name = "odk_common_hdrs",
|
||||
srcs = [
|
||||
"oemcrypto/odk/src/odk_serialize.h",
|
||||
"oemcrypto/odk/src/odk_structs_priv.h",
|
||||
"oemcrypto/odk/src/serialization_base.h",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "core",
|
||||
srcs = [
|
||||
"oemcrypto/odk/src/odk.c",
|
||||
"oemcrypto/odk/src/odk_assert.h",
|
||||
"oemcrypto/odk/src/odk_endian.h",
|
||||
"oemcrypto/odk/src/odk_overflow.c",
|
||||
"oemcrypto/odk/src/odk_overflow.h",
|
||||
"oemcrypto/odk/src/odk_serialize.c",
|
||||
"oemcrypto/odk/src/odk_timer.c",
|
||||
"oemcrypto/odk/src/odk_util.c",
|
||||
"oemcrypto/odk/src/odk_util.h",
|
||||
"oemcrypto/odk/src/serialization_base.c",
|
||||
":odk_common_hdrs",
|
||||
],
|
||||
hdrs = [
|
||||
"oemcrypto/odk/include/OEMCryptoCENCCommon.h",
|
||||
"oemcrypto/odk/include/odk.h",
|
||||
"oemcrypto/odk/include/odk_structs.h",
|
||||
"oemcrypto/odk/include/odk_target.h",
|
||||
],
|
||||
copts = ["-std=c99"],
|
||||
includes = [
|
||||
"oemcrypto/odk/include",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "serialization",
|
||||
srcs = [
|
||||
"oemcrypto/odk/src/core_message_deserialize.cpp",
|
||||
"oemcrypto/odk/src/core_message_serialize.cpp",
|
||||
"oemcrypto/odk/src/core_message_serialize_proto.cpp",
|
||||
":odk_common_hdrs",
|
||||
],
|
||||
hdrs = [
|
||||
"oemcrypto/odk/include/core_message_deserialize.h",
|
||||
"oemcrypto/odk/include/core_message_serialize.h",
|
||||
"oemcrypto/odk/include/core_message_serialize_proto.h",
|
||||
"oemcrypto/odk/include/core_message_types.h",
|
||||
],
|
||||
deps = [
|
||||
":core",
|
||||
"@//odk_deps"
|
||||
],
|
||||
includes = [
|
||||
"oemcrypto/odk/include",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "odk",
|
||||
deps = [
|
||||
":core",
|
||||
":serialization",
|
||||
],
|
||||
)
|
||||
@@ -34,6 +34,36 @@ cc_library(
|
||||
"//crypto_utils:aes_ctr_encryptor",
|
||||
"//crypto_utils:crypto_util",
|
||||
"//crypto_utils:rsa_key",
|
||||
"//external:odk",
|
||||
],
|
||||
)
|
||||
|
||||
# Create a shared library of the whole api. The library will appear in the
|
||||
# target specific directory under "//bazel-bin".
|
||||
#
|
||||
# In order for this to work, the white-box source needs to be in `srcs`. If it
|
||||
# was included via `deps`, when the library is loaded, the symbols will be
|
||||
# missing.
|
||||
cc_binary(
|
||||
name = "whiteboxapi",
|
||||
srcs = [
|
||||
"aead_whitebox_impl.cc",
|
||||
"license_whitebox_impl.cc",
|
||||
],
|
||||
linkshared = True,
|
||||
deps = [
|
||||
":memory_util",
|
||||
"//api:aead_whitebox",
|
||||
"//api:license_whitebox",
|
||||
"//api:result",
|
||||
"//chromium_deps/cdm/keys:dev_certs",
|
||||
"//chromium_deps/cdm/protos:license_protocol_proto",
|
||||
"//chromium_deps/third_party/boringssl",
|
||||
"//crypto_utils:aes_cbc_decryptor",
|
||||
"//crypto_utils:aes_ctr_encryptor",
|
||||
"//crypto_utils:crypto_util",
|
||||
"//crypto_utils:rsa_key",
|
||||
"//external:odk",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -75,6 +105,7 @@ cc_test(
|
||||
deps = [
|
||||
":license_whitebox",
|
||||
":test_data",
|
||||
"//api:license_whitebox_core_message_test",
|
||||
"//api:license_whitebox_test",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -15,6 +15,10 @@
|
||||
#include "crypto_utils/crypto_util.h"
|
||||
#include "crypto_utils/rsa_key.h"
|
||||
#include "impl/reference/memory_util.h"
|
||||
#include "oemcrypto/odk/include/odk.h"
|
||||
#include "oemcrypto/odk/include/odk_structs.h"
|
||||
#include "oemcrypto/odk/src/odk_serialize.h"
|
||||
#include "oemcrypto/odk/src/serialization_base.h"
|
||||
#include "third_party/boringssl/src/include/openssl/aes.h"
|
||||
#include "third_party/boringssl/src/include/openssl/cmac.h"
|
||||
#include "third_party/boringssl/src/include/openssl/err.h"
|
||||
@@ -39,6 +43,145 @@ struct ContentKey {
|
||||
// Key used to decrypt content.
|
||||
std::vector<uint8_t> key;
|
||||
};
|
||||
|
||||
// Helper function to decrypt |encrypted| into |decrypted| using |decryptor|
|
||||
// and |iv|. Done as the protobuf and ODK code use std::string, AesCbcDecryptor
|
||||
// requires uint8_t* + size_t parameters.
|
||||
bool Decrypt(AesCbcDecryptor& decryptor,
|
||||
const std::string& iv,
|
||||
const std::string& encrypted,
|
||||
std::vector<uint8_t>* decrypted) {
|
||||
DCHECK_GE(decrypted->size(), encrypted.size());
|
||||
return decryptor.Decrypt(reinterpret_cast<const uint8_t*>(iv.data()),
|
||||
iv.size(),
|
||||
reinterpret_cast<const uint8_t*>(encrypted.data()),
|
||||
encrypted.size(), decrypted->data());
|
||||
}
|
||||
|
||||
bool IsOdkVersionSupported(uint16_t major_version, uint16_t minor_version) {
|
||||
// Only ODK v16.5 and later support the fields needed.
|
||||
constexpr uint16_t first_major_version_supported = 16;
|
||||
constexpr uint16_t first_minor_version_supported = 5;
|
||||
|
||||
return (major_version > first_major_version_supported) ||
|
||||
((major_version == first_major_version_supported) &&
|
||||
(minor_version >= first_minor_version_supported));
|
||||
}
|
||||
|
||||
// Helper function to extract the substring |item| from the provided |buffer|.
|
||||
std::string ExtractItem(const OEMCrypto_Substring& item,
|
||||
const std::string& buffer) {
|
||||
return buffer.substr(item.offset, item.length);
|
||||
}
|
||||
|
||||
video_widevine::License_KeyContainer_SecurityLevel ExtractLevel(
|
||||
const std::string& key_control_block) {
|
||||
// The key control block is an 128 bit structure containing the following
|
||||
// fields. The fields are defined to be in big-endian byte order.
|
||||
//
|
||||
// Bytes 0..3: Verification.
|
||||
// Constant bytes “kctl”, “kc09”, “kc10”, “kc11”, ... “kc15”.
|
||||
// Bytes 4..7: Obsolete.
|
||||
// Bytes 8..11: Nonce.
|
||||
// Bytes 12..15: Control Bits
|
||||
// Bits 27..26: Security_Level (Only for L3 white-box implementations)
|
||||
// 0 = SW_SECURE_CRYPTO
|
||||
// 1 = SW_SECURE_DECODE
|
||||
// 2 = HW_SECURE_CRYPTO
|
||||
// 3 = HW_SECURE_DECODE or HW_SECURE_ALL
|
||||
|
||||
// Limited checks to verify that this is proper key control block.
|
||||
// If not valid, assume it's the highest level.
|
||||
if ((key_control_block.size() != 16u) || (key_control_block[0] != 'k') ||
|
||||
(key_control_block[1] != 'c')) {
|
||||
return video_widevine::License_KeyContainer_SecurityLevel_HW_SECURE_DECODE;
|
||||
}
|
||||
|
||||
// Extract bits 26..27 from Control Bits.
|
||||
int security_level = (key_control_block[12] & 0x0C) >> 2;
|
||||
switch (security_level) {
|
||||
case 0:
|
||||
return video_widevine::
|
||||
License_KeyContainer_SecurityLevel_SW_SECURE_CRYPTO;
|
||||
case 1:
|
||||
return video_widevine::
|
||||
License_KeyContainer_SecurityLevel_SW_SECURE_DECODE;
|
||||
case 2:
|
||||
return video_widevine::
|
||||
License_KeyContainer_SecurityLevel_HW_SECURE_CRYPTO;
|
||||
default:
|
||||
return video_widevine::
|
||||
License_KeyContainer_SecurityLevel_HW_SECURE_DECODE;
|
||||
}
|
||||
}
|
||||
|
||||
// Creates and returns a ContentKey based on the values provided.
|
||||
// |level| determines whether decrypt or masked_decrypt is allowed.
|
||||
// |is_hw_verified|, if set, overrides |level| so that both decrypt and
|
||||
// masked_decrypt is allowed. |Key| is the decryption key, and is only
|
||||
// returned in ContentKey if decrypt or masked_decrypt is allowed.
|
||||
// Otherwise |key| is dropped.
|
||||
ContentKey CreateContentKey(
|
||||
video_widevine::License_KeyContainer_SecurityLevel level,
|
||||
bool is_hw_verified,
|
||||
const std::vector<uint8_t>& key) {
|
||||
ContentKey content_key;
|
||||
|
||||
switch (level) {
|
||||
case video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_CRYPTO:
|
||||
content_key.allow_decrypt = true;
|
||||
content_key.allow_masked_decrypt = true;
|
||||
break;
|
||||
case video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_DECODE:
|
||||
content_key.allow_decrypt = false;
|
||||
content_key.allow_masked_decrypt = true;
|
||||
break;
|
||||
default:
|
||||
content_key.allow_decrypt = false;
|
||||
content_key.allow_masked_decrypt = false;
|
||||
break;
|
||||
}
|
||||
|
||||
content_key.allow_decrypt |= is_hw_verified;
|
||||
content_key.allow_masked_decrypt |= is_hw_verified;
|
||||
|
||||
// Unless we are going to use the key, we don't want to save this key as
|
||||
// it will only risk exposing it. We only have an entry for it so we can
|
||||
// handle errors correctly.
|
||||
if (content_key.allow_decrypt || content_key.allow_masked_decrypt) {
|
||||
content_key.key = key;
|
||||
}
|
||||
|
||||
return content_key;
|
||||
}
|
||||
|
||||
// This function uses 16 non-linear bijections that are applied to a byte.
|
||||
// This is "Example Masking Function 1" from the shared document
|
||||
// https://docs.google.com/document/d/1xWPwlFHyjT8YzWhY3TyaC02SQvclC_dkEpliGPOUk_o#heading=h.j64j2z3b9v99
|
||||
uint8_t MaskingFunction1(uint8_t input) {
|
||||
const size_t x = 97 * input;
|
||||
const size_t y = x + 96;
|
||||
return y % 257;
|
||||
}
|
||||
|
||||
// This function performs the reverse of MaskingFunction1().
|
||||
uint8_t InverseMaskingFunction1(uint8_t input) {
|
||||
// Rather than compute the byte each time, generate a lookup table for all
|
||||
// 256 values the first time this is called.
|
||||
static bool initialized = false;
|
||||
static uint8_t mapping[256];
|
||||
if (!initialized) {
|
||||
initialized = true;
|
||||
uint8_t byte = 0;
|
||||
do {
|
||||
mapping[MaskingFunction1(byte)] = byte;
|
||||
++byte;
|
||||
} while (byte != 0);
|
||||
}
|
||||
|
||||
return mapping[input];
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// The white-box type can't be in the namespace as it is defined in the header.
|
||||
@@ -177,6 +320,7 @@ bool IsPlatformHardwareVerified(const video_widevine::License& license) {
|
||||
// false values, then we know something was found to be invalid.
|
||||
return flags[0] && flags[1];
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
WB_Result WB_License_Create(const uint8_t* whitebox_init_data,
|
||||
@@ -230,7 +374,7 @@ WB_Result WB_License_SignLicenseRequest(const WB_License_Whitebox* whitebox,
|
||||
}
|
||||
|
||||
std::string result;
|
||||
DCHECK(whitebox->key->GenerateSignature(
|
||||
CHECK(whitebox->key->GenerateSignature(
|
||||
std::string(license_request, license_request + license_request_size),
|
||||
&result));
|
||||
|
||||
@@ -246,6 +390,8 @@ WB_Result WB_License_SignLicenseRequest(const WB_License_Whitebox* whitebox,
|
||||
}
|
||||
|
||||
WB_Result WB_License_ProcessLicenseResponse(WB_License_Whitebox* whitebox,
|
||||
const uint8_t* core_message,
|
||||
size_t core_message_size,
|
||||
const uint8_t* message,
|
||||
size_t message_size,
|
||||
const uint8_t* signature,
|
||||
@@ -262,6 +408,13 @@ WB_Result WB_License_ProcessLicenseResponse(WB_License_Whitebox* whitebox,
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
// |core_message| is optional. If provided, both |core_message| and
|
||||
// |core_message_size| need to be set.
|
||||
if (core_message_size > 0 && !core_message) {
|
||||
DVLOG(1) << "Invalid parameter: core_message null pointer.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
// If we have already loaded a license, ProcessLicenseResponse() may not be
|
||||
// called again. The white-box needs to be destroyed and a new one needs
|
||||
// to be created.
|
||||
@@ -298,16 +451,8 @@ WB_Result WB_License_ProcessLicenseResponse(WB_License_Whitebox* whitebox,
|
||||
DVLOG(1) << "Invalid parameter: invalid session key size.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
const std::string server_signing_key =
|
||||
signing_key_material.substr(0, kSigningKeySizeBytes);
|
||||
if (!widevine::crypto_util::VerifySignatureHmacSha256(
|
||||
server_signing_key,
|
||||
std::string(signature, signature + signature_size),
|
||||
std::string(message, message + message_size))) {
|
||||
DVLOG(1) << "Failed to verify signed message.";
|
||||
return WB_RESULT_INVALID_SIGNATURE;
|
||||
}
|
||||
|
||||
std::string decryption_key = widevine::crypto_util::DeriveKey(
|
||||
decrypted_session_key, widevine::crypto_util::kWrappingKeyLabel,
|
||||
@@ -318,61 +463,106 @@ WB_Result WB_License_ProcessLicenseResponse(WB_License_Whitebox* whitebox,
|
||||
return WB_RESULT_INVALID_SIGNATURE;
|
||||
}
|
||||
|
||||
const std::string message_str(message, message + message_size);
|
||||
std::string core_message_str;
|
||||
if (core_message_size > 0) {
|
||||
core_message_str.assign(reinterpret_cast<const char*>(core_message),
|
||||
core_message_size);
|
||||
}
|
||||
const std::string combined_message_str = core_message_str + message_str;
|
||||
if (!widevine::crypto_util::VerifySignatureHmacSha256(
|
||||
server_signing_key,
|
||||
std::string(signature, signature + signature_size),
|
||||
combined_message_str)) {
|
||||
DVLOG(1) << "Failed to verify signed message.";
|
||||
return WB_RESULT_INVALID_SIGNATURE;
|
||||
}
|
||||
|
||||
// If |core_message| is provided, parse it into |parsed_license| and validate
|
||||
// that it's an appropriate version.
|
||||
uint16_t odk_major_version = 0;
|
||||
uint16_t odk_minor_version = 0;
|
||||
ODK_ParsedLicense parsed_license;
|
||||
if (core_message_size > 0) {
|
||||
// Decode |core_message|.
|
||||
Message* msg = NULL;
|
||||
AllocateMessage(&msg, message_block);
|
||||
InitMessage(msg,
|
||||
reinterpret_cast<uint8_t*>(
|
||||
const_cast<char*>(combined_message_str.data())),
|
||||
combined_message_str.size());
|
||||
// The core message is at the beginning of the buffer, and is the part to be
|
||||
// parsed.
|
||||
SetSize(msg, core_message_size);
|
||||
|
||||
ODK_LicenseResponse license_response = {{{0}}, &parsed_license, {0}};
|
||||
Unpack_ODK_LicenseResponse(msg, &license_response);
|
||||
|
||||
odk_major_version =
|
||||
license_response.request.core_message.nonce_values.api_major_version;
|
||||
odk_minor_version =
|
||||
license_response.request.core_message.nonce_values.api_minor_version;
|
||||
if ((GetStatus(msg) != MESSAGE_STATUS_OK) ||
|
||||
(license_response.request.core_message.message_type !=
|
||||
ODK_License_Response_Type)) {
|
||||
DVLOG(1) << "Failed to validate core message.";
|
||||
return WB_RESULT_INVALID_SIGNATURE;
|
||||
}
|
||||
}
|
||||
|
||||
AesCbcDecryptor decryptor;
|
||||
CHECK(
|
||||
decryptor.SetKey(reinterpret_cast<const uint8_t*>(decryption_key.data()),
|
||||
decryption_key.size()));
|
||||
|
||||
video_widevine::License license;
|
||||
if (!license.ParseFromArray(message, message_size)) {
|
||||
DVLOG(1) << "Invalid parameter: Invalid license.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
std::string server_renewal_key;
|
||||
std::string client_renewal_key;
|
||||
std::map<std::string, ContentKey> content_keys;
|
||||
|
||||
// When the platform is hardware verified, all keys are unlocked and are
|
||||
// available to be used with either decrypt function. Use this flag to
|
||||
// overwrite the default values internal our internal policies to enable
|
||||
// this behaviour.
|
||||
const bool is_verified = IsPlatformHardwareVerified(license);
|
||||
// Even if |core_message| is provided, only ODK v16.5 and later support the
|
||||
// fields needed. If an older API is used, ignore it and use the protobuf
|
||||
// as if |core_message| was not provided.
|
||||
if (IsOdkVersionSupported(odk_major_version, odk_minor_version)) {
|
||||
// Start by extracting the signing key.
|
||||
const std::string signing_key_encrypted =
|
||||
ExtractItem(parsed_license.enc_mac_keys, message_str);
|
||||
const std::string signing_key_iv =
|
||||
ExtractItem(parsed_license.enc_mac_keys_iv, message_str);
|
||||
if (!signing_key_encrypted.empty() && !signing_key_iv.empty()) {
|
||||
std::vector<uint8_t> unwrapped_signing_key(signing_key_encrypted.size());
|
||||
if (!Decrypt(decryptor, signing_key_iv, signing_key_encrypted,
|
||||
&unwrapped_signing_key)) {
|
||||
DVLOG(1) << "Invalid parameter: Invalid enc_mac_keys.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
for (const auto& key : license.key()) {
|
||||
// If this is not a key we're interested in, skip it as soon as possible.
|
||||
// Don't even bother unwrapping it.
|
||||
if (key.type() != KeyContainer::SIGNING &&
|
||||
key.type() != KeyContainer::CONTENT) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::string wrapped_key = key.key();
|
||||
std::vector<uint8_t> unwrapped_key(wrapped_key.size());
|
||||
|
||||
if (!decryptor.Decrypt(reinterpret_cast<const uint8_t*>(key.iv().data()),
|
||||
key.iv().size(),
|
||||
reinterpret_cast<const uint8_t*>(wrapped_key.data()),
|
||||
wrapped_key.size(), unwrapped_key.data())) {
|
||||
// The input has to be a specific length, so if it is not, it means that
|
||||
// something is wrong with the license.
|
||||
DVLOG(1) << "Invalid parameter: Invalid license.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (key.type() == KeyContainer::SIGNING) {
|
||||
if (unwrapped_key.size() < kSigningKeySizeBytes * 2) {
|
||||
if (unwrapped_signing_key.size() < kSigningKeySizeBytes * 2) {
|
||||
DVLOG(1) << "Invalid parameter: Invalid signing key.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
const std::string signing_key(unwrapped_key.begin(), unwrapped_key.end());
|
||||
const std::string signing_key(unwrapped_signing_key.begin(),
|
||||
unwrapped_signing_key.end());
|
||||
server_renewal_key = signing_key.substr(0, kSigningKeySizeBytes);
|
||||
client_renewal_key =
|
||||
signing_key.substr(kSigningKeySizeBytes, kSigningKeySizeBytes);
|
||||
} else if (key.type() == KeyContainer::CONTENT) {
|
||||
constexpr size_t kContentKeySizeBytes = 16;
|
||||
}
|
||||
|
||||
// Now extract all the content keys.
|
||||
for (size_t i = 0; i < parsed_license.key_array_length; ++i) {
|
||||
const OEMCrypto_KeyObject& key = parsed_license.key_array[i];
|
||||
const std::string key_id = ExtractItem(key.key_id, message_str);
|
||||
|
||||
const std::string iv = ExtractItem(key.key_data_iv, message_str);
|
||||
const std::string wrapped_key = ExtractItem(key.key_data, message_str);
|
||||
std::vector<uint8_t> unwrapped_key(wrapped_key.size());
|
||||
|
||||
if (!Decrypt(decryptor, iv, wrapped_key, &unwrapped_key)) {
|
||||
DVLOG(1) << "Invalid parameter: Invalid key.key_data.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
constexpr size_t kContentKeySizeBytes = 16;
|
||||
if (unwrapped_key.size() < kContentKeySizeBytes) {
|
||||
DVLOG(1) << "Invalid parameter: Invalid content key.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
@@ -380,39 +570,75 @@ WB_Result WB_License_ProcessLicenseResponse(WB_License_Whitebox* whitebox,
|
||||
|
||||
unwrapped_key.resize(kContentKeySizeBytes);
|
||||
|
||||
ContentKey content_key;
|
||||
// When the platform is hardware verified, all keys are unlocked and are
|
||||
// available to be used with either decrypt function. The license server
|
||||
// adjusts the level returned inside the key control block to handle
|
||||
// this.
|
||||
const std::string key_control_block =
|
||||
ExtractItem(key.key_control, message_str);
|
||||
content_keys[key_id] =
|
||||
CreateContentKey(ExtractLevel(key_control_block),
|
||||
/* is_hw_verified */ false, unwrapped_key);
|
||||
}
|
||||
} else {
|
||||
// Core message not provided or an old version, so extract the keys from
|
||||
// the protobuf.
|
||||
video_widevine::License license;
|
||||
if (!license.ParseFromArray(message, message_size)) {
|
||||
DVLOG(1) << "Invalid parameter: Invalid license.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
switch (key.level()) {
|
||||
case video_widevine::
|
||||
License_KeyContainer_SecurityLevel_SW_SECURE_CRYPTO:
|
||||
content_key.allow_decrypt = true;
|
||||
content_key.allow_masked_decrypt = true;
|
||||
break;
|
||||
case video_widevine::
|
||||
License_KeyContainer_SecurityLevel_SW_SECURE_DECODE:
|
||||
content_key.allow_decrypt = false;
|
||||
content_key.allow_masked_decrypt = true;
|
||||
break;
|
||||
default:
|
||||
content_key.allow_decrypt = false;
|
||||
content_key.allow_masked_decrypt = false;
|
||||
break;
|
||||
// When the platform is hardware verified, all keys are unlocked and are
|
||||
// available to be used with either decrypt function. Use this flag to
|
||||
// overwrite the calculated values for the internal policies to enable
|
||||
// this behaviour.
|
||||
const bool is_verified = IsPlatformHardwareVerified(license);
|
||||
|
||||
for (const auto& key : license.key()) {
|
||||
// If this is not a key we're interested in, skip it as soon as possible.
|
||||
// Don't even bother unwrapping it.
|
||||
if (key.type() != KeyContainer::SIGNING &&
|
||||
key.type() != KeyContainer::CONTENT) {
|
||||
continue;
|
||||
}
|
||||
|
||||
content_key.allow_decrypt |= is_verified;
|
||||
content_key.allow_masked_decrypt |= is_verified;
|
||||
const std::string wrapped_key = key.key();
|
||||
std::vector<uint8_t> unwrapped_key(wrapped_key.size());
|
||||
|
||||
// Unless we are going to use the key, we don't want to save this key as
|
||||
// it will only risk exposing it. We only have an entry for it so we can
|
||||
// handle errors correctly.
|
||||
if (content_key.allow_decrypt || content_key.allow_masked_decrypt) {
|
||||
content_key.key = std::move(unwrapped_key);
|
||||
if (!Decrypt(decryptor, key.iv(), wrapped_key, &unwrapped_key)) {
|
||||
// The input has to be a specific length, so if it is not, it means that
|
||||
// something is wrong with the license.
|
||||
DVLOG(1) << "Invalid parameter: Invalid license.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
content_keys[key.id()] = content_key;
|
||||
} else {
|
||||
// We should have already skipped over this key.
|
||||
CHECK(false);
|
||||
if (key.type() == KeyContainer::SIGNING) {
|
||||
if (unwrapped_key.size() < kSigningKeySizeBytes * 2) {
|
||||
DVLOG(1) << "Invalid parameter: Invalid signing key.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
const std::string signing_key(unwrapped_key.begin(),
|
||||
unwrapped_key.end());
|
||||
server_renewal_key = signing_key.substr(0, kSigningKeySizeBytes);
|
||||
client_renewal_key =
|
||||
signing_key.substr(kSigningKeySizeBytes, kSigningKeySizeBytes);
|
||||
} else if (key.type() == KeyContainer::CONTENT) {
|
||||
constexpr size_t kContentKeySizeBytes = 16;
|
||||
|
||||
if (unwrapped_key.size() < kContentKeySizeBytes) {
|
||||
DVLOG(1) << "Invalid parameter: Invalid content key.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
unwrapped_key.resize(kContentKeySizeBytes);
|
||||
content_keys[key.id()] =
|
||||
CreateContentKey(key.level(), is_verified, unwrapped_key);
|
||||
} else {
|
||||
// We should have already skipped over this key.
|
||||
CHECK(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -662,14 +888,13 @@ WB_Result WB_License_MaskedDecrypt(const WB_License_Whitebox* whitebox,
|
||||
return result;
|
||||
}
|
||||
|
||||
// Trivial implementation that simply takes the decrypted output and XORs it
|
||||
// with a fixed pattern. |output|'s size is based on |masked_output_data| so
|
||||
// we shouldn't need to worry about overflow. This logic must be mirrored in
|
||||
// Now apply the masking function to the data. This logic must be mirrored in
|
||||
// Unmask().
|
||||
const uint8_t* mask = kSecretStringPattern;
|
||||
const size_t mask_size = kSecretStringPatternSize;
|
||||
for (size_t i = 0; i < output.size(); ++i) {
|
||||
masked_output_data[i] = output[i] ^ mask[i % mask_size];
|
||||
masked_output_data[i] =
|
||||
InverseMaskingFunction1(output[i] ^ mask[i % mask_size]);
|
||||
}
|
||||
|
||||
return WB_RESULT_OK;
|
||||
@@ -689,6 +914,6 @@ void WB_License_Unmask(const uint8_t* masked_data,
|
||||
for (size_t local_i = 0; local_i < size; local_i++) {
|
||||
const size_t global_i = offset + local_i;
|
||||
const uint8_t mask = secret_string[global_i % secret_string_size];
|
||||
unmasked_data[local_i] = masked_data[global_i] ^ mask;
|
||||
unmasked_data[local_i] = MaskingFunction1(masked_data[global_i]) ^ mask;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
#include "impl/reference/string_view_util.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
absl::string_view AsStringView(const uint8_t* buffer, size_t buffer_size) {
|
||||
return absl::string_view(reinterpret_cast<const char*>(buffer), buffer_size);
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
@@ -1,17 +0,0 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
#ifndef WHITEBOX_IMPL_REFERENCE_STRING_VIEW_UTIL_H_
|
||||
#define WHITEBOX_IMPL_REFERENCE_STRING_VIEW_UTIL_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include "absl/strings/string_view.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
// Create a string view out of a uint8_t array. String view won't create a copy
|
||||
// copy of the data.
|
||||
absl::string_view AsStringView(const uint8_t* buffer, size_t buffer_size);
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
#endif // WHITEBOX_IMPL_REFERENCE_STRING_VIEW_UTIL_H_
|
||||
13
odk_deps/BUILD
Normal file
13
odk_deps/BUILD
Normal file
@@ -0,0 +1,13 @@
|
||||
# Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
cc_library(
|
||||
name = "odk_deps",
|
||||
hdrs = ["license_protocol.pb.h"],
|
||||
strip_include_prefix = "//odk_deps/",
|
||||
deps = [
|
||||
"//chromium_deps/cdm/protos/defs:certificate_provisioning_proto",
|
||||
"//chromium_deps/cdm/protos/defs:license_protocol_proto",
|
||||
],
|
||||
)
|
||||
11
odk_deps/license_protocol.pb.h
Normal file
11
odk_deps/license_protocol.pb.h
Normal file
@@ -0,0 +1,11 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
#ifndef ODK_DEPS_LICENSE_PROTOCOL_PB_H_
|
||||
#define ODK_DEPS_LICENSE_PROTOCOL_PB_H_
|
||||
|
||||
// Because the Android repo combines the two protobufs, we need to include both
|
||||
// of them in this header so that the ODK code can find it.
|
||||
#include "chromium_deps/cdm/protos/defs/certificate_provisioning.pb.h"
|
||||
#include "chromium_deps/cdm/protos/defs/license_protocol.pb.h"
|
||||
|
||||
#endif // ODK_DEPS_LICENSE_PROTOCOL_PB_H_
|
||||
Reference in New Issue
Block a user