// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary // source code may only be used and distributed under the Widevine // 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; supports_secp256r1 = false; api_version = 0; derive_key_method = NO_METHOD; OEMCrypto_SetSandbox(kTestSandbox, sizeof(kTestSandbox)); const OEMCryptoResult init_status = OEMCrypto_Initialize(); if (OEMCrypto_SUCCESS != init_status) { printf("OEMCrypto_Initialize failed %d. All tests will fail.\n", init_status); return; } const OEMCryptoResult api_status = OEMCrypto_SetMaxAPIVersion(kCurrentAPI); if (api_status != OEMCrypto_SUCCESS && api_status != OEMCrypto_ERROR_NOT_IMPLEMENTED) { // Log error, but continue assuming no error. printf("OEMCrypto_SetMaxAPIVersion returned %d\n", api_status); } const OEMCryptoResult test_mode_status = OEMCrypto_EnterTestMode(); if (OEMCrypto_SUCCESS != test_mode_status) { printf("OEMCrypto_EnterTestMode returned %d. Tests might fail.\n", test_mode_status); }; 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. Devices with a keybox or OEM Certificate are required to support // loading a DRM certificate. Other devices are either broken, or they have a // baked in certificate. loads_certificate = provisioning_method == OEMCrypto_Keybox || provisioning_method == OEMCrypto_OEMCertificate || provisioning_method == OEMCrypto_BootCertificateChain; printf("loads_certificate = %s.\n", loads_certificate ? "true" : "false"); generic_crypto = (OEMCrypto_ERROR_NOT_IMPLEMENTED != OEMCrypto_Generic_Encrypt(buffer, 0, buffer, 0, iv, OEMCrypto_AES_CBC_128_NO_PADDING, buffer)); printf("generic_crypto = %s.\n", generic_crypto ? "true" : "false"); supports_cas = (OEMCrypto_ERROR_NOT_IMPLEMENTED != OEMCrypto_LoadCasECMKeys(session, nullptr, 0, nullptr, nullptr)); printf("supports_cas = %s.\n", supports_cas ? "true" : "false"); OEMCrypto_CloseSession(session); api_version = OEMCrypto_APIVersion(); printf("api_version = %u.\n", api_version); if (api_version < kCoreMessagesAPI) { printf("--------- WARNING: minimum API is %d ----------\n", api_version); printf("--------- Expect most tests will fail. --------\n"); } // These unit tests only work with new usage tables. We do not test v12 // usage tables. usage_table = OEMCrypto_SupportsUsageTable(); printf("usage_table = %s.\n", usage_table ? "true" : "false"); PickDerivedKey(); const 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; } if (supported_cert & OEMCrypto_Supports_ECC_secp256r1) { supports_secp256r1 = true; } printf("cast_receiver = %s.\n", cast_receiver ? "true" : "false"); printf("supports_rsa_3072 = %s.\n", supports_rsa_3072 ? "true" : "false"); printf("supports_secp256r1 = %s.\n", supports_secp256r1 ? "true" : "false"); resource_rating = OEMCrypto_ResourceRatingTier(); printf("resource_rating = %u, security level %u.\n", resource_rating, static_cast(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"); 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 provisioned with OEM Cert.\n"); break; case TEST_PROVISION_40: printf("TEST_PROVISION_40: Device has boot certificate chain.\n"); break; } OEMCrypto_Security_Level security_level = OEMCrypto_SecurityLevel(); supports_level_1 = (security_level == OEMCrypto_Level1); printf("SecurityLevel is %s (L%u)\n", supports_level_1 ? "Level 1" : "Not Level 1", static_cast(security_level)); CheckSecureBuffers(); OEMCrypto_Terminate(); initialized_ = true; } void DeviceFeatures::PickDerivedKey() { switch (provisioning_method) { case OEMCrypto_OEMCertificate: derive_key_method = TEST_PROVISION_30; return; case OEMCrypto_DrmCertificate: case OEMCrypto_DrmReprovisioning: if (OEMCrypto_ERROR_NOT_IMPLEMENTED != OEMCrypto_LoadTestRSAKey()) { derive_key_method = LOAD_TEST_RSA_KEY; } return; case OEMCrypto_Keybox: if (OEMCrypto_ERROR_NOT_IMPLEMENTED != OEMCrypto_LoadTestKeybox(nullptr, 0)) { derive_key_method = LOAD_TEST_KEYBOX; } return; case OEMCrypto_BootCertificateChain: derive_key_method = TEST_PROVISION_40; return; case OEMCrypto_ProvisioningError: printf( "ERROR: OEMCrypto_GetProvisioningMethod() returns " "OEMCrypto_ProvisioningError\n"); if (OEMCrypto_ERROR_NOT_IMPLEMENTED != OEMCrypto_LoadTestRSAKey()) { derive_key_method = LOAD_TEST_RSA_KEY; } return; } } 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"; case OEMCrypto_BootCertificateChain: return "OEMCrypto_BootCertificateChain"; case OEMCrypto_DrmReprovisioning: return "OEMCrypto_DrmReprovisioning"; } // Not reachable return ""; } } // namespace wvoec