diff --git a/WORKSPACE b/WORKSPACE index b0ea0cd..7d8a4c3 100644 --- a/WORKSPACE +++ b/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", +) diff --git a/api/BUILD b/api/BUILD index fb5978d..090e158 100644 --- a/api/BUILD +++ b/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, diff --git a/api/aead_whitebox.h b/api/aead_whitebox.h index 28f39ef..cb9f5dc 100644 --- a/api/aead_whitebox.h +++ b/api/aead_whitebox.h @@ -6,6 +6,7 @@ #include #include +#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 } diff --git a/api/export.h b/api/export.h new file mode 100644 index 0000000..4d22b97 --- /dev/null +++ b/api/export.h @@ -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_ diff --git a/api/license_test_helper.cc b/api/license_test_helper.cc deleted file mode 100644 index 04c80c6..0000000 --- a/api/license_test_helper.cc +++ /dev/null @@ -1,345 +0,0 @@ -// Copyright 2020 Google LLC. All Rights Reserved. - -#include "api/license_test_helper.h" - -#include - -#include "base/logging.h" -#include "cdm/keys/certs.h" -#include "cdm/protos/license_protocol.pb.h" -#include "crypto_utils/aes_cbc_encryptor.h" -#include "crypto_utils/crypto_util.h" - -namespace widevine { - -namespace { - -struct KeyData { - video_widevine::License_KeyContainer_SecurityLevel level; - std::vector key_id; - std::vector iv; - std::vector content_key; -}; - -// clang-format off -KeyData SoftwareCryptoKey() { - return { - video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_CRYPTO, - { // key_id: - '0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', '0', '1', '2', '3', '4', '5', - }, - { // iv: - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, - 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, - }, - { // content_key: - 0xeb, 0xdd, 0x62, 0xf1, 0x68, 0x14, 0xd2, 0x7b, - 0x68, 0xef, 0x12, 0x2a, 0xfc, 0xe4, 0xae, 0x3c, - } - }; -} - -KeyData SoftwareDecodeKey() { - return { - video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_DECODE, - { // key_id: - '9', '8', '7', '6', '5', '4', '3', '2', - '1', '0', '9', '8', '7', '6', '5', '4', - }, - { // iv: - 0x17, 0x18, 0x19, 0x20, 0x21, 0x22, 0x23, 0x24, - 0x25, 0x26, 0x27, 0x28, 0x29, 0x30, 0x31, 0x32, - }, - { // content_key: - 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, - 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, - } - }; -} - -KeyData HardwareCryptoKey() { - return { - video_widevine::License_KeyContainer_SecurityLevel_HW_SECURE_CRYPTO, - { // key_id: - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', - }, - { // iv: - 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x40, - 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, - }, - { // content_key: - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, - 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, - } - }; -} -// clang-format on - -// Returns |buffer| converted into a string. -std::string AsString(const uint8_t* buffer, size_t buffer_size) { - return std::string(reinterpret_cast(buffer), buffer_size); -} - -// Returns the encryption key based on |license_request| and |session_key|. -std::string GetEncryptionKey(const std::string& license_request, - const std::string& session_key) { - return widevine::crypto_util::DeriveKey( - session_key, widevine::crypto_util::kWrappingKeyLabel, license_request, - widevine::crypto_util::kWrappingKeySizeBits); -} - -std::string AesEncrypt(const std::string& key, - const uint8_t* iv, - size_t iv_size, - const uint8_t* plain_text, - size_t plain_text_size) { - widevine::AesCbcEncryptor encryptor; - encryptor.SetKey(reinterpret_cast(key.data()), key.size()); - std::vector cipher_text(plain_text_size); - CHECK(encryptor.Encrypt(iv, iv_size, plain_text, plain_text_size, - cipher_text.data())); - return AsString(cipher_text.data(), cipher_text.size()); -} - -video_widevine::License::KeyContainer CreateContentKeyContainer( - const KeyData& key_data, - const std::string& key_container_encryption_key) { - video_widevine::License::KeyContainer key; - key.set_id(key_data.key_id.data(), key_data.key_id.size()); - key.set_iv(key_data.iv.data(), key_data.iv.size()); - key.set_type(video_widevine::License_KeyContainer_KeyType_CONTENT); - key.set_level(key_data.level); - key.set_key(AesEncrypt(key_container_encryption_key, key_data.iv.data(), - key_data.iv.size(), key_data.content_key.data(), - key_data.content_key.size())); - return key; -} - -video_widevine::License::KeyContainer CreateSigningKeyContainer( - const std::string& key_container_encryption_key) { - const std::vector signing_iv = { - '0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', '0', '1', '2', '3', '4', '5', - }; - // 512 bits of key material for dual server/client signing key. - const std::vector signing_key = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, - }; - - video_widevine::License::KeyContainer key; - key.set_iv(signing_iv.data(), signing_iv.size()); - key.set_type(video_widevine::License_KeyContainer_KeyType_SIGNING); - key.set_key(AesEncrypt(key_container_encryption_key, signing_iv.data(), - signing_iv.size(), signing_key.data(), - signing_key.size())); - return key; -} - -} // namespace - -std::vector GetSoftwareCryptoKeyId() { - const auto key = SoftwareCryptoKey(); - return key.key_id; -} - -std::vector GetSoftwareDecodeKeyId() { - const auto key = SoftwareDecodeKey(); - return key.key_id; -} - -std::vector GetHardwareCryptoKeyId() { - const auto key = HardwareCryptoKey(); - return key.key_id; -} - -std::vector GetUnusedKeyId() { - return {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; -} - -// Returns a serialized license request with the following data: -// { -// "requestTime": , -// "clientId": { -// "clientCapabilities": { -// "videoResolutionConstraints": true, -// "clientToken": true, -// "sessionToken": false, -// "maxHdcpVersion": "HDCP_V1" -// }, -// "clientInfo": [{ -// "name": "architecture_name", -// "value": "x86-64" -// },{ -// "name": "company_name", -// "value": "Google" -// },{ -// "name": "model_name", -// "value": "ChromeCDM" -// },{ -// "name": "platform_name", -// "value": "Windows" -// },{ -// "name": "widevine_cdm_version", -// "value": "4.10.1686.29" -// }], -// "type": "DRM_DEVICE_CERTIFICATE", -// "token": { -// -// }" -// }", -// "contentId": { -// "webmKeyId": { -// "licenseType": "STREAMING", -// "requestId": "REQUEST_ID", -// "header": "01234567890123456" -// } -// }, -// "protocolVersion": "VERSION_2_1", -// "type": "NEW" -// } -std::string CreateLicenseRequest() { - video_widevine::LicenseRequest license_request; - - // Request time is now. - license_request.set_request_time(std::time(nullptr)); - - auto* client_id = license_request.mutable_client_id(); - auto* capabilities = client_id->mutable_client_capabilities(); - capabilities->set_video_resolution_constraints(true); - capabilities->set_client_token(true); - capabilities->set_session_token(false); - capabilities->set_max_hdcp_version( - video_widevine::ClientIdentification::ClientCapabilities::HDCP_V1); - - auto* client_info = client_id->add_client_info(); - client_info->set_name("architecture_name"); - client_info->set_value("x86-64"); - client_info = client_id->add_client_info(); - client_info->set_name("company_name"); - client_info->set_value("Google"); - client_info = client_id->add_client_info(); - client_info->set_name("model_name"); - client_info->set_value("ChromeCDM"); - client_info = client_id->add_client_info(); - client_info->set_name("platform_name"); - client_info->set_value("Windows"); - client_info = client_id->add_client_info(); - client_info->set_name("widevine_cdm_version"); - client_info->set_value("4.10.1686.29"); - - client_id->set_type( - video_widevine::ClientIdentification_TokenType_DRM_DEVICE_CERTIFICATE); - client_id->set_token(wvcdm::kRsaDrmCertificate, - wvcdm::kRsaDrmCertificateSize); - - auto* content_id = license_request.mutable_content_id(); - auto* webm_key_id = content_id->mutable_webm_key_id(); - webm_key_id->set_license_type(video_widevine::STREAMING); - webm_key_id->set_request_id("REQUEST_ID"); - webm_key_id->set_header("01234567890123456"); - - license_request.set_protocol_version(video_widevine::VERSION_2_1); - license_request.set_type(video_widevine::LicenseRequest::NEW); - return license_request.SerializeAsString(); -} - -// Returns a serialized license using values from |serialized_license_request| -// as well as |session_key|. Some of the values match what is in the license -// request, but as they are not checked it really doesn't matter. The license -// will contain all 3 keys |kKeyId1|, |kKeyId2|, and |kKeyId3| with different -// security levels. The license is of the form: -// { -// "id": { -// "requestId": "REQUEST_ID", -// "sessionId": "SESSION_ID", -// "type": "STREAMING", -// "version": 0 -// }, -// "policy": { -// "canPlay": true, -// "canPersist": false, -// "canRenew": true, -// "licenseDurationSeconds": "600", -// "renewalDelaySeconds": "30", -// "renewalRetryIntervalSeconds": "10", -// "renewWithUsage": false -// }, -// "key": [{ -// "iv": <|kInitializationVector|>, -// "type": "SIGNING", -// "key": -// },{ -// "id": <|kKeyId1|>, -// "iv": <|kInitializationVector|>, -// "type": "CONTENT", -// "level": "SW_SECURE_CRYPTO", -// "key": <|kContentKey1|> -// },{ -// "id": <|kKeyId2|>, -// "iv": <|kInitializationVector|>, -// "type": "CONTENT", -// "level": "SW_SECURE_DECODE", -// "key": <|kContentKey2|> -// },{ -// "id": <|kKeyId3|>, -// "iv": <|kInitializationVector|>, -// "type": "CONTENT", -// "level": "HW_SECURE_CRYPTO", -// "key": <|kContentKey3|> -// }], -// "licenseStartTime": , -// "remoteAttestationVerified": false, -// "platformVerificationStatus": "PLATFORM_UNVERIFIED" -// } -std::string CreateLicense(const std::string& serialized_license_request, - const std::string& session_key) { - DCHECK_EQ(session_key.size(), 16u); - - // Extract values needed for the license. - video_widevine::LicenseRequest license_request; - CHECK(license_request.ParseFromString(serialized_license_request)); - const auto request_time = license_request.request_time(); - - const std::string key_container_encryption_key = - GetEncryptionKey(serialized_license_request, session_key); - - // Now create the license. - video_widevine::License license; - auto* license_id = license.mutable_id(); - license_id->set_request_id("REQUEST_ID"); - license_id->set_session_id("SESSION_ID"); - license_id->set_type(video_widevine::STREAMING); - license_id->set_version(0); - - auto* policy = license.mutable_policy(); - policy->set_can_play(true); - policy->set_can_persist(false); - policy->set_can_renew(true); - policy->set_license_duration_seconds(600); - policy->set_renewal_delay_seconds(30); - policy->set_renewal_retry_interval_seconds(10); - policy->set_renew_with_usage(false); - - license.add_key()->CopyFrom( - CreateSigningKeyContainer(key_container_encryption_key)); - license.add_key()->CopyFrom(CreateContentKeyContainer( - SoftwareCryptoKey(), key_container_encryption_key)); - license.add_key()->CopyFrom(CreateContentKeyContainer( - SoftwareDecodeKey(), key_container_encryption_key)); - license.add_key()->CopyFrom(CreateContentKeyContainer( - HardwareCryptoKey(), key_container_encryption_key)); - - license.set_license_start_time(request_time); - license.set_remote_attestation_verified(false); - license.set_platform_verification_status( - video_widevine::PlatformVerificationStatus::PLATFORM_UNVERIFIED); - - return license.SerializeAsString(); -} - -} // namespace widevine diff --git a/api/license_test_helper.h b/api/license_test_helper.h deleted file mode 100644 index 9e9f1f7..0000000 --- a/api/license_test_helper.h +++ /dev/null @@ -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 -#include -#include -#include - -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 GetSoftwareCryptoKeyId(); -std::vector GetSoftwareDecodeKeyId(); -std::vector GetHardwareCryptoKeyId(); - -// Returns a key ID which is not included in the license. -std::vector GetUnusedKeyId(); - -} // namespace widevine - -#endif // WHITEBOX_API_LICENSE_TEST_HELPER_H_ diff --git a/api/license_whitebox.h b/api/license_whitebox.h index 65ea6e3..cdfa3c0 100644 --- a/api/license_whitebox.h +++ b/api/license_whitebox.h @@ -6,6 +6,7 @@ #include #include +#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 } diff --git a/api/license_whitebox_chromeos_test.cc b/api/license_whitebox_chromeos_test.cc index 8a93653..58694ab 100644 --- a/api/license_whitebox_chromeos_test.cc +++ b/api/license_whitebox_chromeos_test.cc @@ -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 diff --git a/api/license_whitebox_decrypt_benchmark.cc b/api/license_whitebox_decrypt_benchmark.cc index b93fa47..9140f17 100644 --- a/api/license_whitebox_decrypt_benchmark.cc +++ b/api/license_whitebox_decrypt_benchmark.cc @@ -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_); } diff --git a/api/license_whitebox_decrypt_test.cc b/api/license_whitebox_decrypt_test.cc index b8df56a..cad808f 100644 --- a/api/license_whitebox_decrypt_test.cc +++ b/api/license_whitebox_decrypt_test.cc @@ -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 diff --git a/api/license_whitebox_get_secret_string_test.cc b/api/license_whitebox_get_secret_string_test.cc index 226d59b..5142ab5 100644 --- a/api/license_whitebox_get_secret_string_test.cc +++ b/api/license_whitebox_get_secret_string_test.cc @@ -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 diff --git a/api/license_whitebox_masked_decrypt_test.cc b/api/license_whitebox_masked_decrypt_test.cc index 2042055..39edf6c 100644 --- a/api/license_whitebox_masked_decrypt_test.cc +++ b/api/license_whitebox_masked_decrypt_test.cc @@ -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 diff --git a/api/license_whitebox_process_license_response_benchmark.cc b/api/license_whitebox_process_license_response_benchmark.cc index ab51f84..df88c1d 100644 --- a/api/license_whitebox_process_license_response_benchmark.cc +++ b/api/license_whitebox_process_license_response_benchmark.cc @@ -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()); diff --git a/api/license_whitebox_process_license_response_core_message_test.cc b/api/license_whitebox_process_license_response_core_message_test.cc new file mode 100644 index 0000000..f33d89f --- /dev/null +++ b/api/license_whitebox_process_license_response_core_message_test.cc @@ -0,0 +1,153 @@ +// Copyright 2020 Google LLC. All Rights Reserved. + +#include "api/license_whitebox.h" + +#include + +#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& 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 diff --git a/api/license_whitebox_process_license_response_test.cc b/api/license_whitebox_process_license_response_test.cc index 23ddbae..2fe34cd 100644 --- a/api/license_whitebox_process_license_response_test.cc +++ b/api/license_whitebox_process_license_response_test.cc @@ -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 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 diff --git a/api/license_whitebox_sign_benchmark.cc b/api/license_whitebox_sign_benchmark.cc index 4ec076b..d9697b2 100644 --- a/api/license_whitebox_sign_benchmark.cc +++ b/api/license_whitebox_sign_benchmark.cc @@ -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_); } diff --git a/api/license_whitebox_sign_renewal_request_test.cc b/api/license_whitebox_sign_renewal_request_test.cc index f660b0e..d075879 100644 --- a/api/license_whitebox_sign_renewal_request_test.cc +++ b/api/license_whitebox_sign_renewal_request_test.cc @@ -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(), diff --git a/api/license_whitebox_test.cc b/api/license_whitebox_test.cc deleted file mode 100644 index 5aaf344..0000000 --- a/api/license_whitebox_test.cc +++ /dev/null @@ -1,257 +0,0 @@ -// Copyright 2020 Google LLC. All Rights Reserved. - -#include "api/license_whitebox.h" - -#include -#include -#include -#include - -#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(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 init_data = GetLicenseInitData(); - ASSERT_EQ(WB_License_Create(init_data.data(), init_data.size(), &whitebox_), - WB_RESULT_OK); - - std::vector 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 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(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 message{1, 2, 3}; - std::vector signature{4, 5, 6, 7}; - std::vector session_key{1, 2, 3, 4, 5, 6, 7, 8}; - std::vector 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 message{1, 2, 3}; - std::vector signature{4, 5, 6, 7}; - std::vector session_key{1, 2, 3, 4, 5, 6, 7, 8}; - std::vector 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 message{/* empty */}; - std::vector signature{4, 5, 6, 7}; - std::vector session_key{1, 2, 3, 4, 5, 6, 7, 8}; - std::vector 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 message{1, 2, 3}; - std::vector signature{4, 5, 6, 7}; - std::vector session_key{1, 2, 3, 4, 5, 6, 7, 8}; - std::vector 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 message{1, 2, 3}; - std::vector signature{/* empty */}; - std::vector session_key{1, 2, 3, 4, 5, 6, 7, 8}; - std::vector 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 message{1, 2, 3}; - std::vector signature{4, 5, 6, 7}; - std::vector session_key{1, 2, 3, 4, 5, 6, 7, 8}; - std::vector 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 message{1, 2, 3}; - std::vector signature{4, 5, 6, 7}; - std::vector session_key{/* empty */}; - std::vector 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 message{1, 2, 3}; - std::vector signature{4, 5, 6, 7}; - std::vector session_key{1, 2, 3, 4, 5, 6, 7, 8}; - std::vector 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 message{1, 2, 3}; - std::vector signature{4, 5, 6, 7}; - std::vector session_key{1, 2, 3, 4, 5, 6, 7, 8}; - std::vector 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 diff --git a/api/license_whitebox_verify_benchmark.cc b/api/license_whitebox_verify_benchmark.cc index 7340888..9c01013 100644 --- a/api/license_whitebox_verify_benchmark.cc +++ b/api/license_whitebox_verify_benchmark.cc @@ -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_); } diff --git a/api/license_whitebox_verify_renewal_response_test.cc b/api/license_whitebox_verify_renewal_response_test.cc index 89df2af..59eaab6 100644 --- a/api/license_whitebox_verify_renewal_response_test.cc +++ b/api/license_whitebox_verify_renewal_response_test.cc @@ -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 Sign(const std::vector& 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(), diff --git a/api/test_license_builder.cc b/api/test_license_builder.cc index 3fc533f..9eb5472 100644 --- a/api/test_license_builder.cc +++ b/api/test_license_builder.cc @@ -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& 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 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(const_cast(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( + const_cast(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& 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 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(serialized_request_.begin(), - serialized_request_.end()); - license->message = - std::vector(message_str.begin(), message_str.end()); - license->signature = - std::vector(signature_str.begin(), signature_str.end()); - license->session_key = - std::vector(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 diff --git a/api/test_license_builder.h b/api/test_license_builder.h index 939817f..f438faa 100644 --- a/api/test_license_builder.h +++ b/api/test_license_builder.h @@ -14,6 +14,8 @@ namespace widevine { struct License { std::vector request; + + std::vector core_message; std::vector message; std::vector 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 diff --git a/chromium_deps/base/base64.cc b/chromium_deps/base/base64.cc deleted file mode 100644 index c173eda..0000000 --- a/chromium_deps/base/base64.cc +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2020 Google LLC. All Rights Reserved. - -#include "base/base64.h" - -#include -#include - -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 diff --git a/chromium_deps/base/base64.h b/chromium_deps/base/base64.h deleted file mode 100644 index ee14e83..0000000 --- a/chromium_deps/base/base64.h +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2020 Google LLC. All Rights Reserved. - -#ifndef BASE_BASE64_H_ -#define BASE_BASE64_H_ - -#include - -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_ diff --git a/chromium_deps/base/strings/string_number_conversions.cc b/chromium_deps/base/strings/string_number_conversions.cc deleted file mode 100644 index aabee07..0000000 --- a/chromium_deps/base/strings/string_number_conversions.cc +++ /dev/null @@ -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(bytes), size)); -} - -} // namespace base diff --git a/chromium_deps/base/strings/string_number_conversions.h b/chromium_deps/base/strings/string_number_conversions.h deleted file mode 100644 index e6efe8b..0000000 --- a/chromium_deps/base/strings/string_number_conversions.h +++ /dev/null @@ -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 -#include -#include - -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_ diff --git a/chromium_deps/cdm/protos/defs/BUILD b/chromium_deps/cdm/protos/defs/BUILD index f21f02c..1519f59 100644 --- a/chromium_deps/cdm/protos/defs/BUILD +++ b/chromium_deps/cdm/protos/defs/BUILD @@ -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"], diff --git a/chromium_deps/cdm/protos/defs/certificate_provisioning.proto b/chromium_deps/cdm/protos/defs/certificate_provisioning.proto new file mode 100644 index 0000000..7b5f113 --- /dev/null +++ b/chromium_deps/cdm/protos/defs/certificate_provisioning.proto @@ -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; +} diff --git a/chromium_deps/cdm/protos/defs/license_protocol.proto b/chromium_deps/cdm/protos/defs/license_protocol.proto index b4c4555..5975bb3 100644 --- a/chromium_deps/cdm/protos/defs/license_protocol.proto +++ b/chromium_deps/cdm/protos/defs/license_protocol.proto @@ -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) diff --git a/external/BUILD b/external/BUILD new file mode 100644 index 0000000..69df2d4 --- /dev/null +++ b/external/BUILD @@ -0,0 +1,2 @@ +# An empty BUILD file. This is needed for the "*.BUILD" files to be loaded by +# bazel. diff --git a/external/odk.BUILD b/external/odk.BUILD new file mode 100644 index 0000000..3d8ab53 --- /dev/null +++ b/external/odk.BUILD @@ -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", + ], +) diff --git a/impl/reference/BUILD b/impl/reference/BUILD index 837636a..d8a3a7b 100644 --- a/impl/reference/BUILD +++ b/impl/reference/BUILD @@ -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", ], ) diff --git a/impl/reference/license_whitebox_impl.cc b/impl/reference/license_whitebox_impl.cc index cf426f1..225953e 100644 --- a/impl/reference/license_whitebox_impl.cc +++ b/impl/reference/license_whitebox_impl.cc @@ -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 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* decrypted) { + DCHECK_GE(decrypted->size(), encrypted.size()); + return decryptor.Decrypt(reinterpret_cast(iv.data()), + iv.size(), + reinterpret_cast(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& 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(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( + const_cast(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(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 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 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 unwrapped_key(wrapped_key.size()); - - if (!decryptor.Decrypt(reinterpret_cast(key.iv().data()), - key.iv().size(), - reinterpret_cast(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 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 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; } } diff --git a/impl/reference/string_view_util.cc b/impl/reference/string_view_util.cc deleted file mode 100644 index 7866a57..0000000 --- a/impl/reference/string_view_util.cc +++ /dev/null @@ -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(buffer), buffer_size); -} - -} // namespace widevine diff --git a/impl/reference/string_view_util.h b/impl/reference/string_view_util.h deleted file mode 100644 index 008221f..0000000 --- a/impl/reference/string_view_util.h +++ /dev/null @@ -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 -#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_ diff --git a/odk_deps/BUILD b/odk_deps/BUILD new file mode 100644 index 0000000..7bfd643 --- /dev/null +++ b/odk_deps/BUILD @@ -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", + ], +) diff --git a/odk_deps/license_protocol.pb.h b/odk_deps/license_protocol.pb.h new file mode 100644 index 0000000..0e87af3 --- /dev/null +++ b/odk_deps/license_protocol.pb.h @@ -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_