OEMCrypto and OPK 18.8

This commit is contained in:
Vicky Min
2024-11-26 20:08:25 +00:00
parent 92b538334c
commit f79f9bf998
33 changed files with 2047 additions and 1471 deletions

View File

@@ -2,6 +2,16 @@
[TOC] [TOC]
## [Version 18.8][v18.8]
This is a minor release with minor test improvements.
### Tests
- Update some CAST tests to enforce format of the message signed by
OEMCrypto_GenerateRSASignature()
- Skip usage table tests on devices that don't support usage tables
## [Version 18.7][v18.7] ## [Version 18.7][v18.7]
This release adds new tests that provide stricter enforcement of the existing This release adds new tests that provide stricter enforcement of the existing
@@ -588,3 +598,4 @@ Public release for OEMCrypto API and ODK library version 16.4.
[v18.5]: https://widevine-partner.googlesource.com/oemcrypto/+/refs/tags/v18.5 [v18.5]: https://widevine-partner.googlesource.com/oemcrypto/+/refs/tags/v18.5
[v18.6]: https://widevine-partner.googlesource.com/oemcrypto/+/refs/tags/v18.6 [v18.6]: https://widevine-partner.googlesource.com/oemcrypto/+/refs/tags/v18.6
[v18.7]: https://widevine-partner.googlesource.com/oemcrypto/+/refs/tags/v18.7 [v18.7]: https://widevine-partner.googlesource.com/oemcrypto/+/refs/tags/v18.7
[v18.8]: https://widevine-partner.googlesource.com/oemcrypto/+/refs/tags/v18.8

View File

@@ -3,7 +3,7 @@
// License Agreement. // License Agreement.
/** /**
* @mainpage OEMCrypto API v18.7 * @mainpage OEMCrypto API v18.8
* *
* OEMCrypto is the low level library implemented by the OEM to provide key and * OEMCrypto is the low level library implemented by the OEM to provide key and
* content protection, usually in a separate secure memory or process space. The * content protection, usually in a separate secure memory or process space. The

View File

@@ -26,9 +26,9 @@ struct CoreMessageFeatures {
// This is the published version of the ODK Core Message library. The default // This is the published version of the ODK Core Message library. The default
// behavior is for the server to restrict messages to at most this version // behavior is for the server to restrict messages to at most this version
// number. The default is 18.7. // number. The default is 18.8.
uint32_t maximum_major_version = 18; uint32_t maximum_major_version = 18;
uint32_t maximum_minor_version = 7; uint32_t maximum_minor_version = 8;
bool operator==(const CoreMessageFeatures &other) const; bool operator==(const CoreMessageFeatures &other) const;
bool operator!=(const CoreMessageFeatures &other) const { bool operator!=(const CoreMessageFeatures &other) const {

View File

@@ -16,10 +16,10 @@ extern "C" {
/* The version of this library. */ /* The version of this library. */
#define ODK_MAJOR_VERSION 18 #define ODK_MAJOR_VERSION 18
#define ODK_MINOR_VERSION 7 #define ODK_MINOR_VERSION 8
/* ODK Version string. Date changed automatically on each release. */ /* ODK Version string. Date changed automatically on each release. */
#define ODK_RELEASE_DATE "ODK v18.7 2024-09-04" #define ODK_RELEASE_DATE "ODK v18.8 2024-11-04"
/* The lowest version number for an ODK message. */ /* The lowest version number for an ODK message. */
#define ODK_FIRST_VERSION 16 #define ODK_FIRST_VERSION 16

View File

