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]
## [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]
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.6]: https://widevine-partner.googlesource.com/oemcrypto/+/refs/tags/v18.6
[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.
/**
* @mainpage OEMCrypto API v18.7
* @mainpage OEMCrypto API v18.8
*
* 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

View File

@@ -26,9 +26,9 @@ struct CoreMessageFeatures {
// 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
// number. The default is 18.7.
// number. The default is 18.8.
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 {

View File

@@ -16,10 +16,10 @@ extern "C" {
/* The version of this library. */
#define ODK_MAJOR_VERSION 18
#define ODK_MINOR_VERSION 7
#define ODK_MINOR_VERSION 8
/* 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. */
#define ODK_FIRST_VERSION 16

View File

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

View File

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

View File

@@ -1216,7 +1216,7 @@ std::vector<VersionParameters> TestCases() {
// number.
{16, ODK_MAJOR_VERSION, ODK_MINOR_VERSION, 16, 5},
{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.
{ODK_MAJOR_VERSION, 16, 3, 16, 3},
{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, 6, 18, 6},
{ODK_MAJOR_VERSION, 18, 7, 18, 7},
{ODK_MAJOR_VERSION, 18, 8, 18, 8},
{0, 16, 3, 16, 3},
{0, 16, 4, 16, 4},
{0, 16, 5, 16, 5},
@@ -1239,6 +1240,7 @@ std::vector<VersionParameters> TestCases() {
{0, 18, 5, 18, 5},
{0, 18, 6, 18, 6},
{0, 18, 7, 18, 7},
{0, 18, 8, 18, 8},
};
return test_cases;
}

View File

@@ -216,6 +216,7 @@ oemcrypto_unittests_sources += \
$(oemcrypto_unittests_dir)/oemcrypto_generic_crypto_test.cpp \
$(oemcrypto_unittests_dir)/oemcrypto_license_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_test.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);
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
* signature size. */
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);
if (result != OEMCrypto_SUCCESS) {
LOGE(
@@ -380,7 +394,7 @@ static OEMCryptoResult RewrapDeviceDRMKeyOEMCert(
/* Check that it's a valid DRM key. */
result =
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) {
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
// v17.1.0
#define API_MAJOR_VERSION 18
#define API_MINOR_VERSION 7
#define API_MINOR_VERSION 8
#define OPK_PATCH_VERSION 0
#endif /* OEMCRYPTO_TA_OEMCRYPTO_API_MACROS_H_ */

View File

@@ -13,6 +13,7 @@
# production use.
'wtpi_stub_dir': '<(oemcrypto_ta_dir)/wtpi_useless',
'use_provisioning_40%': 0,
'use_random_device_key%': 0,
},
'target_defaults': {
'toolsets' : [ 'target' ],
@@ -31,7 +32,6 @@
'test-only/wtpi_persistent_storage.c',
'test-only/file_store_interface.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_secure_buffer_access.c',
'<(wtpi_stub_dir)/wtpi_fused.c',
@@ -49,6 +49,11 @@
'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': [
'<(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
cd $TRUSTY_DIR/prebuilts
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
# 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
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
"$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): LIBOEMCRYPTO_BUILD_DIR := $(LIBOEMCRYPTO_BUILD_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):
$(NOECHO)$(NDK_ROOT)/ndk-build \
-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) {
TEE_SharedMemory_Bind(NULL, 0);
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_size = 0;
}
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_size = 0;
}
@@ -310,7 +316,10 @@ static int dispatch_bind_message(handle_t chan,
PROT_READ | PROT_WRITE, 0, handles[1], 0);
if (shared_buffer_addr == MAP_FAILED) {
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;
}

View File

@@ -22,6 +22,7 @@ LOCAL_SRC_FILES:= \
oemcrypto_generic_crypto_test.cpp \
oemcrypto_license_test.cpp \
oemcrypto_provisioning_test.cpp \
oemcrypto_security_test.cpp \
oemcrypto_usage_table_test.cpp \
oemcrypto_test.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) {
case OEMCrypto_BufferType_Clear:
dest_buffer->buffer.clear.clear_buffer += bytes;
dest_buffer->buffer.clear.clear_buffer_length -= bytes;
break;
case OEMCrypto_BufferType_Secure:
@@ -98,11 +99,6 @@ OEMCryptoResult DecryptFallbackChain::DecryptSample(
const size_t length =
subsample.num_bytes_clear + subsample.num_bytes_encrypted;
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_length = 1;
@@ -148,11 +144,6 @@ OEMCryptoResult DecryptFallbackChain::DecryptSubsample(
if (subsample.num_bytes_clear > 0) {
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_encrypted = 0;
fake_subsample.block_offset = 0;
@@ -176,11 +167,6 @@ OEMCryptoResult DecryptFallbackChain::DecryptSubsample(
if (subsample.num_bytes_encrypted > 0) {
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_encrypted = subsample.num_bytes_encrypted;
fake_subsample.block_offset = subsample.block_offset;

View File

@@ -73,6 +73,76 @@ int32_t JsmnAncestorCount(const std::vector<jsmntok_t>& tokens,
}
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
void OEMCryptoClientTest::SetUp() {
@@ -148,6 +218,11 @@ OEMCryptoResult OEMCryptoClientTest::CopyBuffer(
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) {
switch (value) {
case HDCP_NONE:
@@ -174,9 +249,9 @@ const char* HDCPCapabilityAsString(OEMCrypto_HDCP_Capability value) {
return "HDCP version 2.3";
case HDCP_NO_DIGITAL_OUTPUT:
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,
@@ -218,7 +293,7 @@ TEST_F(OEMCryptoClientTest, FreeUnallocatedSecureBufferNoFailure) {
*/
TEST_F(OEMCryptoClientTest, VersionNumber) {
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 << " "
<< "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
// above.
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));
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();
EXPECT_GT(level, OEMCrypto_Level_Unknown);
EXPECT_LE(level, OEMCrypto_Level3);
cout << " OEMCrypto Security Level is L" << level << endl;
RecordWvProperty("security_level", SecurityLevelName(level));
uint32_t version = OEMCrypto_APIVersion();
uint32_t minor_version = OEMCrypto_MinorAPIVersion();
cout << " OEMCrypto API version is " << version << "."
<< 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;
} else {
cout << " OEMCrypto does not support usage tables" << endl;
}
RecordWvProperty("supports_usage_tables", BoolName(supports_usage_tables));
if (version >= 15) {
const uint32_t tier = OEMCrypto_ResourceRatingTier();
cout << " Resource Rating Tier: " << tier << endl;
RecordWvProperty("resource_rating_tier", std::to_string(tier));
}
if (version >= 17) {
OEMCryptoResult sts = OEMCrypto_ProductionReady();
if (sts != OEMCrypto_SUCCESS) {
LOGW("Device is not production ready, returns %d", sts);
}
RecordWvProperty("is_production_ready", BoolName(sts == OEMCrypto_SUCCESS));
std::string build_info;
size_t buf_length = 0;
sts = OEMCrypto_BuildInformation(&build_info[0], &buf_length);
@@ -262,6 +352,7 @@ TEST_F(OEMCryptoClientTest, VersionNumber) {
if (build_info.size() != buf_length) {
build_info.resize(buf_length);
}
RecordWvProperty("build_information", build_info);
const std::string comma = ",";
const std::string pretty_comma = ",\n ";
std::string::size_type pos = 0;
@@ -270,9 +361,12 @@ TEST_F(OEMCryptoClientTest, VersionNumber) {
pos += pretty_comma.size();
}
cout << " BuildInformation: " << build_info << endl;
OEMCrypto_WatermarkingSupport support = OEMCrypto_GetWatermarkingSupport();
cout << " WatermarkingSupport: " << support << endl;
RecordWvProperty("watermarking_support", WatermarkingSupportName(support));
}
ASSERT_GE(version, 8u);
ASSERT_LE(version, kCurrentAPI);
}
@@ -292,8 +386,9 @@ TEST_F(OEMCryptoClientTest, ResourceRatingAPI15) {
TEST_F(OEMCryptoClientTest, ProvisioningDeclaredAPI12) {
OEMCrypto_ProvisioningMethod provisioning_method =
OEMCrypto_GetProvisioningMethod();
cout << " Provisioning method = "
<< ProvisioningMethodName(provisioning_method) << endl;
const char* const name = ProvisioningMethodName(provisioning_method);
cout << " Provisioning method = " << name << endl;
RecordWvProperty("provisioning_method", name);
ASSERT_NE(OEMCrypto_ProvisioningError, provisioning_method);
}
@@ -306,6 +401,8 @@ TEST_F(OEMCryptoClientTest, CheckHDCPCapabilityAPI09) {
static_cast<unsigned int>(current), HDCPCapabilityAsString(current));
printf(" Maximum HDCP Capability: 0x%02x = %s.\n",
static_cast<unsigned int>(maximum), HDCPCapabilityAsString(maximum));
RecordWvProperty("hdcp_current", HDCPCapabilityName(current));
RecordWvProperty("hdcp_max", HDCPCapabilityName(maximum));
}
TEST_F(OEMCryptoClientTest, CheckSRMCapabilityV13) {
@@ -314,11 +411,15 @@ TEST_F(OEMCryptoClientTest, CheckSRMCapabilityV13) {
OEMCryptoResult current_result = OEMCrypto_GetCurrentSRMVersion(&version);
if (current_result == OEMCrypto_SUCCESS) {
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));
} else if (current_result == OEMCrypto_LOCAL_DISPLAY_ONLY) {
printf(" Current SRM Status: Local Display Only.\n");
RecordWvProperty("srm_supported", BoolName(false));
} else {
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},
};
// 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
// build information, use to verify all fields are present.
std::set<std::string> found_required_fields;
@@ -573,11 +677,17 @@ TEST_F(OEMCryptoClientTest, CheckJsonBuildInformationAPI18) {
<< "Unexpected required field type: field = " << key
<< ", build_info = " << build_info;
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()) {
ASSERT_EQ(value_token.type, kOptionalFields.at(key))
<< "Unexpected optional field type: field = " << key
<< ", 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) {
// 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 (!has_ree_info) return;
// 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())
<< "REE field was specified, but contents were empty: build_info = "
<< build_info;
@@ -647,7 +762,10 @@ TEST_F(OEMCryptoClientTest, CheckJsonBuildInformationAPI18) {
<< "Unexpected optional REE field type: ree_field = " << key
<< ", build_info = " << build_info;
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.
i += JsmnAncestorCount(ree_tokens, i + 1);
@@ -683,6 +801,7 @@ TEST_F(OEMCryptoClientTest, CheckMaxNumberOfSessionsAPI10) {
OEMCryptoResult sts = OEMCrypto_GetMaxNumberOfSessions(&maximum);
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
printf(" Max Number of Sessions: %zu.\n", maximum);
RecordWvProperty("max_number_of_sessions", std::to_string(maximum));
size_t required_max = GetResourceValue(kMaxConcurrentSession);
ASSERT_GE(maximum, required_max);
}
@@ -690,6 +809,7 @@ TEST_F(OEMCryptoClientTest, CheckMaxNumberOfSessionsAPI10) {
TEST_F(OEMCryptoClientTest, CheckUsageTableSizeAPI16) {
const size_t maximum = OEMCrypto_MaximumUsageTableHeaderSize();
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.
if (maximum > 0) {
ASSERT_GE(maximum, RequiredUsageSize());
@@ -723,6 +843,7 @@ TEST_F(OEMCryptoClientTest, CheckDTCP2CapabilityAPI17) {
"DTCP2 is supported.\n");
break;
}
RecordWvProperty("dtcp2_capability", DTCP2CapabiityName(capability));
}
//

View File

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

View File

@@ -5,12 +5,13 @@
#include "oemcrypto_cast_test.h"
#include "oemcrypto_usage_table_test.h"
using ::testing::Range;
namespace wvoec {
/// @addtogroup cast
/// @{
/** If a device can load a private key with the alternate padding schemes, it
* should support signing with the alternate scheme. */
TEST_F(OEMCryptoLoadsCertificateAlternates, TestSignaturePKCS1) {
@@ -262,18 +263,8 @@ class OEMCryptoCastReceiverTest : public OEMCryptoLoadsCertificateAlternates {
ASSERT_NO_FATAL_FAILURE(s.open());
ASSERT_NO_FATAL_FAILURE(s.LoadWrappedRsaDrmKey(wrapped_drm_key_));
// 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 ...
vector<uint8_t> digest = wvutil::a2b_hex("3021300906052b0e03021a05000414");
digest.insert(digest.end(), hash, hash + SHA_DIGEST_LENGTH);
vector<uint8_t> digest;
ASSERT_NO_FATAL_FAILURE(PrepareCastDigestedMessage(message, digest));
// OEMCrypto will apply the padding, and encrypt to generate the
// signature.
@@ -1018,4 +1009,6 @@ TEST_P(OEMCryptoSessionTestLoadCasKeysWithHDCP, CasOnlyLoadCasKeysAPI17) {
}
INSTANTIATE_TEST_SUITE_P(TestHDCP, OEMCryptoSessionTestLoadCasKeysWithHDCP,
Range(1, 6));
/// @}
} // namespace wvoec

View File

@@ -14,6 +14,7 @@
#include "OEMCryptoCENC.h"
#include "oemcrypto_provisioning_test.h"
#include "oemcrypto_session_tests_helper.h"
#include "oemcrypto_usage_table_test.h"
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.
class OEMCryptoLoadsCertificateAlternates : public OEMCryptoLoadsCertificate {
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) {
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
@@ -32,16 +52,19 @@ class OEMCryptoLoadsCertificateAlternates : public OEMCryptoLoadsCertificate {
vector<uint8_t> 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;
OEMCryptoResult sts = OEMCrypto_GenerateRSASignature(
s.session_id(), licenseRequest.data(), licenseRequest.size(), nullptr,
&signature_length, scheme);
s.session_id(), digested_message.data(), digested_message.size(),
nullptr, &signature_length, scheme);
ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts);
ASSERT_NE(static_cast<size_t>(0), signature_length);
std::vector<uint8_t> signature(signature_length, 0);
sts = OEMCrypto_GenerateRSASignature(
s.session_id(), licenseRequest.data(), licenseRequest.size(),
s.session_id(), digested_message.data(), digested_message.size(),
signature.data(), &signature_length, scheme);
ASSERT_EQ(OEMCrypto_SUCCESS, sts)
@@ -51,7 +74,7 @@ class OEMCryptoLoadsCertificateAlternates : public OEMCryptoLoadsCertificate {
ASSERT_NO_FATAL_FAILURE(s.SetRsaPublicKeyFromPrivateKeyInfo(
encoded_rsa_key_.data(), encoded_rsa_key_.size()));
ASSERT_NO_FATAL_FAILURE(s.VerifyRsaSignature(
licenseRequest, signature.data(), signature_length, scheme));
digested_message, signature.data(), signature_length, scheme));
}
void DisallowDeriveKeys() {

View File

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

View File

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

View File

@@ -5,6 +5,8 @@
#include "oemcrypto_license_test.h"
#include <string>
#include "platform.h"
#include "test_sleep.h"
@@ -12,6 +14,9 @@ using ::testing::Range;
namespace wvoec {
/// @addtogroup license
/// @{
// 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
// doesn't crash.
@@ -758,6 +763,7 @@ TEST_P(OEMCryptoLicenseTest, MaxTotalKeysManySessions) {
TEST_F(OEMCryptoSessionTests, CheckMinimumPatchLevel) {
uint8_t patch_level = OEMCrypto_Security_Patch_Level();
printf(" Current Patch Level: %u.\n", patch_level);
RecordWvProperty("security_patch_level", std::to_string(patch_level));
{
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());

View File

@@ -5,6 +5,10 @@
#include "oemcrypto_provisioning_test.h"
#include <stdint.h>
#include <string>
#include "log.h"
#include "oec_device_features.h"
#include "platform.h"
@@ -12,6 +16,9 @@
namespace wvoec {
/// @addtogroup provision
/// @{
// This test is used to print the device ID to stdout.
TEST_F(OEMCryptoKeyboxTest, NormalGetDeviceId) {
OEMCryptoResult sts;
@@ -21,6 +28,7 @@ TEST_F(OEMCryptoKeyboxTest, NormalGetDeviceId) {
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
cout << " NormalGetDeviceId: dev_id = "
<< 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) {
@@ -50,8 +58,12 @@ TEST_F(OEMCryptoKeyboxTest, NormalGetKeyData) {
sts = OEMCrypto_GetKeyData(key_data, &key_data_len);
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",
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);
}
@@ -106,6 +118,8 @@ TEST_F(OEMCryptoProv30Test, GetDeviceId) {
dev_id.resize(dev_id_len);
cout << " NormalGetDeviceId: dev_id = " << MaybeHex(dev_id)
<< " len = " << dev_id_len << endl;
RecordWvProperty("device_id",
wvutil::HexEncode(dev_id.data(), dev_id.size()));
}
// The OEM certificate must be valid.
@@ -545,13 +559,13 @@ TEST_F(OEMCryptoProv40Test, InstallOemPrivateKeyCanBeUsed) {
wrapped_private_key2.resize(wrapped_private_key_size2);
// 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(
public_key1.data(), public_key1.size()));
ASSERT_NO_FATAL_FAILURE(
s.VerifyRsaSignature(public_key2, public_key_signature2.data(),
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(
public_key1.data(), public_key1.size()));
ASSERT_NO_FATAL_FAILURE(s.VerifyEccSignature(public_key2,
@@ -619,6 +633,8 @@ TEST_F(OEMCryptoProv40Test, GetDeviceId) {
dev_id.resize(dev_id_len);
cout << " NormalGetDeviceId: dev_id = " << MaybeHex(dev_id)
<< " len = " << dev_id_len << endl;
RecordWvProperty("device_id",
wvutil::HexEncode(dev_id.data(), dev_id.size()));
// Device id should be stable. Query again.
std::vector<uint8_t> dev_id2(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()));
}
/// @}
} // 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 {
/// @addtogroup android
/// @{
// These tests are required for LollyPop Android devices.
class OEMCryptoAndroidLMPTest : public ::testing::Test {
protected:
@@ -174,4 +177,5 @@ TEST_F(OEMCryptoAndroidQTest, MinVersionNumber14) {
ASSERT_GE(version, 15u);
}
/// @}
} // namespace wvoec

View File

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

View File

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

View File

@@ -7,7 +7,8 @@
#ifndef WVCDM_UTIL_FILE_STORE_H_
#define WVCDM_UTIL_FILE_STORE_H_
#include <cstdint>
#include <stddef.h>
#include <memory>
#include <string>
#include <vector>
@@ -18,16 +19,35 @@
namespace wvutil {
// Fixed filename for ATSC DRM certificate pre-installed
// on ATSC devices for ATSC licenses.
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";
// File extension for DRM and OEM certificate files.
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 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 kOemCertificateFileNamePrefix = "oemcert_";
// File class. The implementation is platform dependent.
// File interface. The implementation is platform dependent.
class File {
public:
File() {}
@@ -35,35 +55,70 @@ class File {
virtual ssize_t Read(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);
};
// File system base class. The implementation is platform dependent.
class FileSystem {
public:
FileSystem();
FileSystem(const std::string& origin, void* extra_data);
virtual ~FileSystem();
// Concreate implementation of FileSystem.
// Depending on the platform, this may be vendor or Widevine implemented.
class Impl;
// defines as bit flag
enum OpenFlags {
kNoFlags = 0,
kCreate = 1,
kReadOnly = 2, // defaults to read and write access
kTruncate = 4
};
// Flags for calls to Open.
static constexpr int kNoFlags = 0;
// Create file if does not already exist, open file if it does exist.
static constexpr int kCreate = (1 << 0);
// Open file as read-only; typically should not be used with kCreate.
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 bool Exists(const std::string& file_path);
virtual bool Exists(const std::string& file_path, int* errno_value);
virtual bool Remove(const std::string& file_path);
// Checks if the |path| exists. The |path| may be a file or directory.
// Return true if an entry in the file system exists; false otherwise.
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);
// Return the filenames stored at dir_path.
// dir_path will be stripped from the returned names.
// Return the entries stored at |dir_path| (includes both files
// 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,
std::vector<std::string>* names);

View File

@@ -1,9 +1,14 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
#include "file_store.h"
#include <errno.h>
#include <set>
#include <string>
#include <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -11,18 +16,20 @@
#include "test_vectors.h"
namespace wvutil {
namespace {
const std::string kTestDirName = "test_dir";
const std::string kTestFileName = "test.txt";
const std::string kTestFileName2 = "test2.txt";
const std::string kTestFileName3 = "test3.other";
const std::string kTestFileNameExt = ".txt";
const std::string kTestFileNameExt3 = ".other";
const std::string kTestIdentifier1 = "some_identifier";
const std::string kTestIdentifier2 = "some_other_identifier";
const std::string kWildcard = "*";
const std::string kUnderscore = "_";
constexpr char kTestFilename[] = "sample.txt";
constexpr char kTestIdentifier1[] = "some_identifier";
constexpr char kTestIdentifier2[] = "some_other_identifier";
constexpr int kNoError = 0;
constexpr int kEntryDoesNotExist = ENOENT;
bool StartsWith(const std::string& haystack, const std::string& needle) {
if (needle.empty()) return true;
if (haystack.size() < needle.size()) return false;
return haystack.find(needle) == 0;
}
} // namespace
class FileTest : public testing::Test {
@@ -32,7 +39,19 @@ class FileTest : public testing::Test {
void TearDown() override { 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_;
@@ -40,336 +59,505 @@ class FileTest : public testing::Test {
TEST_F(FileTest, FileExists) {
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(
file_system_.Exists(wvcdm::test_vectors::kExistentFile, &errno_value));
EXPECT_EQ(0, errno_value);
EXPECT_TRUE(file_system_.Exists(wvcdm::test_vectors::kExistentDir));
file_system_.Exists(wvcdm::test_vectors::kExistentFile, &errno_value))
<< "path = " << wvcdm::test_vectors::kExistentFile;
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(
file_system_.Exists(wvcdm::test_vectors::kNonExistentFile, &errno_value));
EXPECT_EQ(ENOENT, errno_value);
EXPECT_FALSE(file_system_.Exists(wvcdm::test_vectors::kNonExistentDir));
file_system_.Exists(wvcdm::test_vectors::kNonExistentFile, &errno_value))
<< "path = " << wvcdm::test_vectors::kNonExistentFile;
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) {
EXPECT_TRUE(file_system_.Remove(wvcdm::test_vectors::kTestDir));
EXPECT_FALSE(file_system_.Exists(wvcdm::test_vectors::kTestDir));
EXPECT_TRUE(file_system_.Remove(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) {
std::string path = wvcdm::test_vectors::kTestDir + kTestFileName;
EXPECT_TRUE(file_system_.Remove(path));
const std::string 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);
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) {
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);
ASSERT_TRUE(file);
ASSERT_TRUE(file) << "Failed to create file: " << path;
EXPECT_TRUE(file_system_.Exists(path));
EXPECT_TRUE(file_system_.Remove(path));
EXPECT_FALSE(file_system_.Exists(path));
EXPECT_TRUE(file_system_.Exists(path)) << "path = " << path;
EXPECT_TRUE(file_system_.Remove(path)) << "path = " << path;
EXPECT_FALSE(file_system_.Exists(path)) << "path = " << path;
file = file_system_.Open(path, FileSystem::kCreate);
ASSERT_TRUE(file);
ASSERT_TRUE(file) << "Failed to create file: " << path;
EXPECT_TRUE(file_system_.Exists(path));
RemoveTestDir();
EXPECT_FALSE(file_system_.Exists(wvcdm::test_vectors::kTestDir));
EXPECT_FALSE(file_system_.Exists(path));
EXPECT_TRUE(file_system_.Exists(path)) << "path = " << path;
ASSERT_NO_FATAL_FAILURE(RemoveTestDir());
EXPECT_FALSE(file_system_.Exists(wvcdm::test_vectors::kTestDir))
<< "path = " << path;
EXPECT_FALSE(file_system_.Exists(path)) << "path = " << path;
}
TEST_F(FileTest, RemoveWildcardFiles) {
std::string path1 = wvcdm::test_vectors::kTestDir + kTestFileName;
std::string path2 = wvcdm::test_vectors::kTestDir + kTestFileName2;
std::string wildcard_path =
wvcdm::test_vectors::kTestDir + kWildcard + kTestFileNameExt;
const std::string path1 =
PathJoin(wvcdm::test_vectors::kTestDir, "first.txt");
const std::string path2 =
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);
ASSERT_TRUE(file);
ASSERT_TRUE(file) << "Failed to create file (1): " << path1;
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(path2));
EXPECT_TRUE(file_system_.Remove(wildcard_path));
EXPECT_FALSE(file_system_.Exists(path1));
EXPECT_FALSE(file_system_.Exists(path2));
EXPECT_TRUE(file_system_.Exists(path1)) << "path = " << path1;
EXPECT_TRUE(file_system_.Exists(path2)) << "path = " << path2;
EXPECT_TRUE(file_system_.Remove(wildcard_path))
<< "wildcard_path = " << wildcard_path;
EXPECT_FALSE(file_system_.Exists(path1)) << "path = " << path1;
EXPECT_FALSE(file_system_.Exists(path2)) << "path = " << path2;
}
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);
std::string write_data = CdmRandom::RandomData(600);
size_t write_data_size = write_data.size();
std::unique_ptr<File> file = file_system_.Open(path, FileSystem::kCreate);
ASSERT_TRUE(file);
EXPECT_EQ(file->Write(write_data.data(), write_data_size), write_data_size);
EXPECT_TRUE(file_system_.Exists(path));
constexpr size_t kDataSize = 600;
const std::string write_data = CdmRandom::RandomData(kDataSize);
ASSERT_EQ(write_data.size(), kDataSize);
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) {
std::string path = wvcdm::test_vectors::kTestDir + kTestFileName;
const std::string path =
PathJoin(wvcdm::test_vectors::kTestDir, kTestFilename);
file_system_.Remove(path);
std::string write_data = CdmRandom::RandomData(600);
size_t write_data_size = write_data.size();
std::unique_ptr<File> file = file_system_.Open(path, FileSystem::kCreate);
ASSERT_TRUE(file);
EXPECT_EQ(file->Write(write_data.data(), write_data_size), write_data_size);
EXPECT_TRUE(file_system_.Exists(path));
constexpr size_t kDataSize = 600;
const std::string write_data = CdmRandom::RandomData(kDataSize);
ASSERT_EQ(write_data.size(), kDataSize);
std::unique_ptr<File> file = file_system_.Open(path, FileSystem::kCreate);
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);
ASSERT_TRUE(file);
EXPECT_EQ(file->Read(&read_data[0], read_data_size), read_data_size);
ASSERT_TRUE(file) << "Failed to re-open file: " << path;
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);
}
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");
std::string path1 = wvcdm::test_vectors::kTestDir + kTestFileName;
std::string path2 = wvcdm::test_vectors::kTestDir + kTestFileName2;
std::string path3 = wvcdm::test_vectors::kTestDir + kTestFileName3;
std::string path_dir = wvcdm::test_vectors::kTestDir;
const std::string dir_path = wvcdm::test_vectors::kTestDir;
const std::string path1 = PathJoin(dir_path, kTxtFilename1);
const std::string path2 = PathJoin(dir_path, kTxtFilename2);
const std::string path3 = PathJoin(dir_path, kBinFilename);
// Create files.
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);
ASSERT_TRUE(file);
ASSERT_TRUE(file) << "Failed to create file (2): " << path2;
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(path2));
EXPECT_TRUE(file_system_.Exists(path3));
// Ask for non-existent path.
EXPECT_FALSE(file_system_.List(not_path, &names));
std::vector<std::string> names;
ASSERT_TRUE(file_system_.List(dir_path, &names)) << "dir_path = " << dir_path;
// Valid path, but no way to return names.
EXPECT_FALSE(file_system_.List(path_dir, nullptr));
size_t expected_file_count = 3;
EXPECT_EQ(names.size(), expected_file_count);
// Valid path, valid return.
EXPECT_TRUE(file_system_.List(path_dir, &names));
// Should find three files. Order not important.
EXPECT_EQ(3u, names.size());
// Should find the three files. Order not important.
EXPECT_THAT(names, ::testing::UnorderedElementsAre(
kTestFileName, kTestFileName2, kTestFileName3));
kTxtFilename1, kTxtFilename2, kBinFilename));
std::string wild_card_path = path_dir + kWildcard + kTestFileNameExt;
EXPECT_TRUE(file_system_.Remove(wild_card_path));
EXPECT_TRUE(file_system_.List(path_dir, &names));
// Remove .txt files.
const std::string txt_wildcard_path = PathJoin(dir_path, "*.txt");
EXPECT_EQ(1u, names.size());
EXPECT_TRUE(names[0].compare(kTestFileName3) == 0);
EXPECT_TRUE(file_system_.Remove(txt_wildcard_path))
<< "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;
EXPECT_TRUE(file_system_.Remove(wild_card_path2));
EXPECT_TRUE(file_system_.List(path_dir, &names));
expected_file_count = 1;
ASSERT_EQ(names.size(), expected_file_count);
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) {
// Clear directory
TEST_F(FileTest, ListFiles_NotAPath) {
const std::string not_path("zzz/xxx");
std::vector<std::string> names;
std::string path_dir = wvcdm::test_vectors::kTestDir;
std::string wild_card_path = path_dir + kWildcard;
file_system_.Remove(wild_card_path);
if (file_system_.List(path_dir, &names)) {
EXPECT_EQ(0u, names.size());
// Ask for non-existent path.
EXPECT_FALSE(file_system_.List(not_path, &names));
}
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
std::string certificate_path =
wvcdm::test_vectors::kTestDir + kCertificateFileName;
std::string legacy_certificate_path =
wvcdm::test_vectors::kTestDir + kLegacyCertificateFileName;
const std::string certificate_path = PathJoin(dir_path, kCertificateFileName);
const std::string legacy_certificate_path =
PathJoin(dir_path, kLegacyCertificateFileName);
// Create certificates and verify that they exist
std::unique_ptr<File> file =
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);
ASSERT_TRUE(file);
EXPECT_TRUE(file_system_.IsGlobal());
ASSERT_TRUE(file) << "Failed to create legacy certificate file: "
<< legacy_certificate_path;
file.reset(); // Close file.
EXPECT_TRUE(file_system_.Exists(certificate_path));
EXPECT_TRUE(file_system_.Exists(legacy_certificate_path));
EXPECT_TRUE(file_system_.Exists(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.
EXPECT_EQ(2u, names.size());
constexpr size_t kExpectedCount = 2;
EXPECT_EQ(kExpectedCount, names.size());
EXPECT_THAT(names, ::testing::UnorderedElementsAre(
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) {
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
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::string path_dir = wvcdm::test_vectors::kTestDir;
std::string wild_card_path = path_dir + kWildcard;
file_system_.Remove(wild_card_path);
if (file_system_.List(path_dir, &names)) {
EXPECT_EQ(0u, names.size());
if (file_system_.List(dir_path, &names)) {
constexpr size_t kZero = 0;
ASSERT_EQ(kZero, names.size()) << "Test requires empty directory";
}
std::string certificate_path =
wvcdm::test_vectors::kTestDir + kCertificateFileName;
std::string legacy_certificate_path =
wvcdm::test_vectors::kTestDir + kLegacyCertificateFileName;
const std::string certificate_path = PathJoin(dir_path, kCertificateFileName);
const std::string legacy_certificate_path =
PathJoin(dir_path, kLegacyCertificateFileName);
// Create Global certificates
// Create Global certificates.
std::unique_ptr<File> file =
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);
ASSERT_TRUE(file);
EXPECT_TRUE(file_system_.IsGlobal());
ASSERT_TRUE(file) << "Failed to create global legacy certificate: "
<< 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
file_system_.set_identifier(kTestIdentifier1);
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);
ASSERT_TRUE(file);
EXPECT_TRUE(!file_system_.IsGlobal());
ASSERT_TRUE(file) << "Failed to create " << kTestIdentifier1
<< " 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
file_system_.set_identifier(kTestIdentifier2);
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);
ASSERT_TRUE(file);
EXPECT_TRUE(!file_system_.IsGlobal());
ASSERT_TRUE(file) << "Failed to create " << kTestIdentifier2
<< " legacy certificate: " << legacy_certificate_path;
file.reset(); // Close file.
EXPECT_TRUE(file_system_.Exists(certificate_path));
EXPECT_TRUE(file_system_.Exists(legacy_certificate_path));
// Verify they now exist.
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.
constexpr size_t kExpectedTotalCertCount = 6;
ASSERT_EQ(names.size(), kExpectedTotalCertCount);
bool is_global_certificate_present = false;
bool is_global_legacy_certificate_present = false;
size_t certificate_count = 0;
size_t legacy_certificate_count = 0;
EXPECT_EQ(6u, names.size());
for (size_t i = 0; i < names.size(); ++i) {
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) {
for (const auto& filename : names) {
if (filename == kLegacyCertificateFileName) {
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 {
EXPECT_TRUE(false);
ADD_FAILURE() << "Unexpected filename: " << filename;
}
}
EXPECT_EQ(2, certificate_count);
EXPECT_EQ(2, legacy_certificate_count);
EXPECT_TRUE(is_global_certificate_present);
EXPECT_TRUE(is_global_legacy_certificate_present);
constexpr size_t kExpectedScopedCertCount = 2;
EXPECT_EQ(certificate_count, kExpectedScopedCertCount)
<< "Missing certificates";
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) {
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
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::string path_dir = wvcdm::test_vectors::kTestDir;
std::string wild_card_path = path_dir + kWildcard;
file_system_.Remove(wild_card_path);
if (file_system_.List(path_dir, &names)) {
EXPECT_EQ(0u, names.size());
if (file_system_.List(dir_path, &names)) {
constexpr size_t kZero = 0;
ASSERT_EQ(kZero, names.size()) << "Test requires empty directory";
}
std::string certificate_path =
wvcdm::test_vectors::kTestDir + kCertificateFileName;
std::string legacy_certificate_path =
wvcdm::test_vectors::kTestDir + kLegacyCertificateFileName;
const std::string certificate_path = PathJoin(dir_path, kCertificateFileName);
const std::string legacy_certificate_path =
PathJoin(dir_path, kLegacyCertificateFileName);
// Create Global certificates
// Create Global certificates.
std::unique_ptr<File> file =
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);
ASSERT_TRUE(file);
EXPECT_TRUE(file_system_.IsGlobal());
ASSERT_TRUE(file) << "Failed to create global legacy certificate: "
<< 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
file_system_.set_identifier(kTestIdentifier1);
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);
ASSERT_TRUE(file);
EXPECT_TRUE(!file_system_.IsGlobal());
ASSERT_TRUE(file) << "Failed to create " << kTestIdentifier1
<< " 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
file_system_.set_identifier(kTestIdentifier2);
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);
ASSERT_TRUE(file);
EXPECT_TRUE(!file_system_.IsGlobal());
ASSERT_TRUE(file) << "Failed to create " << kTestIdentifier2
<< " legacy certificate: " << legacy_certificate_path;
file.reset(); // Close file.
EXPECT_TRUE(file_system_.Exists(certificate_path));
EXPECT_TRUE(file_system_.Exists(legacy_certificate_path));
EXPECT_TRUE(file_system_.List(path_dir, &names));
EXPECT_EQ(6u, names.size());
// 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.
constexpr size_t kExpectedTotalCertCount = 6;
ASSERT_EQ(names.size(), kExpectedTotalCertCount);
std::set<std::string> removed_certs;
// Remove all even number listed files
for (size_t i = 0; i < names.size(); ++i) {
if (i % 2 == 0) {
EXPECT_TRUE(
file_system_.Remove(wvcdm::test_vectors::kTestDir + names[i]));
}
if ((i % 2) != 0) continue;
const std::string& cert_filename = 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
for (size_t i = 0; i < names.size(); ++i) {
if (i % 2 == 1) {
EXPECT_TRUE(
file_system_.Exists(wvcdm::test_vectors::kTestDir + names[i]));
for (const std::string& cert_filename : names) {
const std::string cert_path = PathJoin(dir_path, cert_filename);
if (removed_certs.find(cert_filename) == removed_certs.end()) {
// Ensure still exists.
ASSERT_TRUE(file_system_.Exists(cert_path))
<< "Cert missing: " << cert_filename;
} else {
EXPECT_FALSE(
file_system_.Exists(wvcdm::test_vectors::kTestDir + names[i]));
ASSERT_FALSE(file_system_.Exists(cert_path))
<< "Cert not removed: " << cert_filename;
}
}
// Remove all odd number listed files
for (size_t i = 0; i < names.size(); ++i) {
if (i % 2 == 1) {
EXPECT_TRUE(
file_system_.Remove(wvcdm::test_vectors::kTestDir + names[i]));
}
// Remove all remaining.
for (const std::string& cert_filename : names) {
if (removed_certs.find(cert_filename) != removed_certs.end()) continue;
const std::string cert_path = PathJoin(dir_path, cert_filename);
ASSERT_TRUE(file_system_.Remove(cert_path))
<< "Failed to remove cert: " << cert_path;
}
// Verify that all have been removed
for (size_t i = 0; i < names.size(); ++i) {
EXPECT_FALSE(file_system_.Exists(wvcdm::test_vectors::kTestDir + names[i]));
for (const std::string& cert_filename : names) {
const std::string cert_path = PathJoin(dir_path, cert_filename);
EXPECT_FALSE(file_system_.Exists(cert_path))
<< "Cert not removed: " << cert_filename;
}
}
} // namespace wvutil