// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary // source code may only be used and distributed under the Widevine Master // License Agreement. // // OEMCrypto device features for unit tests // #include "oec_device_features.h" #include #include #include "oec_test_data.h" #include "test_sleep.h" namespace wvoec { DeviceFeatures global_features; void DeviceFeatures::Initialize() { if (initialized_) return; uses_keybox = false; loads_certificate = false; generic_crypto = false; usage_table = false; supports_rsa_3072 = false; api_version = 0; derive_key_method = NO_METHOD; OEMCrypto_SetSandbox(kTestSandbox, sizeof(kTestSandbox)); if (OEMCrypto_SUCCESS != OEMCrypto_Initialize()) { printf("OEMCrypto_Initialize failed. All tests will fail.\n"); return; } uint8_t buffer[1]; uint8_t iv[16] = {}; size_t size = 0; provisioning_method = OEMCrypto_GetProvisioningMethod(); printf("provisioning_method = %s\n", ProvisioningMethodName(provisioning_method)); uses_keybox = (OEMCrypto_ERROR_NOT_IMPLEMENTED != OEMCrypto_GetKeyData(buffer, &size)); printf("uses_keybox = %s.\n", uses_keybox ? "true" : "false"); OEMCrypto_SESSION session; OEMCryptoResult result = OEMCrypto_OpenSession(&session); if (result != OEMCrypto_SUCCESS) { printf("--- ERROR: Could not open session: %d ----\n", result); } // If the device uses a keybox, check to see if loading a certificate is // installed. if (provisioning_method == OEMCrypto_Keybox || provisioning_method == OEMCrypto_OEMCertificate) { // Devices with a keybox or OEM Certificate are required to support loading // a DRM certificate. loads_certificate = true; } else { // Other devices are either broken, or they have a baked in certificate. loads_certificate = false; } printf("loads_certificate = %s.\n", loads_certificate ? "true" : "false"); generic_crypto = (OEMCrypto_ERROR_NOT_IMPLEMENTED != OEMCrypto_Generic_Encrypt(session, buffer, 0, iv, OEMCrypto_AES_CBC_128_NO_PADDING, buffer)); printf("generic_crypto = %s.\n", generic_crypto ? "true" : "false"); OEMCrypto_CloseSession(session); api_version = OEMCrypto_APIVersion(); printf("api_version = %u.\n", api_version); // These unit tests only work with new usage tables. We do not test v12 // usage tables. if (api_version > 12) usage_table = OEMCrypto_SupportsUsageTable(); printf("usage_table = %s.\n", usage_table ? "true" : "false"); PickDerivedKey(); if (api_version >= 13) { uint32_t supported_cert = OEMCrypto_SupportedCertificates(); if (supported_cert & OEMCrypto_Supports_RSA_CAST) { cast_receiver = true; } if (supported_cert & OEMCrypto_Supports_RSA_3072bit) { supports_rsa_3072 = true; } } printf("cast_receiver = %s.\n", cast_receiver ? "true" : "false"); resource_rating = OEMCrypto_ResourceRatingTier(); printf("resource_rating = %u, security level %s.\n", resource_rating, OEMCrypto_SecurityLevel()); uint32_t decrypt_hash_type = OEMCrypto_SupportsDecryptHash(); supports_crc = (decrypt_hash_type == OEMCrypto_CRC_Clear_Buffer); if (supports_crc) { printf("Decrypt hashes will be tested.\n"); } else { printf("Decrypt hashes will not be tested -- %s.\n", decrypt_hash_type == OEMCrypto_Hash_Not_Supported ? "not supported" : "partner defined hash"); } switch (derive_key_method) { case NO_METHOD: printf("NO_METHOD: Cannot derive known session keys.\n"); // Note: cast_receiver left unchanged because set by user on command line. uses_keybox = false; loads_certificate = false; generic_crypto = false; usage_table = false; break; case LOAD_TEST_KEYBOX: printf("LOAD_TEST_KEYBOX: Call LoadTestKeybox before deriving keys.\n"); break; case LOAD_TEST_RSA_KEY: printf("LOAD_TEST_RSA_KEY: Call LoadTestRSAKey before deriving keys.\n"); break; case TEST_PROVISION_30: printf("TEST_PROVISION_30: Device provisioed with OEM Cert.\n"); break; } std::string security_level = OEMCrypto_SecurityLevel(); supports_level_1 = (security_level == "L1"); printf("SecurityLevel is %s (%s)\n", supports_level_1 ? "Level 1" : "Not Level 1", security_level.c_str()); CheckSecureBuffers(); OEMCrypto_Terminate(); initialized_ = true; } std::string DeviceFeatures::RestrictFilter(const std::string& initial_filter) { std::string filter = initial_filter; // clang-format off if (!uses_keybox) FilterOut(&filter, "*KeyboxTest*"); if (!loads_certificate) FilterOut(&filter, "OEMCryptoLoadsCert*"); if (!generic_crypto) FilterOut(&filter, "*GenericCrypto*"); if (!cast_receiver) FilterOut(&filter, "*CastReceiver*"); if (!usage_table) FilterOut(&filter, "*UsageTable*"); if (derive_key_method == NO_METHOD) FilterOut(&filter, "*SessionTest*"); if (provisioning_method != OEMCrypto_OEMCertificate) FilterOut(&filter, "*Prov30*"); if (!supports_rsa_3072) FilterOut(&filter, "*RSAKey3072*"); if (api_version < 9) FilterOut(&filter, "*API09*"); if (api_version < 10) FilterOut(&filter, "*API10*"); if (api_version < 11) FilterOut(&filter, "*API11*"); if (api_version < 12) FilterOut(&filter, "*API12*"); if (api_version < 13) FilterOut(&filter, "*API13*"); if (api_version < 14) FilterOut(&filter, "*API14*"); if (api_version < 15) FilterOut(&filter, "*API15*"); if (api_version < 16) FilterOut(&filter, "*API16*"); // clang-format on // Some tests may require root access. If user is not root, filter these tests // out. if (!wvcdm::TestSleep::CanChangeSystemTime()) { printf("Filtering out TimeRollbackPrevention.\n"); FilterOut(&filter, "*TimeRollbackPrevention*"); } else { printf("Can change time. I will run TimeRollbackPrevention.\n"); } // Performance tests take a long time. Filter them out if they are not // specifically requested. if (filter.find("Performance") == std::string::npos) { FilterOut(&filter, "*Performance*"); } return filter; } void DeviceFeatures::PickDerivedKey() { if (api_version >= 12) { switch (provisioning_method) { case OEMCrypto_OEMCertificate: derive_key_method = TEST_PROVISION_30; return; case OEMCrypto_DrmCertificate: if (OEMCrypto_ERROR_NOT_IMPLEMENTED != OEMCrypto_LoadTestRSAKey()) { derive_key_method = LOAD_TEST_RSA_KEY; } return; case OEMCrypto_Keybox: // Fall through to api_version < 12 case. break; case OEMCrypto_ProvisioningError: printf( "ERROR: OEMCrypto_GetProvisioningMethod() returns " "OEMCrypto_ProvisioningError\n"); // Then fall through to api_version < 12 case. break; } } if (uses_keybox) { // If device uses a keybox, try to load the test keybox. if (OEMCrypto_ERROR_NOT_IMPLEMENTED != OEMCrypto_LoadTestKeybox(nullptr, 0)) { derive_key_method = LOAD_TEST_KEYBOX; } } else if (OEMCrypto_ERROR_NOT_IMPLEMENTED != OEMCrypto_LoadTestRSAKey()) { derive_key_method = LOAD_TEST_RSA_KEY; } } void DeviceFeatures::CheckSecureBuffers() { output_types_.push_back({false, OEMCrypto_BufferType_Clear}); output_types_.push_back({true, OEMCrypto_BufferType_Clear}); test_secure_buffers = false; OEMCrypto_SESSION session; OEMCryptoResult result = OEMCrypto_OpenSession(&session); if (result != OEMCrypto_SUCCESS) { printf("--- ERROR: Could not open session: %d ----\n", result); return; } OEMCrypto_DestBufferDesc output_descriptor; output_descriptor.type = OEMCrypto_BufferType_Secure; int secure_fd; result = OEMCrypto_AllocateSecureBuffer(session, 42, &output_descriptor, &secure_fd); if (result == OEMCrypto_ERROR_NOT_IMPLEMENTED) { printf("Secure buffers will not be tested\n"); return; } if (result != OEMCrypto_SUCCESS) { printf("--- ERROR: Could not create secure buffer: %d ----\n", result); return; } result = OEMCrypto_FreeSecureBuffer(session, &output_descriptor, secure_fd); if (result != OEMCrypto_SUCCESS) { printf("--- ERROR: Could not free secure buffer: %d ----\n", result); return; } printf("Secure buffers will be tested\n"); output_types_.push_back({false, OEMCrypto_BufferType_Secure}); test_secure_buffers = true; } void DeviceFeatures::FilterOut(std::string* current_filter, const std::string& new_filter) { if (current_filter->find('-') == std::string::npos) { *current_filter += "-" + new_filter; } else { *current_filter += ":" + new_filter; } } // Return the list of output types for the decrypt tests. const std::vector& DeviceFeatures::GetOutputTypes() { if (!initialized_) { Initialize(); } return output_types_; } const char* ProvisioningMethodName(OEMCrypto_ProvisioningMethod method) { switch (method) { case OEMCrypto_ProvisioningError: return "OEMCrypto_ProvisioningError"; case OEMCrypto_DrmCertificate: return "OEMCrypto_DrmCertificate"; case OEMCrypto_Keybox: return "OEMCrypto_Keybox"; case OEMCrypto_OEMCertificate: return "OEMCrypto_OEMCertificate"; } // Not reachable return ""; } } // namespace wvoec