@@ -30,7 +30,7 @@ CoreMessageFeatures CoreMessageFeatures::DefaultFeatures(
features.maximum_minor_version = 2; // 17.2 features.maximum_minor_version = 2; // 17.2
break; break;
case 18: case 18:
features.maximum_minor_version = 7; // 18.7 features.maximum_minor_version = 8; // 18.8
break; break;
default: default:
features.maximum_minor_version = 0; features.maximum_minor_version = 0;

View File

@@ -274,7 +274,7 @@ OEMCryptoResult ODK_InitializeSessionValues(ODK_TimerLimits* timer_limits,
nonce_values->api_minor_version = 2; nonce_values->api_minor_version = 2;
break; break;
case 18: case 18:
nonce_values->api_minor_version = 7; nonce_values->api_minor_version = 8;
break; break;
default: default:
nonce_values->api_minor_version = 0; nonce_values->api_minor_version = 0;

View File

@@ -1216,7 +1216,7 @@ std::vector<VersionParameters> TestCases() {
// number. // number.
{16, ODK_MAJOR_VERSION, ODK_MINOR_VERSION, 16, 5}, {16, ODK_MAJOR_VERSION, ODK_MINOR_VERSION, 16, 5},
{17, ODK_MAJOR_VERSION, ODK_MINOR_VERSION, 17, 2}, {17, ODK_MAJOR_VERSION, ODK_MINOR_VERSION, 17, 2},
{18, ODK_MAJOR_VERSION, ODK_MINOR_VERSION, 18, 7}, {18, ODK_MAJOR_VERSION, ODK_MINOR_VERSION, 18, 8},
// Here are some known good versions. Make extra sure they work. // Here are some known good versions. Make extra sure they work.
{ODK_MAJOR_VERSION, 16, 3, 16, 3}, {ODK_MAJOR_VERSION, 16, 3, 16, 3},
{ODK_MAJOR_VERSION, 16, 4, 16, 4}, {ODK_MAJOR_VERSION, 16, 4, 16, 4},
@@ -1230,6 +1230,7 @@ std::vector<VersionParameters> TestCases() {
{ODK_MAJOR_VERSION, 18, 5, 18, 5}, {ODK_MAJOR_VERSION, 18, 5, 18, 5},
{ODK_MAJOR_VERSION, 18, 6, 18, 6}, {ODK_MAJOR_VERSION, 18, 6, 18, 6},
{ODK_MAJOR_VERSION, 18, 7, 18, 7}, {ODK_MAJOR_VERSION, 18, 7, 18, 7},
{ODK_MAJOR_VERSION, 18, 8, 18, 8},
{0, 16, 3, 16, 3}, {0, 16, 3, 16, 3},
{0, 16, 4, 16, 4}, {0, 16, 4, 16, 4},
{0, 16, 5, 16, 5}, {0, 16, 5, 16, 5},
@@ -1239,6 +1240,7 @@ std::vector<VersionParameters> TestCases() {
{0, 18, 5, 18, 5}, {0, 18, 5, 18, 5},
{0, 18, 6, 18, 6}, {0, 18, 6, 18, 6},
{0, 18, 7, 18, 7}, {0, 18, 7, 18, 7},
{0, 18, 8, 18, 8},
}; };
return test_cases; return test_cases;
} }

View File

@@ -216,6 +216,7 @@ oemcrypto_unittests_sources += \
$(oemcrypto_unittests_dir)/oemcrypto_generic_crypto_test.cpp \ $(oemcrypto_unittests_dir)/oemcrypto_generic_crypto_test.cpp \
$(oemcrypto_unittests_dir)/oemcrypto_license_test.cpp \ $(oemcrypto_unittests_dir)/oemcrypto_license_test.cpp \
$(oemcrypto_unittests_dir)/oemcrypto_provisioning_test.cpp \ $(oemcrypto_unittests_dir)/oemcrypto_provisioning_test.cpp \
$(oemcrypto_unittests_dir)/oemcrypto_security_test.cpp \
$(oemcrypto_unittests_dir)/oemcrypto_usage_table_test.cpp \ $(oemcrypto_unittests_dir)/oemcrypto_usage_table_test.cpp \
$(oemcrypto_unittests_dir)/oemcrypto_test.cpp \ $(oemcrypto_unittests_dir)/oemcrypto_test.cpp \
$(oemcrypto_dir)/util/src/cmac.cpp \ $(oemcrypto_dir)/util/src/cmac.cpp \

View File

@@ -350,10 +350,24 @@ static OEMCryptoResult RewrapDeviceDRMKeyOEMCert(
LOGE("Failed to AES CBC decrypt DRM private key with result: %u", result); LOGE("Failed to AES CBC decrypt DRM private key with result: %u", result);
goto cleanup; goto cleanup;
} }
/* Adjust for RSA keys with specified schemes */
uint32_t allowed_schemes = 0;
size_t key_offset = 0;
if (drm_key_type == DRM_RSA_PRIVATE_KEY) {
if (enc_drm_key_length >= 8 &&
crypto_memcmp(clear_drm_key, "SIGN", 4) == 0) {
memcpy(&allowed_schemes, clear_drm_key + 4, 4);
allowed_schemes = oemcrypto_be32toh(allowed_schemes);
key_offset = 8;
} else {
allowed_schemes = kSign_RSASSA_PSS;
}
}
/* Create asymmetric key handle for DRM private key in order to get the /* Create asymmetric key handle for DRM private key in order to get the
* signature size. */ * signature size. */
WTPI_AsymmetricKey_Handle private_key_handle; WTPI_AsymmetricKey_Handle private_key_handle;
result = WTPI_CreateAsymmetricKeyHandle(clear_drm_key, enc_drm_key_length, result = WTPI_CreateAsymmetricKeyHandle(clear_drm_key + key_offset,
enc_drm_key_length - key_offset,
drm_key_type, &private_key_handle); drm_key_type, &private_key_handle);
if (result != OEMCrypto_SUCCESS) { if (result != OEMCrypto_SUCCESS) {
LOGE( LOGE(
@@ -380,7 +394,7 @@ static OEMCryptoResult RewrapDeviceDRMKeyOEMCert(
/* Check that it's a valid DRM key. */ /* Check that it's a valid DRM key. */
result = result =
OPKI_LoadDRMKey(session_context, drm_key_type, wrapped_drm_key, OPKI_LoadDRMKey(session_context, drm_key_type, wrapped_drm_key,
wrapped_drm_key_length, signature_size, kSign_RSASSA_PSS); wrapped_drm_key_length, signature_size, allowed_schemes);
if (result != OEMCrypto_SUCCESS) { if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to load DRM key with result: %u", result); LOGE("Failed to load DRM key with result: %u", result);
} }

View File

@@ -34,7 +34,7 @@
// version bumps to v17.1, the first released OPK implementation would be // version bumps to v17.1, the first released OPK implementation would be
// v17.1.0 // v17.1.0
#define API_MAJOR_VERSION 18 #define API_MAJOR_VERSION 18
#define API_MINOR_VERSION 7 #define API_MINOR_VERSION 8
#define OPK_PATCH_VERSION 0 #define OPK_PATCH_VERSION 0
#endif /* OEMCRYPTO_TA_OEMCRYPTO_API_MACROS_H_ */ #endif /* OEMCRYPTO_TA_OEMCRYPTO_API_MACROS_H_ */

View File

@@ -13,6 +13,7 @@
# production use. # production use.
'wtpi_stub_dir': '<(oemcrypto_ta_dir)/wtpi_useless', 'wtpi_stub_dir': '<(oemcrypto_ta_dir)/wtpi_useless',
'use_provisioning_40%': 0, 'use_provisioning_40%': 0,
'use_random_device_key%': 0,
}, },
'target_defaults': { 'target_defaults': {
'toolsets' : [ 'target' ], 'toolsets' : [ 'target' ],
@@ -31,7 +32,6 @@
'test-only/wtpi_persistent_storage.c', 'test-only/wtpi_persistent_storage.c',
'test-only/file_store_interface.c', 'test-only/file_store_interface.c',
'test-only/wtpi_cas.c', 'test-only/wtpi_cas.c',
'test-only/wtpi_device_key_access.c',
'<(wtpi_stub_dir)/wtpi_root_of_trust_layer2.c', '<(wtpi_stub_dir)/wtpi_root_of_trust_layer2.c',
'<(wtpi_stub_dir)/wtpi_secure_buffer_access.c', '<(wtpi_stub_dir)/wtpi_secure_buffer_access.c',
'<(wtpi_stub_dir)/wtpi_fused.c', '<(wtpi_stub_dir)/wtpi_fused.c',
@@ -49,6 +49,11 @@
'wtpi_device_renewal_layer2_test.c', 'wtpi_device_renewal_layer2_test.c',
], ],
}], }],
['use_random_device_key', {
'sources': ['<(wtpi_stub_dir)/wtpi_device_key_access.c',],
}, {
'sources': ['test-only/wtpi_device_key_access.c',],
}],
], ],
'dependencies': [ 'dependencies': [
'<(oemcrypto_ta_dir)/oemcrypto_ta.gyp:oemcrypto_ta_linux_tee', '<(oemcrypto_ta_dir)/oemcrypto_ta.gyp:oemcrypto_ta_linux_tee',

View File

@@ -40,7 +40,7 @@ repo sync -c -j32
# Download an Android NDK prebuilt to compile liboemcrypto.so # Download an Android NDK prebuilt to compile liboemcrypto.so
cd $TRUSTY_DIR/prebuilts cd $TRUSTY_DIR/prebuilts
mkdir ndk && cd ndk mkdir ndk && cd ndk
git clone https://android.googlesource.com/toolchain/prebuilts/ndk/r25 git clone https://android.googlesource.com/toolchain/prebuilts/ndk/r26
# Add the OEMCrypto TA path as a user module. The OEMCRYPTO_TA_DIR must expand # Add the OEMCrypto TA path as a user module. The OEMCRYPTO_TA_DIR must expand
# to a path that is relative to the Trusty directory # to a path that is relative to the Trusty directory

View File

@@ -8,7 +8,7 @@ SCRIPT_DIR=$(dirname "${BASH_SOURCE[0]}")
# Build liboemcrypto.so # Build liboemcrypto.so
if [ ! -v ANDROID_NDK_ROOT ]; then if [ ! -v ANDROID_NDK_ROOT ]; then
ANDROID_NDK_ROOT=$(realpath "$SCRIPT_DIR/../../../../../prebuilts/ndk/r25") ANDROID_NDK_ROOT=$(realpath "$SCRIPT_DIR/../../../../../prebuilts/ndk/r26")
fi fi
"$ANDROID_NDK_ROOT/ndk-build" \ "$ANDROID_NDK_ROOT/ndk-build" \

View File

@@ -28,7 +28,7 @@ LIBOEMCRYPTO_SO := $(LIBOEMCRYPTO_OUT_DIR)/lib/arm64-v8a/liboemcrypto.so
$(LIBOEMCRYPTO_SO): LOCAL_DIR := $(GET_LOCAL_DIR) $(LIBOEMCRYPTO_SO): LOCAL_DIR := $(GET_LOCAL_DIR)
$(LIBOEMCRYPTO_SO): LIBOEMCRYPTO_BUILD_DIR := $(LIBOEMCRYPTO_BUILD_DIR) $(LIBOEMCRYPTO_SO): LIBOEMCRYPTO_BUILD_DIR := $(LIBOEMCRYPTO_BUILD_DIR)
$(LIBOEMCRYPTO_SO): LIBOEMCRYPTO_OUT_DIR := $(LIBOEMCRYPTO_OUT_DIR) $(LIBOEMCRYPTO_SO): LIBOEMCRYPTO_OUT_DIR := $(LIBOEMCRYPTO_OUT_DIR)
$(LIBOEMCRYPTO_SO): NDK_ROOT := $(TRUSTY_TOP)/prebuilts/ndk/r25 $(LIBOEMCRYPTO_SO): NDK_ROOT := $(TRUSTY_TOP)/prebuilts/ndk/r26
$(LIBOEMCRYPTO_SO): $(LIBOEMCRYPTO_SO):
$(NOECHO)$(NDK_ROOT)/ndk-build \ $(NOECHO)$(NDK_ROOT)/ndk-build \
-C $(LOCAL_DIR) \ -C $(LOCAL_DIR) \

View File

@@ -185,12 +185,18 @@ static bool is_bound(struct widevine_ctx* ctx) {
static void close_shared_memory(struct widevine_ctx* ctx) { static void close_shared_memory(struct widevine_ctx* ctx) {
TEE_SharedMemory_Bind(NULL, 0); TEE_SharedMemory_Bind(NULL, 0);
if (ctx->shared_buffer_addr != NULL) { if (ctx->shared_buffer_addr != NULL) {
munmap(ctx->shared_buffer_addr, ctx->shared_buffer_size); int rc = munmap(ctx->shared_buffer_addr, ctx->shared_buffer_size);
if (rc != NO_ERROR) {
TLOGW("munmap() failed: %d\n", rc);
}
ctx->shared_buffer_addr = NULL; ctx->shared_buffer_addr = NULL;
ctx->shared_buffer_size = 0; ctx->shared_buffer_size = 0;
} }
if (ctx->shared_message_addr != NULL) { if (ctx->shared_message_addr != NULL) {
munmap(ctx->shared_message_addr, ctx->shared_message_size); int rc = munmap(ctx->shared_message_addr, ctx->shared_message_size);
if (rc != NO_ERROR) {
TLOGW("munmap() failed: %d\n", rc);
}
ctx->shared_message_addr = NULL; ctx->shared_message_addr = NULL;
ctx->shared_message_size = 0; ctx->shared_message_size = 0;
} }
@@ -310,7 +316,10 @@ static int dispatch_bind_message(handle_t chan,
PROT_READ | PROT_WRITE, 0, handles[1], 0); PROT_READ | PROT_WRITE, 0, handles[1], 0);
if (shared_buffer_addr == MAP_FAILED) { if (shared_buffer_addr == MAP_FAILED) {
TLOGE("Could not map shared buffer.\n"); TLOGE("Could not map shared buffer.\n");
munmap(shared_message_addr, shared_message_size); int rc = munmap(shared_message_addr, shared_message_size);
if (rc != NO_ERROR) {
TLOGW("munmap() failed: %d\n", rc);
}
return -1; return -1;
} }

View File

@@ -22,6 +22,7 @@ LOCAL_SRC_FILES:= \
oemcrypto_generic_crypto_test.cpp \ oemcrypto_generic_crypto_test.cpp \
oemcrypto_license_test.cpp \ oemcrypto_license_test.cpp \
oemcrypto_provisioning_test.cpp \ oemcrypto_provisioning_test.cpp \
oemcrypto_security_test.cpp \
oemcrypto_usage_table_test.cpp \ oemcrypto_usage_table_test.cpp \
oemcrypto_test.cpp \ oemcrypto_test.cpp \
oemcrypto_test_android.cpp \ oemcrypto_test_android.cpp \

View File

@@ -0,0 +1,177 @@
// Copyright 2024 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine
// License Agreement.
// This tool extracts BCC by calling OEMCrypto APIs and generates a CSR file in
// JSON format, which can be handled by CE CDM wv_upload_tool.py.
#include <fstream>
#include <iostream>
#include <map>
#include <regex>
#include <string>
#include <vector>
#include "OEMCryptoCENC.h"
#include "string_conversions.h"
namespace {
// Make and Model for system ID resolution.
const std::string kDeviceMake = "widevine_test";
const std::string kDeviceModel = "prov4";
// Informative fields.
const std::string kDeviceArchitecture = "x86_64";
const std::string kDeviceName = "prov40 test client";
const std::string kDeviceProduct = "prov40 test";
const std::string kDeviceBuildInfo = "prov40 test build";
// == Utils ==
std::string StringMapToJson(
const std::map<std::string, std::string>& string_map) {
std::string json = "{";
for (const auto& value_pair : string_map) {
std::string escaped_value =
std::regex_replace(value_pair.second, std::regex("\""), "\\\"");
json.append("\"" + value_pair.first + "\": " + "\"" + escaped_value +
"\",");
}
json.resize(json.size() - 1); // Remove the last comma.
json.append("}");
return json;
}
// == Primary ==
bool GetBccAndBuildInfo(std::vector<uint8_t>* bcc,
std::string* oemcrypto_build_info) {
// Step 1: Initialize.
OEMCryptoResult result = OEMCrypto_Initialize();
if (result != OEMCrypto_SUCCESS) {
std::cerr << "Failed to initialize: result = " << result << std::endl;
return false;
}
// Step 2: Get BCC.
const OEMCrypto_ProvisioningMethod method = OEMCrypto_GetProvisioningMethod();
if (method != OEMCrypto_BootCertificateChain) {
std::cerr << "ProvisioningMethod is not BCC type: method = ";
std::cerr << method << std::endl;
OEMCrypto_Terminate();
return false;
}
bcc->resize(0);
size_t bcc_size = 0;
std::vector<uint8_t> additional_signature; // It should be empty.
size_t additional_signature_size = 0;
result = OEMCrypto_GetBootCertificateChain(bcc->data(), &bcc_size,
additional_signature.data(),
&additional_signature_size);
if (additional_signature_size != 0) {
std::cerr << "The additional_signature_size required by OEMCrypto is "
<< additional_signature_size
<< ", while it is expected to be zero." << std::endl;
OEMCrypto_Terminate();
return false;
}
if (result == OEMCrypto_ERROR_SHORT_BUFFER) {
bcc->resize(bcc_size);
additional_signature.resize(additional_signature_size);
result = OEMCrypto_GetBootCertificateChain(bcc->data(), &bcc_size,
additional_signature.data(),
&additional_signature_size);
}
if (result != OEMCrypto_SUCCESS) {
std::cerr << "Failed to get BCC: result = " << result << std::endl;
OEMCrypto_Terminate();
return false;
}
bcc->resize(bcc_size);
// Step 3: Get oemcrypto build info.
oemcrypto_build_info->resize(0);
std::vector<char> build_info_buffer;
size_t oemcrypto_build_info_size = 0;
result = OEMCrypto_BuildInformation(build_info_buffer.data(),
&oemcrypto_build_info_size);
if (result == OEMCrypto_ERROR_SHORT_BUFFER) {
build_info_buffer.resize(oemcrypto_build_info_size);
result = OEMCrypto_BuildInformation(build_info_buffer.data(),
&oemcrypto_build_info_size);
}
if (result != OEMCrypto_SUCCESS) {
std::cerr << "Failed to get build information: result = " << result
<< std::endl;
OEMCrypto_Terminate();
return false;
}
build_info_buffer.resize(oemcrypto_build_info_size);
oemcrypto_build_info->assign(build_info_buffer.begin(),
build_info_buffer.end());
oemcrypto_build_info->resize(oemcrypto_build_info_size);
// Step 4: Cleanup.
result = OEMCrypto_Terminate();
if (result != OEMCrypto_SUCCESS) {
std::cerr << "Failed to terminate: result = " << result << std::endl;
return false;
}
return true;
}
bool GenerateBccRecord(const std::vector<uint8_t>& bcc,
const std::string& oemcrypto_build_info,
std::string* bcc_record) {
std::map<std::string, std::string> record;
record["company"] = kDeviceMake;
record["model"] = kDeviceModel;
record["architecture"] = kDeviceArchitecture;
record["name"] = kDeviceName;
record["product"] = kDeviceProduct;
record["build_info"] = kDeviceBuildInfo;
record["bcc"] = wvutil::Base64Encode(bcc);
record["oemcrypto_build_info"] = oemcrypto_build_info;
const std::string record_json = StringMapToJson(record);
bcc_record->assign(record_json.begin(), record_json.end());
return true;
}
bool OutputBccRecord(const std::string& path, const std::string& record) {
std::cout << "Writing BCC record to file " << path << std::endl;
std::cout << record << std::endl;
std::ofstream out(path);
if (out) out << record;
if (out.bad()) {
std::cerr << "Failed to write BCC record to file " << path << std::endl;
return false;
}
return true;
}
} // namespace
int main(int argc, char** argv) {
if (argc != 2) {
std::cerr << "Usage: " << argv[0] << " <output JSON filename>" << std::endl;
return 1;
}
const std::string bcc_path = argv[1];
std::vector<uint8_t> bcc;
std::string oemcrypto_build_info;
if (!GetBccAndBuildInfo(&bcc, &oemcrypto_build_info)) {
std::cerr << "Failed to get BCC or OEMCrypto build info" << std::endl;
return 1;
}
std::string bcc_record;
if (!GenerateBccRecord(bcc, oemcrypto_build_info, &bcc_record)) {
std::cerr << "Failed to generate BCC record" << std::endl;
return 1;
}
if (!OutputBccRecord(bcc_path, bcc_record)) {
std::cerr << "Failed to output BCC record" << std::endl;
return 1;
}
return 0;
}

View File

@@ -17,6 +17,7 @@ void advance_dest_buffer(OEMCrypto_DestBufferDesc* dest_buffer, size_t bytes) {
switch (dest_buffer->type) { switch (dest_buffer->type) {
case OEMCrypto_BufferType_Clear: case OEMCrypto_BufferType_Clear:
dest_buffer->buffer.clear.clear_buffer += bytes; dest_buffer->buffer.clear.clear_buffer += bytes;
dest_buffer->buffer.clear.clear_buffer_length -= bytes;
break; break;
case OEMCrypto_BufferType_Secure: case OEMCrypto_BufferType_Secure:
@@ -98,11 +99,6 @@ OEMCryptoResult DecryptFallbackChain::DecryptSample(
const size_t length = const size_t length =
subsample.num_bytes_clear + subsample.num_bytes_encrypted; subsample.num_bytes_clear + subsample.num_bytes_encrypted;
fake_sample.buffers.input_data_length = length; fake_sample.buffers.input_data_length = length;
if (fake_sample.buffers.output_descriptor.type ==
OEMCrypto_BufferType_Clear) {
fake_sample.buffers.output_descriptor.buffer.clear.clear_buffer_length =
length;
}
fake_sample.subsamples = &subsample; fake_sample.subsamples = &subsample;
fake_sample.subsamples_length = 1; fake_sample.subsamples_length = 1;
@@ -148,11 +144,6 @@ OEMCryptoResult DecryptFallbackChain::DecryptSubsample(
if (subsample.num_bytes_clear > 0) { if (subsample.num_bytes_clear > 0) {
fake_sample.buffers.input_data_length = subsample.num_bytes_clear; fake_sample.buffers.input_data_length = subsample.num_bytes_clear;
if (fake_sample.buffers.output_descriptor.type ==
OEMCrypto_BufferType_Clear) {
fake_sample.buffers.output_descriptor.buffer.clear.clear_buffer_length =
subsample.num_bytes_clear;
}
fake_subsample.num_bytes_clear = subsample.num_bytes_clear; fake_subsample.num_bytes_clear = subsample.num_bytes_clear;
fake_subsample.num_bytes_encrypted = 0; fake_subsample.num_bytes_encrypted = 0;
fake_subsample.block_offset = 0; fake_subsample.block_offset = 0;
@@ -176,11 +167,6 @@ OEMCryptoResult DecryptFallbackChain::DecryptSubsample(
if (subsample.num_bytes_encrypted > 0) { if (subsample.num_bytes_encrypted > 0) {
fake_sample.buffers.input_data_length = subsample.num_bytes_encrypted; fake_sample.buffers.input_data_length = subsample.num_bytes_encrypted;
if (fake_sample.buffers.output_descriptor.type ==
OEMCrypto_BufferType_Clear) {
fake_sample.buffers.output_descriptor.buffer.clear.clear_buffer_length =
subsample.num_bytes_encrypted;
}
fake_subsample.num_bytes_clear = 0; fake_subsample.num_bytes_clear = 0;
fake_subsample.num_bytes_encrypted = subsample.num_bytes_encrypted; fake_subsample.num_bytes_encrypted = subsample.num_bytes_encrypted;
fake_subsample.block_offset = subsample.block_offset; fake_subsample.block_offset = subsample.block_offset;

View File

@@ -73,6 +73,76 @@ int32_t JsmnAncestorCount(const std::vector<jsmntok_t>& tokens,
} }
return count; return count;
} }
const char* BoolName(bool value) { return value ? "true" : "false"; }
const char* SecurityLevelName(OEMCrypto_Security_Level value) {
switch (value) {
case OEMCrypto_Level_Unknown:
return "OEMCrypto_Level_Unknown";
case OEMCrypto_Level1:
return "OEMCrypto_Level1";
case OEMCrypto_Level2:
return "OEMCrypto_Level2";
case OEMCrypto_Level3:
return "OEMCrypto_Level3";
}
// Not reachable unless the enum value is invalid
return "<INVALID VALUE>";
}
const char* HDCPCapabilityName(OEMCrypto_HDCP_Capability value) {
switch (value) {
case HDCP_NONE:
return "HDCP_NONE";
case HDCP_V1:
return "HDCP_V1";
case HDCP_V1_0:
return "HDCP_V1_0";
case HDCP_V1_1:
return "HDCP_V1_1";
case HDCP_V1_2:
return "HDCP_V1_2";
case HDCP_V1_3:
return "HDCP_V1_3";
case HDCP_V1_4:
return "HDCP_V1_4";
case HDCP_V2:
return "HDCP_V2";
case HDCP_V2_1:
return "HDCP_V2_1";
case HDCP_V2_2:
return "HDCP_V2_2";
case HDCP_V2_3:
return "HDCP_V2_3";
case HDCP_NO_DIGITAL_OUTPUT:
return "HDCP_NO_DIGITAL_OUTPUT";
}
// Not reachable unless the enum value is invalid
return "<INVALID VALUE>";
}
const char* WatermarkingSupportName(OEMCrypto_WatermarkingSupport value) {
switch (value) {
case OEMCrypto_WatermarkingError:
return "OEMCrypto_WatermarkingError";
case OEMCrypto_WatermarkingNotSupported:
return "OEMCrypto_WatermarkingNotSupported";
case OEMCrypto_WatermarkingConfigurable:
return "OEMCrypto_WatermarkingConfigurable";
case OEMCrypto_WatermarkingAlwaysOn:
return "OEMCrypto_WatermarkingAlwaysOn";
}
// Not reachable unless the enum value is invalid
return "<INVALID VALUE>";
}
const char* DTCP2CapabiityName(OEMCrypto_DTCP2_Capability value) {
switch (value) {
case OEMCrypto_NO_DTCP2:
return "OEMCrypto_NO_DTCP2";
case OEMCrypto_DTCP2_V1:
return "OEMCrypto_DTCP2_V1";
}
// Not reachable unless the enum value is invalid
return "<INVALID VALUE>";
}
} // namespace } // namespace
void OEMCryptoClientTest::SetUp() { void OEMCryptoClientTest::SetUp() {
@@ -148,6 +218,11 @@ OEMCryptoResult OEMCryptoClientTest::CopyBuffer(
dest_buffer_descriptor, subsample_flags); dest_buffer_descriptor, subsample_flags);
} }
void OEMCryptoClientTest::RecordWvProperty(const std::string& key,
const std::string& value) {
RecordProperty("widevine_metadata_oec_" + key, value);
}
const char* HDCPCapabilityAsString(OEMCrypto_HDCP_Capability value) { const char* HDCPCapabilityAsString(OEMCrypto_HDCP_Capability value) {
switch (value) { switch (value) {
case HDCP_NONE: case HDCP_NONE:
@@ -174,9 +249,9 @@ const char* HDCPCapabilityAsString(OEMCrypto_HDCP_Capability value) {
return "HDCP version 2.3"; return "HDCP version 2.3";
case HDCP_NO_DIGITAL_OUTPUT: case HDCP_NO_DIGITAL_OUTPUT:
return "No HDCP device attached/using local display with secure path"; return "No HDCP device attached/using local display with secure path";
default:
return "<INVALID VALUE>";
} }
// Not reachable unless the enum value is invalid
return "<INVALID VALUE>";
} }
// Return a printable string from data. If all the characters are printable, // Return a printable string from data. If all the characters are printable,
@@ -218,7 +293,7 @@ TEST_F(OEMCryptoClientTest, FreeUnallocatedSecureBufferNoFailure) {
*/ */
TEST_F(OEMCryptoClientTest, VersionNumber) { TEST_F(OEMCryptoClientTest, VersionNumber) {
const std::string log_message = const std::string log_message =
"OEMCrypto unit tests for API 18.7. Tests last updated 2024-09-04"; "OEMCrypto unit tests for API 18.8. Tests last updated 2024-11-04";
cout << " " << log_message << "\n"; cout << " " << log_message << "\n";
cout << " " cout << " "
<< "These tests are part of Android U." << "These tests are part of Android U."
@@ -227,30 +302,45 @@ TEST_F(OEMCryptoClientTest, VersionNumber) {
// If any of the following fail, then it is time to update the log message // If any of the following fail, then it is time to update the log message
// above. // above.
EXPECT_EQ(ODK_MAJOR_VERSION, 18); EXPECT_EQ(ODK_MAJOR_VERSION, 18);
EXPECT_EQ(ODK_MINOR_VERSION, 7); EXPECT_EQ(ODK_MINOR_VERSION, 8);
EXPECT_EQ(kCurrentAPI, static_cast<unsigned>(ODK_MAJOR_VERSION)); EXPECT_EQ(kCurrentAPI, static_cast<unsigned>(ODK_MAJOR_VERSION));
RecordWvProperty("test_major_version", std::to_string(ODK_MAJOR_VERSION));
RecordWvProperty("test_minor_version", std::to_string(ODK_MINOR_VERSION));
OEMCrypto_Security_Level level = OEMCrypto_SecurityLevel(); OEMCrypto_Security_Level level = OEMCrypto_SecurityLevel();
EXPECT_GT(level, OEMCrypto_Level_Unknown); EXPECT_GT(level, OEMCrypto_Level_Unknown);
EXPECT_LE(level, OEMCrypto_Level3); EXPECT_LE(level, OEMCrypto_Level3);
cout << " OEMCrypto Security Level is L" << level << endl; cout << " OEMCrypto Security Level is L" << level << endl;
RecordWvProperty("security_level", SecurityLevelName(level));
uint32_t version = OEMCrypto_APIVersion(); uint32_t version = OEMCrypto_APIVersion();
uint32_t minor_version = OEMCrypto_MinorAPIVersion(); uint32_t minor_version = OEMCrypto_MinorAPIVersion();
cout << " OEMCrypto API version is " << version << "." cout << " OEMCrypto API version is " << version << "."
<< minor_version << endl; << minor_version << endl;
if (OEMCrypto_SupportsUsageTable()) { RecordWvProperty("major_version", std::to_string(version));
RecordWvProperty("minor_version", std::to_string(minor_version));
const bool supports_usage_tables = OEMCrypto_SupportsUsageTable();
if (supports_usage_tables) {
cout << " OEMCrypto supports usage tables" << endl; cout << " OEMCrypto supports usage tables" << endl;
} else { } else {
cout << " OEMCrypto does not support usage tables" << endl; cout << " OEMCrypto does not support usage tables" << endl;
} }
RecordWvProperty("supports_usage_tables", BoolName(supports_usage_tables));
if (version >= 15) { if (version >= 15) {
const uint32_t tier = OEMCrypto_ResourceRatingTier(); const uint32_t tier = OEMCrypto_ResourceRatingTier();
cout << " Resource Rating Tier: " << tier << endl; cout << " Resource Rating Tier: " << tier << endl;
RecordWvProperty("resource_rating_tier", std::to_string(tier));
} }
if (version >= 17) { if (version >= 17) {
OEMCryptoResult sts = OEMCrypto_ProductionReady(); OEMCryptoResult sts = OEMCrypto_ProductionReady();
if (sts != OEMCrypto_SUCCESS) { if (sts != OEMCrypto_SUCCESS) {
LOGW("Device is not production ready, returns %d", sts); LOGW("Device is not production ready, returns %d", sts);
} }
RecordWvProperty("is_production_ready", BoolName(sts == OEMCrypto_SUCCESS));
std::string build_info; std::string build_info;
size_t buf_length = 0; size_t buf_length = 0;
sts = OEMCrypto_BuildInformation(&build_info[0], &buf_length); sts = OEMCrypto_BuildInformation(&build_info[0], &buf_length);
@@ -262,6 +352,7 @@ TEST_F(OEMCryptoClientTest, VersionNumber) {
if (build_info.size() != buf_length) { if (build_info.size() != buf_length) {
build_info.resize(buf_length); build_info.resize(buf_length);
} }
RecordWvProperty("build_information", build_info);
const std::string comma = ","; const std::string comma = ",";
const std::string pretty_comma = ",\n "; const std::string pretty_comma = ",\n ";
std::string::size_type pos = 0; std::string::size_type pos = 0;
@@ -270,9 +361,12 @@ TEST_F(OEMCryptoClientTest, VersionNumber) {
pos += pretty_comma.size(); pos += pretty_comma.size();
} }
cout << " BuildInformation: " << build_info << endl; cout << " BuildInformation: " << build_info << endl;
OEMCrypto_WatermarkingSupport support = OEMCrypto_GetWatermarkingSupport(); OEMCrypto_WatermarkingSupport support = OEMCrypto_GetWatermarkingSupport();
cout << " WatermarkingSupport: " << support << endl; cout << " WatermarkingSupport: " << support << endl;
RecordWvProperty("watermarking_support", WatermarkingSupportName(support));
} }
ASSERT_GE(version, 8u); ASSERT_GE(version, 8u);
ASSERT_LE(version, kCurrentAPI); ASSERT_LE(version, kCurrentAPI);
} }
@@ -292,8 +386,9 @@ TEST_F(OEMCryptoClientTest, ResourceRatingAPI15) {
TEST_F(OEMCryptoClientTest, ProvisioningDeclaredAPI12) { TEST_F(OEMCryptoClientTest, ProvisioningDeclaredAPI12) {
OEMCrypto_ProvisioningMethod provisioning_method = OEMCrypto_ProvisioningMethod provisioning_method =
OEMCrypto_GetProvisioningMethod(); OEMCrypto_GetProvisioningMethod();
cout << " Provisioning method = " const char* const name = ProvisioningMethodName(provisioning_method);
<< ProvisioningMethodName(provisioning_method) << endl; cout << " Provisioning method = " << name << endl;
RecordWvProperty("provisioning_method", name);
ASSERT_NE(OEMCrypto_ProvisioningError, provisioning_method); ASSERT_NE(OEMCrypto_ProvisioningError, provisioning_method);
} }
@@ -306,6 +401,8 @@ TEST_F(OEMCryptoClientTest, CheckHDCPCapabilityAPI09) {
static_cast<unsigned int>(current), HDCPCapabilityAsString(current)); static_cast<unsigned int>(current), HDCPCapabilityAsString(current));
printf(" Maximum HDCP Capability: 0x%02x = %s.\n", printf(" Maximum HDCP Capability: 0x%02x = %s.\n",
static_cast<unsigned int>(maximum), HDCPCapabilityAsString(maximum)); static_cast<unsigned int>(maximum), HDCPCapabilityAsString(maximum));
RecordWvProperty("hdcp_current", HDCPCapabilityName(current));
RecordWvProperty("hdcp_max", HDCPCapabilityName(maximum));
} }
TEST_F(OEMCryptoClientTest, CheckSRMCapabilityV13) { TEST_F(OEMCryptoClientTest, CheckSRMCapabilityV13) {
@@ -314,11 +411,15 @@ TEST_F(OEMCryptoClientTest, CheckSRMCapabilityV13) {
OEMCryptoResult current_result = OEMCrypto_GetCurrentSRMVersion(&version); OEMCryptoResult current_result = OEMCrypto_GetCurrentSRMVersion(&version);
if (current_result == OEMCrypto_SUCCESS) { if (current_result == OEMCrypto_SUCCESS) {
printf(" Current SRM Version: %d.\n", version); printf(" Current SRM Version: %d.\n", version);
RecordWvProperty("srm_supported", BoolName(true));
RecordWvProperty("srm_version", std::to_string(version));
EXPECT_NE(OEMCrypto_SUCCESS, OEMCrypto_GetCurrentSRMVersion(nullptr)); EXPECT_NE(OEMCrypto_SUCCESS, OEMCrypto_GetCurrentSRMVersion(nullptr));
} else if (current_result == OEMCrypto_LOCAL_DISPLAY_ONLY) { } else if (current_result == OEMCrypto_LOCAL_DISPLAY_ONLY) {
printf(" Current SRM Status: Local Display Only.\n"); printf(" Current SRM Status: Local Display Only.\n");
RecordWvProperty("srm_supported", BoolName(false));
} else { } else {
EXPECT_EQ(OEMCrypto_ERROR_NOT_IMPLEMENTED, current_result); EXPECT_EQ(OEMCrypto_ERROR_NOT_IMPLEMENTED, current_result);
RecordWvProperty("srm_supported", BoolName(false));
} }
} }
@@ -547,6 +648,9 @@ TEST_F(OEMCryptoClientTest, CheckJsonBuildInformationAPI18) {
{"is_debug", JSMN_PRIMITIVE}, {"is_debug", JSMN_PRIMITIVE},
}; };
// The prefix of every field name when logged to RecordWvProperty.
const std::string kBuildInfoRecordPrefix = "build_info_";
// A set of the required fields found when examining the // A set of the required fields found when examining the
// build information, use to verify all fields are present. // build information, use to verify all fields are present.
std::set<std::string> found_required_fields; std::set<std::string> found_required_fields;
@@ -573,11 +677,17 @@ TEST_F(OEMCryptoClientTest, CheckJsonBuildInformationAPI18) {
<< "Unexpected required field type: field = " << key << "Unexpected required field type: field = " << key
<< ", build_info = " << build_info; << ", build_info = " << build_info;
found_required_fields.insert(key); found_required_fields.insert(key);
RecordWvProperty(kBuildInfoRecordPrefix + key,
build_info.substr(value_token.start,
value_token.end - value_token.start));
} else if (kOptionalFields.find(key) != kOptionalFields.end()) { } else if (kOptionalFields.find(key) != kOptionalFields.end()) {
ASSERT_EQ(value_token.type, kOptionalFields.at(key)) ASSERT_EQ(value_token.type, kOptionalFields.at(key))
<< "Unexpected optional field type: field = " << key << "Unexpected optional field type: field = " << key
<< ", build_info = " << build_info; << ", build_info = " << build_info;
} // Do not validate vendor fields. RecordWvProperty(kBuildInfoRecordPrefix + key,
build_info.substr(value_token.start,
value_token.end - value_token.start));
} // Do not validate or record vendor fields.
if (key == kSpecialCaseReeKey) { if (key == kSpecialCaseReeKey) {
// Store the tokens of the "ree" field for additional validation. // Store the tokens of the "ree" field for additional validation.
@@ -618,6 +728,11 @@ TEST_F(OEMCryptoClientTest, CheckJsonBuildInformationAPI18) {
// If no "ree" field tokens, then end here. // If no "ree" field tokens, then end here.
if (!has_ree_info) return; if (!has_ree_info) return;
// Step 4a: Verify "ree" object scheme. // Step 4a: Verify "ree" object scheme.
// The prefix of every REE field name when logged to RecordWvProperty.
const std::string kReeBuildInfoRecordPrefix =
kBuildInfoRecordPrefix + kSpecialCaseReeKey + "_";
ASSERT_FALSE(ree_tokens.empty()) ASSERT_FALSE(ree_tokens.empty())
<< "REE field was specified, but contents were empty: build_info = " << "REE field was specified, but contents were empty: build_info = "
<< build_info; << build_info;
@@ -647,7 +762,10 @@ TEST_F(OEMCryptoClientTest, CheckJsonBuildInformationAPI18) {
<< "Unexpected optional REE field type: ree_field = " << key << "Unexpected optional REE field type: ree_field = " << key
<< ", build_info = " << build_info; << ", build_info = " << build_info;
found_required_fields.insert(key); found_required_fields.insert(key);
} // Do not validate vendor fields. RecordWvProperty(kReeBuildInfoRecordPrefix + key,
build_info.substr(value_token.start,
value_token.end - value_token.start));
} // Do not validate or record vendor fields.
// Skip potential nested tokens. // Skip potential nested tokens.
i += JsmnAncestorCount(ree_tokens, i + 1); i += JsmnAncestorCount(ree_tokens, i + 1);
@@ -683,6 +801,7 @@ TEST_F(OEMCryptoClientTest, CheckMaxNumberOfSessionsAPI10) {
OEMCryptoResult sts = OEMCrypto_GetMaxNumberOfSessions(&maximum); OEMCryptoResult sts = OEMCrypto_GetMaxNumberOfSessions(&maximum);
ASSERT_EQ(OEMCrypto_SUCCESS, sts); ASSERT_EQ(OEMCrypto_SUCCESS, sts);
printf(" Max Number of Sessions: %zu.\n", maximum); printf(" Max Number of Sessions: %zu.\n", maximum);
RecordWvProperty("max_number_of_sessions", std::to_string(maximum));
size_t required_max = GetResourceValue(kMaxConcurrentSession); size_t required_max = GetResourceValue(kMaxConcurrentSession);
ASSERT_GE(maximum, required_max); ASSERT_GE(maximum, required_max);
} }
@@ -690,6 +809,7 @@ TEST_F(OEMCryptoClientTest, CheckMaxNumberOfSessionsAPI10) {
TEST_F(OEMCryptoClientTest, CheckUsageTableSizeAPI16) { TEST_F(OEMCryptoClientTest, CheckUsageTableSizeAPI16) {
const size_t maximum = OEMCrypto_MaximumUsageTableHeaderSize(); const size_t maximum = OEMCrypto_MaximumUsageTableHeaderSize();
printf(" Max Usage Table Size: %zu.\n", maximum); printf(" Max Usage Table Size: %zu.\n", maximum);
RecordWvProperty("max_usage_table_size", std::to_string(maximum));
// A maximum of 0 means the table is constrained by dynamic memory allocation. // A maximum of 0 means the table is constrained by dynamic memory allocation.
if (maximum > 0) { if (maximum > 0) {
ASSERT_GE(maximum, RequiredUsageSize()); ASSERT_GE(maximum, RequiredUsageSize());
@@ -723,6 +843,7 @@ TEST_F(OEMCryptoClientTest, CheckDTCP2CapabilityAPI17) {
"DTCP2 is supported.\n"); "DTCP2 is supported.\n");
break; break;
} }
RecordWvProperty("dtcp2_capability", DTCP2CapabiityName(capability));
} }
// //

View File

@@ -35,6 +35,7 @@ class OEMCryptoClientTest : public ::testing::Test, public SessionUtil {
size_t input_buffer_size, size_t input_buffer_size,
const OEMCrypto_DestBufferDesc* dest_buffer_descriptor, const OEMCrypto_DestBufferDesc* dest_buffer_descriptor,
uint8_t subsample_flags); uint8_t subsample_flags);
void RecordWvProperty(const std::string& key, const std::string& value);
}; };
} // namespace wvoec } // namespace wvoec

View File

@@ -5,12 +5,13 @@
#include "oemcrypto_cast_test.h" #include "oemcrypto_cast_test.h"
#include "oemcrypto_usage_table_test.h"
using ::testing::Range; using ::testing::Range;
namespace wvoec { namespace wvoec {
/// @addtogroup cast
/// @{
/** If a device can load a private key with the alternate padding schemes, it /** If a device can load a private key with the alternate padding schemes, it
* should support signing with the alternate scheme. */ * should support signing with the alternate scheme. */
TEST_F(OEMCryptoLoadsCertificateAlternates, TestSignaturePKCS1) { TEST_F(OEMCryptoLoadsCertificateAlternates, TestSignaturePKCS1) {
@@ -262,18 +263,8 @@ class OEMCryptoCastReceiverTest : public OEMCryptoLoadsCertificateAlternates {
ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(s.open());
ASSERT_NO_FATAL_FAILURE(s.LoadWrappedRsaDrmKey(wrapped_drm_key_)); ASSERT_NO_FATAL_FAILURE(s.LoadWrappedRsaDrmKey(wrapped_drm_key_));
// The application will compute the SHA-1 Hash of the message, so this vector<uint8_t> digest;
// test must do that also. ASSERT_NO_FATAL_FAILURE(PrepareCastDigestedMessage(message, digest));
uint8_t hash[SHA_DIGEST_LENGTH];
if (!SHA1(message.data(), message.size(), hash)) {
dump_boringssl_error();
FAIL() << "boringssl error creating SHA1 hash.";
}
// The application will prepend the digest info to the hash.
// SHA-1 digest info prefix = 0x30 0x21 0x30 ...
vector<uint8_t> digest = wvutil::a2b_hex("3021300906052b0e03021a05000414");
digest.insert(digest.end(), hash, hash + SHA_DIGEST_LENGTH);
// OEMCrypto will apply the padding, and encrypt to generate the // OEMCrypto will apply the padding, and encrypt to generate the
// signature. // signature.
@@ -1018,4 +1009,6 @@ TEST_P(OEMCryptoSessionTestLoadCasKeysWithHDCP, CasOnlyLoadCasKeysAPI17) {
} }
INSTANTIATE_TEST_SUITE_P(TestHDCP, OEMCryptoSessionTestLoadCasKeysWithHDCP, INSTANTIATE_TEST_SUITE_P(TestHDCP, OEMCryptoSessionTestLoadCasKeysWithHDCP,
Range(1, 6)); Range(1, 6));
/// @}
} // namespace wvoec } // namespace wvoec

View File

@@ -14,6 +14,7 @@
#include "OEMCryptoCENC.h" #include "OEMCryptoCENC.h"
#include "oemcrypto_provisioning_test.h" #include "oemcrypto_provisioning_test.h"
#include "oemcrypto_session_tests_helper.h" #include "oemcrypto_session_tests_helper.h"
#include "oemcrypto_usage_table_test.h"
namespace wvoec { namespace wvoec {
@@ -25,6 +26,25 @@ std::string MaybeHex(const std::vector<uint8_t>& data);
// This test attempts to use alternate algorithms for loaded device certs. // This test attempts to use alternate algorithms for loaded device certs.
class OEMCryptoLoadsCertificateAlternates : public OEMCryptoLoadsCertificate { class OEMCryptoLoadsCertificateAlternates : public OEMCryptoLoadsCertificate {
protected: protected:
// The message to be signed by OEMCrypto_GenerateRSASignature() starts with a
// constant digest info prefix followed by a SHA-1 hash of the message.
void PrepareCastDigestedMessage(const std::vector<uint8_t>& message,
std::vector<uint8_t>& digest) {
// The application will compute the SHA-1 Hash of the message, so this
// test must do that also.
uint8_t hash[SHA_DIGEST_LENGTH];
if (!SHA1(message.data(), message.size(), hash)) {
dump_boringssl_error();
FAIL() << "boringssl error creating SHA1 hash.";
}
// The application will prepend the digest info to the hash.
// SHA-1 digest info prefix = 0x30 0x21 0x30 ...
static const std::vector<uint8_t> prefix =
wvutil::a2b_hex("3021300906052b0e03021a05000414");
digest.insert(digest.end(), prefix.begin(), prefix.end());
digest.insert(digest.end(), hash, hash + SHA_DIGEST_LENGTH);
}
void TestSignature(RSA_Padding_Scheme scheme, size_t size) { void TestSignature(RSA_Padding_Scheme scheme, size_t size) {
Session s; Session s;
ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(s.open());
@@ -32,16 +52,19 @@ class OEMCryptoLoadsCertificateAlternates : public OEMCryptoLoadsCertificate {
vector<uint8_t> licenseRequest(size); vector<uint8_t> licenseRequest(size);
GetRandBytes(licenseRequest.data(), licenseRequest.size()); GetRandBytes(licenseRequest.data(), licenseRequest.size());
vector<uint8_t> digested_message;
ASSERT_NO_FATAL_FAILURE(
PrepareCastDigestedMessage(licenseRequest, digested_message));
size_t signature_length = 0; size_t signature_length = 0;
OEMCryptoResult sts = OEMCrypto_GenerateRSASignature( OEMCryptoResult sts = OEMCrypto_GenerateRSASignature(
s.session_id(), licenseRequest.data(), licenseRequest.size(), nullptr, s.session_id(), digested_message.data(), digested_message.size(),
&signature_length, scheme); nullptr, &signature_length, scheme);
ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts);
ASSERT_NE(static_cast<size_t>(0), signature_length); ASSERT_NE(static_cast<size_t>(0), signature_length);
std::vector<uint8_t> signature(signature_length, 0); std::vector<uint8_t> signature(signature_length, 0);
sts = OEMCrypto_GenerateRSASignature( sts = OEMCrypto_GenerateRSASignature(
s.session_id(), licenseRequest.data(), licenseRequest.size(), s.session_id(), digested_message.data(), digested_message.size(),
signature.data(), &signature_length, scheme); signature.data(), &signature_length, scheme);
ASSERT_EQ(OEMCrypto_SUCCESS, sts) ASSERT_EQ(OEMCrypto_SUCCESS, sts)
@@ -51,7 +74,7 @@ class OEMCryptoLoadsCertificateAlternates : public OEMCryptoLoadsCertificate {
ASSERT_NO_FATAL_FAILURE(s.SetRsaPublicKeyFromPrivateKeyInfo( ASSERT_NO_FATAL_FAILURE(s.SetRsaPublicKeyFromPrivateKeyInfo(
encoded_rsa_key_.data(), encoded_rsa_key_.size())); encoded_rsa_key_.data(), encoded_rsa_key_.size()));
ASSERT_NO_FATAL_FAILURE(s.VerifyRsaSignature( ASSERT_NO_FATAL_FAILURE(s.VerifyRsaSignature(
licenseRequest, signature.data(), signature_length, scheme)); digested_message, signature.data(), signature_length, scheme));
} }
void DisallowDeriveKeys() { void DisallowDeriveKeys() {

View File

@@ -13,6 +13,9 @@ using ::testing::Values;
namespace wvoec { namespace wvoec {
/// @addtogroup decrypt
/// @{
// Cannot decrypt without first getting a key handle. // Cannot decrypt without first getting a key handle.
TEST_P(OEMCryptoLicenseTest, FailDecryptWithoutGettingAHandle) { TEST_P(OEMCryptoLicenseTest, FailDecryptWithoutGettingAHandle) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
@@ -661,4 +664,5 @@ TEST_P(OEMCryptoLicenseTest, KeyDuration) {
INSTANTIATE_TEST_SUITE_P(TestAll, OEMCryptoLicenseTest, INSTANTIATE_TEST_SUITE_P(TestAll, OEMCryptoLicenseTest,
Range<uint32_t>(kCurrentAPI - 2, kCurrentAPI + 1)); Range<uint32_t>(kCurrentAPI - 2, kCurrentAPI + 1));
/// @}
} // namespace wvoec } // namespace wvoec

View File

@@ -11,6 +11,9 @@ using ::testing::Range;
namespace wvoec { namespace wvoec {
/// @addtogroup generic
/// @{
TEST_P(OEMCryptoGenericCryptoTest, GenericKeyLoad) { EncryptAndLoadKeys(); } TEST_P(OEMCryptoGenericCryptoTest, GenericKeyLoad) { EncryptAndLoadKeys(); }
// Test that the Generic_Encrypt function works correctly. // Test that the Generic_Encrypt function works correctly.
@@ -574,4 +577,4 @@ INSTANTIATE_TEST_SUITE_P(TestAll, OEMCryptoGenericCryptoKeyIdLengthTest,
Range<uint32_t>(kCoreMessagesAPI, kCurrentAPI + 1)); Range<uint32_t>(kCoreMessagesAPI, kCurrentAPI + 1));
/// @} /// @}
} // namespace wvoec } // namespace wvoec

View File

@@ -5,6 +5,8 @@
#include "oemcrypto_license_test.h" #include "oemcrypto_license_test.h"
#include <string>
#include "platform.h" #include "platform.h"
#include "test_sleep.h" #include "test_sleep.h"
@@ -12,6 +14,9 @@ using ::testing::Range;
namespace wvoec { namespace wvoec {
/// @addtogroup license
/// @{
// Function to test APIs that expect a buffer length as input // Function to test APIs that expect a buffer length as input
// by passing huge buffer lengths up to end_buffer_length and test that the API // by passing huge buffer lengths up to end_buffer_length and test that the API
// doesn't crash. // doesn't crash.
@@ -758,6 +763,7 @@ TEST_P(OEMCryptoLicenseTest, MaxTotalKeysManySessions) {
TEST_F(OEMCryptoSessionTests, CheckMinimumPatchLevel) { TEST_F(OEMCryptoSessionTests, CheckMinimumPatchLevel) {
uint8_t patch_level = OEMCrypto_Security_Patch_Level(); uint8_t patch_level = OEMCrypto_Security_Patch_Level();
printf(" Current Patch Level: %u.\n", patch_level); printf(" Current Patch Level: %u.\n", patch_level);
RecordWvProperty("security_patch_level", std::to_string(patch_level));
{ {
Session s; Session s;
ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(s.open());

View File

@@ -5,6 +5,10 @@
#include "oemcrypto_provisioning_test.h" #include "oemcrypto_provisioning_test.h"
#include <stdint.h>
#include <string>
#include "log.h" #include "log.h"
#include "oec_device_features.h" #include "oec_device_features.h"
#include "platform.h" #include "platform.h"
@@ -12,6 +16,9 @@
namespace wvoec { namespace wvoec {
/// @addtogroup provision
/// @{
// This test is used to print the device ID to stdout. // This test is used to print the device ID to stdout.
TEST_F(OEMCryptoKeyboxTest, NormalGetDeviceId) { TEST_F(OEMCryptoKeyboxTest, NormalGetDeviceId) {
OEMCryptoResult sts; OEMCryptoResult sts;
@@ -21,6 +28,7 @@ TEST_F(OEMCryptoKeyboxTest, NormalGetDeviceId) {
ASSERT_EQ(OEMCrypto_SUCCESS, sts); ASSERT_EQ(OEMCrypto_SUCCESS, sts);
cout << " NormalGetDeviceId: dev_id = " cout << " NormalGetDeviceId: dev_id = "
<< MaybeHex(dev_id, dev_id_len) << " len = " << dev_id_len << endl; << MaybeHex(dev_id, dev_id_len) << " len = " << dev_id_len << endl;
RecordWvProperty("device_id", wvutil::HexEncode(dev_id, dev_id_len));
} }
TEST_F(OEMCryptoKeyboxTest, GetDeviceIdShortBuffer) { TEST_F(OEMCryptoKeyboxTest, GetDeviceIdShortBuffer) {
@@ -50,8 +58,12 @@ TEST_F(OEMCryptoKeyboxTest, NormalGetKeyData) {
sts = OEMCrypto_GetKeyData(key_data, &key_data_len); sts = OEMCrypto_GetKeyData(key_data, &key_data_len);
uint32_t* data = reinterpret_cast<uint32_t*>(key_data); uint32_t* data = reinterpret_cast<uint32_t*>(key_data);
const uint32_t system_id = htonl(data[1]);
const uint32_t version = htonl(data[0]);
printf(" NormalGetKeyData: system_id = %u = 0x%04X, version=%u\n", printf(" NormalGetKeyData: system_id = %u = 0x%04X, version=%u\n",
htonl(data[1]), htonl(data[1]), htonl(data[0])); system_id, system_id, version);
RecordWvProperty("system_id", std::to_string(system_id));
RecordWvProperty("key_data_version", std::to_string(version));
ASSERT_EQ(OEMCrypto_SUCCESS, sts); ASSERT_EQ(OEMCrypto_SUCCESS, sts);
} }
@@ -106,6 +118,8 @@ TEST_F(OEMCryptoProv30Test, GetDeviceId) {
dev_id.resize(dev_id_len); dev_id.resize(dev_id_len);
cout << " NormalGetDeviceId: dev_id = " << MaybeHex(dev_id) cout << " NormalGetDeviceId: dev_id = " << MaybeHex(dev_id)
<< " len = " << dev_id_len << endl; << " len = " << dev_id_len << endl;
RecordWvProperty("device_id",
wvutil::HexEncode(dev_id.data(), dev_id.size()));
} }
// The OEM certificate must be valid. // The OEM certificate must be valid.
@@ -545,13 +559,13 @@ TEST_F(OEMCryptoProv40Test, InstallOemPrivateKeyCanBeUsed) {
wrapped_private_key2.resize(wrapped_private_key_size2); wrapped_private_key2.resize(wrapped_private_key_size2);
// Verify public_key_signature2 with public_key1. // Verify public_key_signature2 with public_key1.
if (key_type2 == OEMCrypto_PrivateKeyType::OEMCrypto_RSA_Private_Key) { if (key_type1 == OEMCrypto_PrivateKeyType::OEMCrypto_RSA_Private_Key) {
ASSERT_NO_FATAL_FAILURE(s.SetRsaPublicKeyFromSubjectPublicKey( ASSERT_NO_FATAL_FAILURE(s.SetRsaPublicKeyFromSubjectPublicKey(
public_key1.data(), public_key1.size())); public_key1.data(), public_key1.size()));
ASSERT_NO_FATAL_FAILURE( ASSERT_NO_FATAL_FAILURE(
s.VerifyRsaSignature(public_key2, public_key_signature2.data(), s.VerifyRsaSignature(public_key2, public_key_signature2.data(),
public_key_signature2.size(), kSign_RSASSA_PSS)); public_key_signature2.size(), kSign_RSASSA_PSS));
} else if (key_type2 == OEMCrypto_PrivateKeyType::OEMCrypto_ECC_Private_Key) { } else if (key_type1 == OEMCrypto_PrivateKeyType::OEMCrypto_ECC_Private_Key) {
ASSERT_NO_FATAL_FAILURE(s.SetEccPublicKeyFromSubjectPublicKey( ASSERT_NO_FATAL_FAILURE(s.SetEccPublicKeyFromSubjectPublicKey(
public_key1.data(), public_key1.size())); public_key1.data(), public_key1.size()));
ASSERT_NO_FATAL_FAILURE(s.VerifyEccSignature(public_key2, ASSERT_NO_FATAL_FAILURE(s.VerifyEccSignature(public_key2,
@@ -619,6 +633,8 @@ TEST_F(OEMCryptoProv40Test, GetDeviceId) {
dev_id.resize(dev_id_len); dev_id.resize(dev_id_len);
cout << " NormalGetDeviceId: dev_id = " << MaybeHex(dev_id) cout << " NormalGetDeviceId: dev_id = " << MaybeHex(dev_id)
<< " len = " << dev_id_len << endl; << " len = " << dev_id_len << endl;
RecordWvProperty("device_id",
wvutil::HexEncode(dev_id.data(), dev_id.size()));
// Device id should be stable. Query again. // Device id should be stable. Query again.
std::vector<uint8_t> dev_id2(dev_id_len); std::vector<uint8_t> dev_id2(dev_id_len);
sts = OEMCrypto_GetDeviceID(dev_id2.data(), &dev_id_len); sts = OEMCrypto_GetDeviceID(dev_id2.data(), &dev_id_len);
@@ -1310,4 +1326,5 @@ TEST_F(OEMCryptoUsesCertificate, GenerateDerivedKeysLargeBuffer) {
enc_context.data(), enc_context.size())); enc_context.data(), enc_context.size()));
} }
/// @}
} // namespace wvoec } // namespace wvoec

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -21,6 +21,9 @@
namespace wvoec { namespace wvoec {
/// @addtogroup android
/// @{
// These tests are required for LollyPop Android devices. // These tests are required for LollyPop Android devices.
class OEMCryptoAndroidLMPTest : public ::testing::Test { class OEMCryptoAndroidLMPTest : public ::testing::Test {
protected: protected:
@@ -174,4 +177,5 @@ TEST_F(OEMCryptoAndroidQTest, MinVersionNumber14) {
ASSERT_GE(version, 15u); ASSERT_GE(version, 15u);
} }
/// @}
} // namespace wvoec } // namespace wvoec

View File

@@ -23,6 +23,7 @@
'oemcrypto_generic_crypto_test.cpp', 'oemcrypto_generic_crypto_test.cpp',
'oemcrypto_license_test.cpp', 'oemcrypto_license_test.cpp',
'oemcrypto_provisioning_test.cpp', 'oemcrypto_provisioning_test.cpp',
'oemcrypto_security_test.cpp',
'oemcrypto_usage_table_test.cpp', 'oemcrypto_usage_table_test.cpp',
'oemcrypto_test.cpp', 'oemcrypto_test.cpp',
'<(jsmn_dir)/jsmn.c', '<(jsmn_dir)/jsmn.c',

View File

@@ -10,6 +10,9 @@ using ::testing::Values;
namespace wvoec { namespace wvoec {
/// @addtogroup usage_table
/// @{
// Test that successive calls to PrepAndSignProvisioningRequest only increase // Test that successive calls to PrepAndSignProvisioningRequest only increase
// the provisioning count in the ODK message // the provisioning count in the ODK message
TEST_F(OEMCryptoSessionTests, Provisioning_IncrementCounterAPI18) { TEST_F(OEMCryptoSessionTests, Provisioning_IncrementCounterAPI18) {
@@ -1757,4 +1760,5 @@ INSTANTIATE_TEST_SUITE_P(TestAPI16, OEMCryptoUsageTableDefragTest,
INSTANTIATE_TEST_SUITE_P(TestAPI16, OEMCryptoUsageTableTestWallClock, INSTANTIATE_TEST_SUITE_P(TestAPI16, OEMCryptoUsageTableTestWallClock,
Values<uint32_t>(kCurrentAPI)); Values<uint32_t>(kCurrentAPI));
/// @}
} // namespace wvoec } // namespace wvoec

View File

@@ -7,7 +7,8 @@
#ifndef WVCDM_UTIL_FILE_STORE_H_ #ifndef WVCDM_UTIL_FILE_STORE_H_
#define WVCDM_UTIL_FILE_STORE_H_ #define WVCDM_UTIL_FILE_STORE_H_
#include <cstdint> #include <stddef.h>
#include <memory> #include <memory>
#include <string> #include <string>
#include <vector> #include <vector>
@@ -18,16 +19,35 @@
namespace wvutil { namespace wvutil {
// Fixed filename for ATSC DRM certificate pre-installed
// on ATSC devices for ATSC licenses.
static const std::string kAtscCertificateFileName = "atsccert.bin"; static const std::string kAtscCertificateFileName = "atsccert.bin";
// General filename for either global or unmapped app-origin
// DRM certificates.
static const std::string kCertificateFileName = "cert1.bin"; static const std::string kCertificateFileName = "cert1.bin";
// File extension for DRM and OEM certificate files.
static const std::string kCertificateFileNameExt = ".bin"; static const std::string kCertificateFileNameExt = ".bin";
static const std::string kCertificateFileNamePrefix = "cert1_"; // Filename prefix for mapped (scoped) DRM certificate filenames
// specific to a particular app-origin.
static const std::string kScopedCertificateFilenamePrefix = "cert1_";
// TODO(b/376533901): Replace this constant with
// kScopedCertificateFilenamePrefix in source code..
static const std::string kCertificateFileNamePrefix =
kScopedCertificateFilenamePrefix;
// Legacy general filename for either global or unmapped app-origin
// DRM certificates.
static const std::string kLegacyCertificateFileName = "cert.bin"; static const std::string kLegacyCertificateFileName = "cert.bin";
static const std::string kLegacyCertificateFileNamePrefix = "cert"; // Legacy filename prefix for mapped (scoped) DRM certificate filenames
// specific to a particular app-origin.
static const std::string kLegacyScopedCertificateFilenamePrefix = "cert";
// TODO(b/376533901): Replace this constant with
// kLegacyScopedCertificateFilenamePrefix in source code..
static const std::string kLegacyCertificateFileNamePrefix =
kLegacyScopedCertificateFilenamePrefix;
// Filename for global OEM certificates.
static const std::string kOemCertificateFileName = "oemcert.bin"; static const std::string kOemCertificateFileName = "oemcert.bin";
static const std::string kOemCertificateFileNamePrefix = "oemcert_";
// File class. The implementation is platform dependent. // File interface. The implementation is platform dependent.
class File { class File {
public: public:
File() {} File() {}
@@ -35,35 +55,70 @@ class File {
virtual ssize_t Read(char* buffer, size_t bytes) = 0; virtual ssize_t Read(char* buffer, size_t bytes) = 0;
virtual ssize_t Write(const char* buffer, size_t bytes) = 0; virtual ssize_t Write(const char* buffer, size_t bytes) = 0;
friend class FileSystem;
CORE_DISALLOW_COPY_AND_ASSIGN(File); CORE_DISALLOW_COPY_AND_ASSIGN(File);
}; };
// File system base class. The implementation is platform dependent.
class FileSystem { class FileSystem {
public: public:
FileSystem(); FileSystem();
FileSystem(const std::string& origin, void* extra_data); FileSystem(const std::string& origin, void* extra_data);
virtual ~FileSystem(); virtual ~FileSystem();
// Concreate implementation of FileSystem.
// Depending on the platform, this may be vendor or Widevine implemented.
class Impl; class Impl;
// defines as bit flag // Flags for calls to Open.
enum OpenFlags { static constexpr int kNoFlags = 0;
kNoFlags = 0, // Create file if does not already exist, open file if it does exist.
kCreate = 1, static constexpr int kCreate = (1 << 0);
kReadOnly = 2, // defaults to read and write access // Open file as read-only; typically should not be used with kCreate.
kTruncate = 4 static constexpr int kReadOnly = (1 << 1);
}; // Open file and truncated. May be used with kCreate; should not
// be used with kReadOnly.
static constexpr int kTruncate = (1 << 2);
virtual std::unique_ptr<File> Open(const std::string& file_path, int flags); virtual std::unique_ptr<File> Open(const std::string& file_path, int flags);
virtual bool Exists(const std::string& file_path); // Checks if the |path| exists. The |path| may be a file or directory.
virtual bool Exists(const std::string& file_path, int* errno_value); // Return true if an entry in the file system exists; false otherwise.
virtual bool Remove(const std::string& file_path); virtual bool Exists(const std::string& path);
// Same as above, except the optional parameter of |errno_value| should
// be set to 0 or the value of C errno when attempting to check
// the existence of a file.
virtual bool Exists(const std::string& path, int* errno_value);
// Removes the specified |path|.
//
// If |path| is a regular file, the file should be removed.
// If |path| is a directory, both the directory and the directory
// contents should be removed.
//
// Implementation must support a |path| containing a single wildcard
// character in the filename component of the path.
//
// Return value:
// - true : File/directory was removed, or file/directory did not exist
// - false : File/directory could not be removed, or other error.
virtual bool Remove(const std::string& path);
// Obtain the size of a file in bytes. |file_path| must be a file,
// and not a directory.
//
// Return value:
// - non-negative : size of file in bytes if file exists
// - negative : file does not exist, or error occurred.
virtual ssize_t FileSize(const std::string& file_path); virtual ssize_t FileSize(const std::string& file_path);
// Return the filenames stored at dir_path. // Return the entries stored at |dir_path| (includes both files
// dir_path will be stripped from the returned names. // and directories).
//
// Return value:
// - true : Directory exists, and directory entry names are stored
// in |names|; |names| may be empty if directory was empty.
// - false : Directory does not exist, |dir_path| is not a directory,
// or error was encountered.
virtual bool List(const std::string& dir_path, virtual bool List(const std::string& dir_path,
std::vector<std::string>* names); std::vector<std::string>* names);

View File

@@ -1,9 +1,14 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary // Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License // source code may only be used and distributed under the Widevine License
// Agreement. // Agreement.
#include "file_store.h" #include "file_store.h"
#include <errno.h>
#include <set>
#include <string>
#include <vector>
#include <gmock/gmock.h> #include <gmock/gmock.h>
#include <gtest/gtest.h> #include <gtest/gtest.h>
@@ -11,18 +16,20 @@
#include "test_vectors.h" #include "test_vectors.h"
namespace wvutil { namespace wvutil {
namespace { namespace {
const std::string kTestDirName = "test_dir"; constexpr char kTestFilename[] = "sample.txt";
const std::string kTestFileName = "test.txt";
const std::string kTestFileName2 = "test2.txt"; constexpr char kTestIdentifier1[] = "some_identifier";
const std::string kTestFileName3 = "test3.other"; constexpr char kTestIdentifier2[] = "some_other_identifier";
const std::string kTestFileNameExt = ".txt";
const std::string kTestFileNameExt3 = ".other"; constexpr int kNoError = 0;
const std::string kTestIdentifier1 = "some_identifier"; constexpr int kEntryDoesNotExist = ENOENT;
const std::string kTestIdentifier2 = "some_other_identifier";
const std::string kWildcard = "*"; bool StartsWith(const std::string& haystack, const std::string& needle) {
const std::string kUnderscore = "_"; if (needle.empty()) return true;
if (haystack.size() < needle.size()) return false;
return haystack.find(needle) == 0;
}
} // namespace } // namespace
class FileTest : public testing::Test { class FileTest : public testing::Test {
@@ -32,7 +39,19 @@ class FileTest : public testing::Test {
void TearDown() override { RemoveTestDir(); } void TearDown() override { RemoveTestDir(); }
void RemoveTestDir() { void RemoveTestDir() {
EXPECT_TRUE(file_system_.Remove(wvcdm::test_vectors::kTestDir)); ASSERT_TRUE(file_system_.Remove(wvcdm::test_vectors::kTestDir))
<< "Failed to update test directory: " << wvcdm::test_vectors::kTestDir;
}
std::string PathJoin(const std::string& base_path,
const std::string& add_path) {
if (base_path.empty()) return add_path;
std::string path = base_path;
if (path.back() != '/') {
path.push_back('/');
}
path.append(add_path);
return path;
} }
FileSystem file_system_; FileSystem file_system_;
@@ -40,336 +59,505 @@ class FileTest : public testing::Test {
TEST_F(FileTest, FileExists) { TEST_F(FileTest, FileExists) {
int errno_value = -1; int errno_value = -1;
EXPECT_TRUE(file_system_.Exists(wvcdm::test_vectors::kExistentFile)); EXPECT_TRUE(file_system_.Exists(wvcdm::test_vectors::kExistentFile))
<< "path = " << wvcdm::test_vectors::kExistentFile;
EXPECT_TRUE( EXPECT_TRUE(
file_system_.Exists(wvcdm::test_vectors::kExistentFile, &errno_value)); file_system_.Exists(wvcdm::test_vectors::kExistentFile, &errno_value))
EXPECT_EQ(0, errno_value); << "path = " << wvcdm::test_vectors::kExistentFile;
EXPECT_TRUE(file_system_.Exists(wvcdm::test_vectors::kExistentDir)); EXPECT_EQ(kNoError, errno_value);
}
EXPECT_FALSE(file_system_.Exists(wvcdm::test_vectors::kNonExistentFile)); TEST_F(FileTest, FileDoesNotExist) {
int errno_value = -1;
EXPECT_FALSE(file_system_.Exists(wvcdm::test_vectors::kNonExistentFile))
<< "path = " << wvcdm::test_vectors::kNonExistentFile;
EXPECT_FALSE( EXPECT_FALSE(
file_system_.Exists(wvcdm::test_vectors::kNonExistentFile, &errno_value)); file_system_.Exists(wvcdm::test_vectors::kNonExistentFile, &errno_value))
EXPECT_EQ(ENOENT, errno_value); << "path = " << wvcdm::test_vectors::kNonExistentFile;
EXPECT_FALSE(file_system_.Exists(wvcdm::test_vectors::kNonExistentDir)); EXPECT_EQ(kEntryDoesNotExist, errno_value);
}
TEST_F(FileTest, DirectoryExists) {
int errno_value = -1;
EXPECT_TRUE(file_system_.Exists(wvcdm::test_vectors::kExistentDir))
<< "path = " << wvcdm::test_vectors::kExistentDir;
EXPECT_TRUE(
file_system_.Exists(wvcdm::test_vectors::kExistentDir, &errno_value))
<< "path = " << wvcdm::test_vectors::kExistentDir;
EXPECT_EQ(kNoError, errno_value);
}
TEST_F(FileTest, DirectoryDoesNotExist) {
int errno_value = -1;
EXPECT_FALSE(file_system_.Exists(wvcdm::test_vectors::kNonExistentDir))
<< "path = " << wvcdm::test_vectors::kNonExistentDir;
EXPECT_FALSE(
file_system_.Exists(wvcdm::test_vectors::kNonExistentDir, &errno_value))
<< "path = " << wvcdm::test_vectors::kNonExistentDir;
EXPECT_EQ(kEntryDoesNotExist, errno_value);
} }
TEST_F(FileTest, RemoveDir) { TEST_F(FileTest, RemoveDir) {
EXPECT_TRUE(file_system_.Remove(wvcdm::test_vectors::kTestDir)); EXPECT_TRUE(file_system_.Remove(wvcdm::test_vectors::kTestDir))
EXPECT_FALSE(file_system_.Exists(wvcdm::test_vectors::kTestDir)); << "path = " << wvcdm::test_vectors::kTestDir;
EXPECT_FALSE(file_system_.Exists(wvcdm::test_vectors::kTestDir))
<< "path = " << wvcdm::test_vectors::kTestDir;
} }
TEST_F(FileTest, OpenFile) { TEST_F(FileTest, OpenFile) {
std::string path = wvcdm::test_vectors::kTestDir + kTestFileName; const std::string path =
EXPECT_TRUE(file_system_.Remove(path)); PathJoin(wvcdm::test_vectors::kTestDir, kTestFilename);
EXPECT_TRUE(file_system_.Remove(path)) << "path = " << path;
std::unique_ptr<File> file = file_system_.Open(path, FileSystem::kCreate); std::unique_ptr<File> file = file_system_.Open(path, FileSystem::kCreate);
ASSERT_TRUE(file); ASSERT_TRUE(file) << "Failed to create file: " << path;
EXPECT_TRUE(file_system_.Exists(path)); EXPECT_TRUE(file_system_.Exists(path)) << "path = " << path;
} }
TEST_F(FileTest, RemoveDirAndFile) { TEST_F(FileTest, RemoveDirAndFile) {
std::string path = wvcdm::test_vectors::kTestDir + kTestFileName; const std::string path =
PathJoin(wvcdm::test_vectors::kTestDir, kTestFilename);
std::unique_ptr<File> file = file_system_.Open(path, FileSystem::kCreate); std::unique_ptr<File> file = file_system_.Open(path, FileSystem::kCreate);
ASSERT_TRUE(file); ASSERT_TRUE(file) << "Failed to create file: " << path;
EXPECT_TRUE(file_system_.Exists(path)); EXPECT_TRUE(file_system_.Exists(path)) << "path = " << path;
EXPECT_TRUE(file_system_.Remove(path)); EXPECT_TRUE(file_system_.Remove(path)) << "path = " << path;
EXPECT_FALSE(file_system_.Exists(path)); EXPECT_FALSE(file_system_.Exists(path)) << "path = " << path;
file = file_system_.Open(path, FileSystem::kCreate); file = file_system_.Open(path, FileSystem::kCreate);
ASSERT_TRUE(file); ASSERT_TRUE(file) << "Failed to create file: " << path;
EXPECT_TRUE(file_system_.Exists(path)); EXPECT_TRUE(file_system_.Exists(path)) << "path = " << path;
RemoveTestDir(); ASSERT_NO_FATAL_FAILURE(RemoveTestDir());
EXPECT_FALSE(file_system_.Exists(wvcdm::test_vectors::kTestDir)); EXPECT_FALSE(file_system_.Exists(wvcdm::test_vectors::kTestDir))
EXPECT_FALSE(file_system_.Exists(path)); << "path = " << path;
EXPECT_FALSE(file_system_.Exists(path)) << "path = " << path;
} }
TEST_F(FileTest, RemoveWildcardFiles) { TEST_F(FileTest, RemoveWildcardFiles) {
std::string path1 = wvcdm::test_vectors::kTestDir + kTestFileName; const std::string path1 =
std::string path2 = wvcdm::test_vectors::kTestDir + kTestFileName2; PathJoin(wvcdm::test_vectors::kTestDir, "first.txt");
std::string wildcard_path = const std::string path2 =
wvcdm::test_vectors::kTestDir + kWildcard + kTestFileNameExt; PathJoin(wvcdm::test_vectors::kTestDir, "second.txt");
const std::string wildcard_path =
PathJoin(wvcdm::test_vectors::kTestDir, "*.txt");
std::unique_ptr<File> file = file_system_.Open(path1, FileSystem::kCreate); std::unique_ptr<File> file = file_system_.Open(path1, FileSystem::kCreate);
ASSERT_TRUE(file); ASSERT_TRUE(file) << "Failed to create file (1): " << path1;
file = file_system_.Open(path2, FileSystem::kCreate); file = file_system_.Open(path2, FileSystem::kCreate);
ASSERT_TRUE(file); ASSERT_TRUE(file) << "Failed to create file (2): " << path2;
EXPECT_TRUE(file_system_.Exists(path1)); EXPECT_TRUE(file_system_.Exists(path1)) << "path = " << path1;
EXPECT_TRUE(file_system_.Exists(path2)); EXPECT_TRUE(file_system_.Exists(path2)) << "path = " << path2;
EXPECT_TRUE(file_system_.Remove(wildcard_path)); EXPECT_TRUE(file_system_.Remove(wildcard_path))
EXPECT_FALSE(file_system_.Exists(path1)); << "wildcard_path = " << wildcard_path;
EXPECT_FALSE(file_system_.Exists(path2)); EXPECT_FALSE(file_system_.Exists(path1)) << "path = " << path1;
EXPECT_FALSE(file_system_.Exists(path2)) << "path = " << path2;
} }
TEST_F(FileTest, FileSize) { TEST_F(FileTest, FileSize) {
std::string path = wvcdm::test_vectors::kTestDir + kTestFileName; const std::string path =
PathJoin(wvcdm::test_vectors::kTestDir, kTestFilename);
file_system_.Remove(path); file_system_.Remove(path);
std::string write_data = CdmRandom::RandomData(600); constexpr size_t kDataSize = 600;
size_t write_data_size = write_data.size(); const std::string write_data = CdmRandom::RandomData(kDataSize);
std::unique_ptr<File> file = file_system_.Open(path, FileSystem::kCreate); ASSERT_EQ(write_data.size(), kDataSize);
ASSERT_TRUE(file);
EXPECT_EQ(file->Write(write_data.data(), write_data_size), write_data_size);
EXPECT_TRUE(file_system_.Exists(path));
EXPECT_EQ(static_cast<ssize_t>(write_data_size), file_system_.FileSize(path)); std::unique_ptr<File> file = file_system_.Open(path, FileSystem::kCreate);
ASSERT_TRUE(file) << "Failed to create file: " << path;
EXPECT_EQ(file->Write(write_data.data(), write_data.size()),
write_data.size());
file.reset(); // Close file.
EXPECT_TRUE(file_system_.Exists(path)) << "path = " << path;
EXPECT_EQ(static_cast<ssize_t>(kDataSize), file_system_.FileSize(path))
<< "path = " << path;
} }
TEST_F(FileTest, WriteReadBinaryFile) { TEST_F(FileTest, WriteReadBinaryFile) {
std::string path = wvcdm::test_vectors::kTestDir + kTestFileName; const std::string path =
PathJoin(wvcdm::test_vectors::kTestDir, kTestFilename);
file_system_.Remove(path); file_system_.Remove(path);
std::string write_data = CdmRandom::RandomData(600); constexpr size_t kDataSize = 600;
size_t write_data_size = write_data.size(); const std::string write_data = CdmRandom::RandomData(kDataSize);
std::unique_ptr<File> file = file_system_.Open(path, FileSystem::kCreate); ASSERT_EQ(write_data.size(), kDataSize);
ASSERT_TRUE(file);
EXPECT_EQ(file->Write(write_data.data(), write_data_size), write_data_size); std::unique_ptr<File> file = file_system_.Open(path, FileSystem::kCreate);
EXPECT_TRUE(file_system_.Exists(path)); ASSERT_TRUE(file) << "Failed to create file: " << path;
constexpr ssize_t kExpectedFileSizeResult = static_cast<ssize_t>(kDataSize);
EXPECT_EQ(file->Write(write_data.data(), write_data.size()),
kExpectedFileSizeResult)
<< "path = " << path;
file.reset(); // Close file.
EXPECT_TRUE(file_system_.Exists(path)) << "path = " << path;
std::string read_data;
read_data.resize(file_system_.FileSize(path));
size_t read_data_size = read_data.size();
file = file_system_.Open(path, FileSystem::kReadOnly); file = file_system_.Open(path, FileSystem::kReadOnly);
ASSERT_TRUE(file); ASSERT_TRUE(file) << "Failed to re-open file: " << path;
EXPECT_EQ(file->Read(&read_data[0], read_data_size), read_data_size);
std::string read_data(kDataSize, '\0');
;
ASSERT_EQ(file->Read(&read_data[0], read_data.size()),
kExpectedFileSizeResult)
<< "path = " << path;
EXPECT_EQ(write_data, read_data); EXPECT_EQ(write_data, read_data);
} }
TEST_F(FileTest, ListFiles) { TEST_F(FileTest, ListFiles) {
std::vector<std::string> names; const std::string kTxtFilename1 = "data.txt";
const std::string kTxtFilename2 = "other.txt";
const std::string kBinFilename = "sample.bin";
std::string not_path("zzz"); const std::string dir_path = wvcdm::test_vectors::kTestDir;
std::string path1 = wvcdm::test_vectors::kTestDir + kTestFileName; const std::string path1 = PathJoin(dir_path, kTxtFilename1);
std::string path2 = wvcdm::test_vectors::kTestDir + kTestFileName2; const std::string path2 = PathJoin(dir_path, kTxtFilename2);
std::string path3 = wvcdm::test_vectors::kTestDir + kTestFileName3; const std::string path3 = PathJoin(dir_path, kBinFilename);
std::string path_dir = wvcdm::test_vectors::kTestDir;
// Create files.
std::unique_ptr<File> file = file_system_.Open(path1, FileSystem::kCreate); std::unique_ptr<File> file = file_system_.Open(path1, FileSystem::kCreate);
ASSERT_TRUE(file); ASSERT_TRUE(file) << "Failed to create file (1): " << path1;
file = file_system_.Open(path2, FileSystem::kCreate); file = file_system_.Open(path2, FileSystem::kCreate);
ASSERT_TRUE(file); ASSERT_TRUE(file) << "Failed to create file (2): " << path2;
file = file_system_.Open(path3, FileSystem::kCreate); file = file_system_.Open(path3, FileSystem::kCreate);
ASSERT_TRUE(file); ASSERT_TRUE(file) << "Failed to create file (3): " << path3;
file.reset(); // Close file
EXPECT_TRUE(file_system_.Exists(path1)); EXPECT_TRUE(file_system_.Exists(path1));
EXPECT_TRUE(file_system_.Exists(path2)); EXPECT_TRUE(file_system_.Exists(path2));
EXPECT_TRUE(file_system_.Exists(path3)); EXPECT_TRUE(file_system_.Exists(path3));
// Ask for non-existent path. std::vector<std::string> names;
EXPECT_FALSE(file_system_.List(not_path, &names)); ASSERT_TRUE(file_system_.List(dir_path, &names)) << "dir_path = " << dir_path;
// Valid path, but no way to return names. size_t expected_file_count = 3;
EXPECT_FALSE(file_system_.List(path_dir, nullptr)); EXPECT_EQ(names.size(), expected_file_count);
// Valid path, valid return. // Should find the three files. Order not important.
EXPECT_TRUE(file_system_.List(path_dir, &names));
// Should find three files. Order not important.
EXPECT_EQ(3u, names.size());
EXPECT_THAT(names, ::testing::UnorderedElementsAre( EXPECT_THAT(names, ::testing::UnorderedElementsAre(
kTestFileName, kTestFileName2, kTestFileName3)); kTxtFilename1, kTxtFilename2, kBinFilename));
std::string wild_card_path = path_dir + kWildcard + kTestFileNameExt; // Remove .txt files.
EXPECT_TRUE(file_system_.Remove(wild_card_path)); const std::string txt_wildcard_path = PathJoin(dir_path, "*.txt");
EXPECT_TRUE(file_system_.List(path_dir, &names));
EXPECT_EQ(1u, names.size()); EXPECT_TRUE(file_system_.Remove(txt_wildcard_path))
EXPECT_TRUE(names[0].compare(kTestFileName3) == 0); << "txt_wildcard_path = " << txt_wildcard_path;
EXPECT_TRUE(file_system_.List(dir_path, &names)) << "dir_path = " << dir_path;
std::string wild_card_path2 = path_dir + kWildcard + kTestFileNameExt3; expected_file_count = 1;
EXPECT_TRUE(file_system_.Remove(wild_card_path2)); ASSERT_EQ(names.size(), expected_file_count);
EXPECT_TRUE(file_system_.List(path_dir, &names)); EXPECT_EQ(names.front(), kBinFilename);
EXPECT_EQ(0u, names.size()); const std::string bin_wildcard_path = PathJoin(dir_path, "*.bin");
EXPECT_TRUE(file_system_.Remove(bin_wildcard_path))
<< "bin_wildcard_path = " << bin_wildcard_path;
// All files should be removed, but listing should still succeed.
EXPECT_TRUE(file_system_.List(dir_path, &names)) << "dir_path = " << dir_path;
expected_file_count = 0;
EXPECT_EQ(expected_file_count, names.size());
} }
TEST_F(FileTest, CreateGlobalCertificates) { TEST_F(FileTest, ListFiles_NotAPath) {
// Clear directory const std::string not_path("zzz/xxx");
std::vector<std::string> names; std::vector<std::string> names;
std::string path_dir = wvcdm::test_vectors::kTestDir; // Ask for non-existent path.
std::string wild_card_path = path_dir + kWildcard; EXPECT_FALSE(file_system_.List(not_path, &names));
file_system_.Remove(wild_card_path); }
if (file_system_.List(path_dir, &names)) {
EXPECT_EQ(0u, names.size()); TEST_F(FileTest, ListFiles_NullParameter) {
const std::string dir_path = wvcdm::test_vectors::kTestDir;
const std::string path = PathJoin(dir_path, kTestFilename);
std::unique_ptr<File> file = file_system_.Open(path, FileSystem::kCreate);
ASSERT_TRUE(file) << "Failed to create file: " << path;
file.reset(); // Close file.
// Valid path, but no way to return names.
EXPECT_FALSE(file_system_.List(dir_path, nullptr));
}
// On certain platforms, the FileSystem may perform special
// name translations on certificate filenames which make them behave
// differently from non-certificate filenames.
TEST_F(FileTest, CreateGlobalCertificates) {
ASSERT_TRUE(file_system_.IsGlobal())
<< "Test case requires global file system";
const std::string dir_path = wvcdm::test_vectors::kTestDir;
// Clear directory
const std::string all_file_wildcard_path = PathJoin(dir_path, "*");
file_system_.Remove(all_file_wildcard_path);
// Ensure directory is empty.
std::vector<std::string> names;
if (file_system_.List(dir_path, &names)) {
constexpr size_t kZero = 0;
ASSERT_EQ(kZero, names.size()) << "Test requires empty directory";
} }
// Create certificates and verify that they exist const std::string certificate_path = PathJoin(dir_path, kCertificateFileName);
std::string certificate_path = const std::string legacy_certificate_path =
wvcdm::test_vectors::kTestDir + kCertificateFileName; PathJoin(dir_path, kLegacyCertificateFileName);
std::string legacy_certificate_path =
wvcdm::test_vectors::kTestDir + kLegacyCertificateFileName;
// Create certificates and verify that they exist
std::unique_ptr<File> file = std::unique_ptr<File> file =
file_system_.Open(certificate_path, FileSystem::kCreate); file_system_.Open(certificate_path, FileSystem::kCreate);
ASSERT_TRUE(file); ASSERT_TRUE(file) << "Failed to create certificate file: "
<< certificate_path;
file = file_system_.Open(legacy_certificate_path, FileSystem::kCreate); file = file_system_.Open(legacy_certificate_path, FileSystem::kCreate);
ASSERT_TRUE(file); ASSERT_TRUE(file) << "Failed to create legacy certificate file: "
EXPECT_TRUE(file_system_.IsGlobal()); << legacy_certificate_path;
file.reset(); // Close file.
EXPECT_TRUE(file_system_.Exists(certificate_path)); EXPECT_TRUE(file_system_.Exists(certificate_path))
EXPECT_TRUE(file_system_.Exists(legacy_certificate_path)); << "certificate_path = " << certificate_path;
EXPECT_TRUE(file_system_.Exists(legacy_certificate_path))
<< "legacy_certificate_path = " << legacy_certificate_path;
EXPECT_TRUE(file_system_.List(path_dir, &names)); ASSERT_TRUE(file_system_.List(dir_path, &names));
// Should find two files. Order not important. // Should find two files. Order not important.
EXPECT_EQ(2u, names.size()); constexpr size_t kExpectedCount = 2;
EXPECT_EQ(kExpectedCount, names.size());
EXPECT_THAT(names, ::testing::UnorderedElementsAre( EXPECT_THAT(names, ::testing::UnorderedElementsAre(
kCertificateFileName, kLegacyCertificateFileName)); kCertificateFileName, kLegacyCertificateFileName));
} }
// On certain platforms, the FileSystem may perform special
// name translations on certificate filenames which make them behave
// differently from non-certificate filenames.
TEST_F(FileTest, CreateCertificates) { TEST_F(FileTest, CreateCertificates) {
ASSERT_TRUE(file_system_.IsGlobal())
<< "Test case requires starting with a global file system";
const std::string dir_path = wvcdm::test_vectors::kTestDir;
// Clear directory // Clear directory
const std::string all_file_wildcard_path = PathJoin(dir_path, "*");
file_system_.Remove(all_file_wildcard_path);
// Ensure directory is empty.
std::vector<std::string> names; std::vector<std::string> names;
std::string path_dir = wvcdm::test_vectors::kTestDir; if (file_system_.List(dir_path, &names)) {
std::string wild_card_path = path_dir + kWildcard; constexpr size_t kZero = 0;
file_system_.Remove(wild_card_path); ASSERT_EQ(kZero, names.size()) << "Test requires empty directory";
if (file_system_.List(path_dir, &names)) {
EXPECT_EQ(0u, names.size());
} }
std::string certificate_path = const std::string certificate_path = PathJoin(dir_path, kCertificateFileName);
wvcdm::test_vectors::kTestDir + kCertificateFileName; const std::string legacy_certificate_path =
std::string legacy_certificate_path = PathJoin(dir_path, kLegacyCertificateFileName);
wvcdm::test_vectors::kTestDir + kLegacyCertificateFileName;
// Create Global certificates // Create Global certificates.
std::unique_ptr<File> file = std::unique_ptr<File> file =
file_system_.Open(certificate_path, FileSystem::kCreate); file_system_.Open(certificate_path, FileSystem::kCreate);
ASSERT_TRUE(file); ASSERT_TRUE(file) << "Failed to create global certificate: "
<< certificate_path;
file = file_system_.Open(legacy_certificate_path, FileSystem::kCreate); file = file_system_.Open(legacy_certificate_path, FileSystem::kCreate);
ASSERT_TRUE(file); ASSERT_TRUE(file) << "Failed to create global legacy certificate: "
EXPECT_TRUE(file_system_.IsGlobal()); << legacy_certificate_path;
file.reset(); // Close file.
EXPECT_TRUE(file_system_.Exists(certificate_path))
<< "Global certificate: " << certificate_path;
EXPECT_TRUE(file_system_.Exists(legacy_certificate_path))
<< "Global legacy certificate: " << legacy_certificate_path;
// Switch to first identifier.
file_system_.set_identifier(kTestIdentifier1);
ASSERT_FALSE(file_system_.IsGlobal()) << "identifier = " << kTestIdentifier1;
// Global certificates should not be visible once identifier has been
// specified.
EXPECT_FALSE(file_system_.Exists(certificate_path))
<< kTestIdentifier1 << " certificate: " << certificate_path;
EXPECT_FALSE(file_system_.Exists(legacy_certificate_path))
<< kTestIdentifier1 << " legacy certificate: " << legacy_certificate_path;
// Create certificates with first identifier // Create certificates with first identifier
file_system_.set_identifier(kTestIdentifier1);
file = file_system_.Open(certificate_path, FileSystem::kCreate); file = file_system_.Open(certificate_path, FileSystem::kCreate);
ASSERT_TRUE(file); ASSERT_TRUE(file) << "Failed to create " << kTestIdentifier1
<< " certificate: " << certificate_path;
file = file_system_.Open(legacy_certificate_path, FileSystem::kCreate); file = file_system_.Open(legacy_certificate_path, FileSystem::kCreate);
ASSERT_TRUE(file); ASSERT_TRUE(file) << "Failed to create " << kTestIdentifier1
EXPECT_TRUE(!file_system_.IsGlobal()); << " legacy certificate: " << legacy_certificate_path;
file.reset(); // Close file.
// Verify they now exist.
EXPECT_TRUE(file_system_.Exists(certificate_path))
<< kTestIdentifier1 << " certificate: " << certificate_path;
EXPECT_TRUE(file_system_.Exists(legacy_certificate_path))
<< kTestIdentifier1 << " legacy certificate: " << legacy_certificate_path;
// Switch to second identifier.
file_system_.set_identifier(kTestIdentifier2);
ASSERT_FALSE(file_system_.IsGlobal()) << "identifier = " << kTestIdentifier2;
// Global and first identifier certificates should not be
// visible.
EXPECT_FALSE(file_system_.Exists(certificate_path))
<< kTestIdentifier2 << " certificate: " << certificate_path;
EXPECT_FALSE(file_system_.Exists(legacy_certificate_path))
<< kTestIdentifier2 << " legacy certificate: " << legacy_certificate_path;
// Create certificates with second identifier // Create certificates with second identifier
file_system_.set_identifier(kTestIdentifier2);
file = file_system_.Open(certificate_path, FileSystem::kCreate); file = file_system_.Open(certificate_path, FileSystem::kCreate);
ASSERT_TRUE(file); ASSERT_TRUE(file) << "Failed to create " << kTestIdentifier2
<< " certificate: " << certificate_path;
file = file_system_.Open(legacy_certificate_path, FileSystem::kCreate); file = file_system_.Open(legacy_certificate_path, FileSystem::kCreate);
ASSERT_TRUE(file); ASSERT_TRUE(file) << "Failed to create " << kTestIdentifier2
EXPECT_TRUE(!file_system_.IsGlobal()); << " legacy certificate: " << legacy_certificate_path;
file.reset(); // Close file.
EXPECT_TRUE(file_system_.Exists(certificate_path)); // Verify they now exist.
EXPECT_TRUE(file_system_.Exists(legacy_certificate_path)); EXPECT_TRUE(file_system_.Exists(certificate_path))
<< kTestIdentifier2 << " certificate: " << certificate_path;
EXPECT_TRUE(file_system_.Exists(legacy_certificate_path))
<< kTestIdentifier2 << " legacy certificate: " << legacy_certificate_path;
EXPECT_TRUE(file_system_.List(path_dir, &names)); // FileSystem::List is expected to still return all certificate files
// (both global and scoped).
ASSERT_TRUE(file_system_.List(dir_path, &names)) << "dir_path = " << dir_path;
// Should find six files. Order not important. // Should find six files. Order not important.
constexpr size_t kExpectedTotalCertCount = 6;
ASSERT_EQ(names.size(), kExpectedTotalCertCount);
bool is_global_certificate_present = false; bool is_global_certificate_present = false;
bool is_global_legacy_certificate_present = false; bool is_global_legacy_certificate_present = false;
size_t certificate_count = 0; size_t certificate_count = 0;
size_t legacy_certificate_count = 0; size_t legacy_certificate_count = 0;
EXPECT_EQ(6u, names.size()); for (const auto& filename : names) {
for (size_t i = 0; i < names.size(); ++i) { if (filename == kLegacyCertificateFileName) {
if (names[i].size() > kCertificateFileName.size()) {
if (names[i].compare(0, kCertificateFileNamePrefix.size(),
kCertificateFileNamePrefix) == 0)
++certificate_count;
else if (names[i].compare(0, kLegacyCertificateFileNamePrefix.size(),
kLegacyCertificateFileNamePrefix) == 0)
++legacy_certificate_count;
} else if (names[i].compare(kCertificateFileName) == 0) {
is_global_certificate_present = true;
} else if (names[i].compare(kLegacyCertificateFileName) == 0) {
is_global_legacy_certificate_present = true; is_global_legacy_certificate_present = true;
} else if (filename == kCertificateFileName) {
is_global_certificate_present = true;
} else if (StartsWith(filename, kScopedCertificateFilenamePrefix)) {
certificate_count++;
} else if (StartsWith(filename, kLegacyScopedCertificateFilenamePrefix)) {
legacy_certificate_count++;
} else { } else {
EXPECT_TRUE(false); ADD_FAILURE() << "Unexpected filename: " << filename;
} }
} }
EXPECT_EQ(2, certificate_count); constexpr size_t kExpectedScopedCertCount = 2;
EXPECT_EQ(2, legacy_certificate_count); EXPECT_EQ(certificate_count, kExpectedScopedCertCount)
EXPECT_TRUE(is_global_certificate_present); << "Missing certificates";
EXPECT_TRUE(is_global_legacy_certificate_present); EXPECT_EQ(legacy_certificate_count, kExpectedScopedCertCount)
<< "Missing legacy certificates";
EXPECT_TRUE(is_global_certificate_present)
<< "Missing global certificate: " << kCertificateFileName;
EXPECT_TRUE(is_global_legacy_certificate_present)
<< "Missing legacy global certificate: " << kLegacyCertificateFileName;
} }
// On certain platforms, the FileSystem may perform special
// name translations on certificate file names which make them behave
// differently from non-certificate file names.
TEST_F(FileTest, RemoveCertificates) { TEST_F(FileTest, RemoveCertificates) {
ASSERT_TRUE(file_system_.IsGlobal())
<< "Test case requires starting with a global file system";
const std::string dir_path = wvcdm::test_vectors::kTestDir;
// Clear directory // Clear directory
const std::string all_file_wildcard_path = PathJoin(dir_path, "*");
file_system_.Remove(all_file_wildcard_path);
// Ensure directory is empty.
std::vector<std::string> names; std::vector<std::string> names;
std::string path_dir = wvcdm::test_vectors::kTestDir; if (file_system_.List(dir_path, &names)) {
std::string wild_card_path = path_dir + kWildcard; constexpr size_t kZero = 0;
file_system_.Remove(wild_card_path); ASSERT_EQ(kZero, names.size()) << "Test requires empty directory";
if (file_system_.List(path_dir, &names)) {
EXPECT_EQ(0u, names.size());
} }
std::string certificate_path = const std::string certificate_path = PathJoin(dir_path, kCertificateFileName);
wvcdm::test_vectors::kTestDir + kCertificateFileName; const std::string legacy_certificate_path =
std::string legacy_certificate_path = PathJoin(dir_path, kLegacyCertificateFileName);
wvcdm::test_vectors::kTestDir + kLegacyCertificateFileName;
// Create Global certificates // Create Global certificates.
std::unique_ptr<File> file = std::unique_ptr<File> file =
file_system_.Open(certificate_path, FileSystem::kCreate); file_system_.Open(certificate_path, FileSystem::kCreate);
ASSERT_TRUE(file); ASSERT_TRUE(file) << "Failed to create global certificate: "
<< certificate_path;
file = file_system_.Open(legacy_certificate_path, FileSystem::kCreate); file = file_system_.Open(legacy_certificate_path, FileSystem::kCreate);
ASSERT_TRUE(file); ASSERT_TRUE(file) << "Failed to create global legacy certificate: "
EXPECT_TRUE(file_system_.IsGlobal()); << legacy_certificate_path;
file.reset(); // Close file.
// Switch to first identifier.
file_system_.set_identifier(kTestIdentifier1);
ASSERT_FALSE(file_system_.IsGlobal()) << "identifier = " << kTestIdentifier1;
// Create certificates with first identifier // Create certificates with first identifier
file_system_.set_identifier(kTestIdentifier1);
file = file_system_.Open(certificate_path, FileSystem::kCreate); file = file_system_.Open(certificate_path, FileSystem::kCreate);
ASSERT_TRUE(file); ASSERT_TRUE(file) << "Failed to create " << kTestIdentifier1
<< " certificate: " << certificate_path;
file = file_system_.Open(legacy_certificate_path, FileSystem::kCreate); file = file_system_.Open(legacy_certificate_path, FileSystem::kCreate);
ASSERT_TRUE(file); ASSERT_TRUE(file) << "Failed to create " << kTestIdentifier1
EXPECT_TRUE(!file_system_.IsGlobal()); << " legacy certificate: " << legacy_certificate_path;
file.reset(); // Close file.
// Switch to second identifier.
file_system_.set_identifier(kTestIdentifier2);
ASSERT_FALSE(file_system_.IsGlobal()) << "identifier = " << kTestIdentifier2;
// Create certificates with second identifier // Create certificates with second identifier
file_system_.set_identifier(kTestIdentifier2);
file = file_system_.Open(certificate_path, FileSystem::kCreate); file = file_system_.Open(certificate_path, FileSystem::kCreate);
ASSERT_TRUE(file); ASSERT_TRUE(file) << "Failed to create " << kTestIdentifier2
<< " certificate: " << certificate_path;
file = file_system_.Open(legacy_certificate_path, FileSystem::kCreate); file = file_system_.Open(legacy_certificate_path, FileSystem::kCreate);
ASSERT_TRUE(file); ASSERT_TRUE(file) << "Failed to create " << kTestIdentifier2
EXPECT_TRUE(!file_system_.IsGlobal()); << " legacy certificate: " << legacy_certificate_path;
file.reset(); // Close file.
EXPECT_TRUE(file_system_.Exists(certificate_path)); // FileSystem::List is expected to still return all certificate files
EXPECT_TRUE(file_system_.Exists(legacy_certificate_path)); // (both global and scoped).
ASSERT_TRUE(file_system_.List(dir_path, &names)) << "dir_path = " << dir_path;
EXPECT_TRUE(file_system_.List(path_dir, &names)); // Should find six files. Order not important.
constexpr size_t kExpectedTotalCertCount = 6;
EXPECT_EQ(6u, names.size()); ASSERT_EQ(names.size(), kExpectedTotalCertCount);
std::set<std::string> removed_certs;
// Remove all even number listed files // Remove all even number listed files
for (size_t i = 0; i < names.size(); ++i) { for (size_t i = 0; i < names.size(); ++i) {
if (i % 2 == 0) { if ((i % 2) != 0) continue;
EXPECT_TRUE( const std::string& cert_filename = names[i];
file_system_.Remove(wvcdm::test_vectors::kTestDir + names[i])); const std::string cert_path = PathJoin(dir_path, cert_filename);
} ASSERT_TRUE(file_system_.Remove(cert_path))
<< "Failed to remove cert: " << cert_path;
removed_certs.insert(cert_filename);
} }
// Verify that they have been removed // Verify that they have been removed
for (size_t i = 0; i < names.size(); ++i) { for (const std::string& cert_filename : names) {
if (i % 2 == 1) { const std::string cert_path = PathJoin(dir_path, cert_filename);
EXPECT_TRUE( if (removed_certs.find(cert_filename) == removed_certs.end()) {
file_system_.Exists(wvcdm::test_vectors::kTestDir + names[i])); // Ensure still exists.
ASSERT_TRUE(file_system_.Exists(cert_path))
<< "Cert missing: " << cert_filename;
} else { } else {
EXPECT_FALSE( ASSERT_FALSE(file_system_.Exists(cert_path))
file_system_.Exists(wvcdm::test_vectors::kTestDir + names[i])); << "Cert not removed: " << cert_filename;
} }
} }
// Remove all odd number listed files // Remove all remaining.
for (size_t i = 0; i < names.size(); ++i) { for (const std::string& cert_filename : names) {
if (i % 2 == 1) { if (removed_certs.find(cert_filename) != removed_certs.end()) continue;
EXPECT_TRUE( const std::string cert_path = PathJoin(dir_path, cert_filename);
file_system_.Remove(wvcdm::test_vectors::kTestDir + names[i])); ASSERT_TRUE(file_system_.Remove(cert_path))
} << "Failed to remove cert: " << cert_path;
} }
// Verify that all have been removed // Verify that all have been removed
for (size_t i = 0; i < names.size(); ++i) { for (const std::string& cert_filename : names) {
EXPECT_FALSE(file_system_.Exists(wvcdm::test_vectors::kTestDir + names[i])); const std::string cert_path = PathJoin(dir_path, cert_filename);
EXPECT_FALSE(file_system_.Exists(cert_path))
<< "Cert not removed: " << cert_filename;
} }
} }
} // namespace wvutil } // namespace wvutil