diff --git a/CHANGELOG.md b/CHANGELOG.md index 72cdac1..28a7098 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,21 @@ [TOC] +## [Version 18.4][v18.4] + +Version 18.4 includes the reference implementation in OPK to support MediaCAS, +and an end-to-end demo of OEMCrypto CAS functionality. These updates were in +fact introduced in Version 17.2. See CAS-related updates in the change log of +Version 17.2 for details. Since Version 17.2 changes were merged to OEMCrypto +v18 after Version 18.3 got published, we bumped the version to 18.4 to reflect +the updates. If your device doesn't support MediaCAS, this update can be skipped. + +### Other changes + +- A fix in ODK that matches minor version with major version during session +initialization. +- Added a unit test for zero subsample size. + ## [Version 18.3][v18.3] Version 18.3 includes a major feature (Cast with Provisioning 4.0) and various @@ -9,7 +24,7 @@ minor changes. Version 18.2 was an internal version bump for the ODK that included changes used by the provisioning server. Since we keep the ODK and OPK version numbers in sync, the OPK version effectively skipped 18.2. -## Cast with Provisioning 4.0 +### Cast with Provisioning 4.0 The OPK now supports devices that wish to act as cast receivers while using Provisioning 4.0. Previously, only devices using Provisioning 2.0 or devices @@ -20,7 +35,7 @@ The OPK changes are included in this release. The CDM changes are part of Android U. The provisioning server changes are live on Widevine staging servers, and will be pushed to production by August 2023. -## OP-TEE port changes +### OP-TEE port changes - Added CSR and DeviceInformation implementations for Provisioning 4.0. - Bugfix: REE->TEE message shared memory was sized based on the incoming request @@ -29,7 +44,7 @@ did not fit in the nearest page boundary. Fixed by setting the shared memory size to the maximum allowed and passing in the request size as a separate TEE_Param. -## Other changes +### Other changes - Updated BoringSSL dependency to https://boringssl.googlesource.com/boringssl/+/e1b8685770d0e82e5a4a3c5d24ad1602e05f2e83 @@ -50,7 +65,7 @@ license response. The client-side value of `MAX_NUM_KEYS` must be changed in `odk/include/odk_target.h` to match the server's value. This is only intended for closed network systems. -## Known issues +### Known issues - CdmOtaKeyboxTest.BasicTest may fail due to server issues - The ODK renewal clock is not correctly checked for all circumstances. This @@ -243,6 +258,46 @@ OS. 4.0. - The OPK does not yet support MediaCAS functionality. +## [Version 17.2][v17.2] + +This release contains the first version of OPK to support MediaCAS, an +end-to-end demo of OEMCrypto CAS functionality, several bug fixes in OPK and a +few updates to the OEMCrypto unit tests and fuzz tests. + +MediaCAS support has been added to OPK. `OPK_Pack_LoadCasECMKeys_Request()`, +`OPK_Unpack_LoadCasECMKeys_Request()`, `OPK_Pack_LoadCasECMKeys_Response()`, +`OPK_Unpack_LoadCasECMKeys_Response()` are moved out of the auto-generated +serialization code and are added to the special cases, to allow implementor to +pack customized data. CAS-specific WTPI functions along with a reference +implementation have been added. + +A new `cas` directory is added to the `ports/linux` project. This contains +an end-to-end demo of OEMCrypto CAS functionality. The OEMCrypto CAS test client +communicates with the Linux `tee_simulator_cas` via `liboemcrypto.so` and +`libtuner.so`. `tee_simulator_cas` loads CAS keys and performs descrambling. + +All CAS specific code in OPK is guarded by the compiler flag `SUPPORT_CAS`. + +Several other updates and fixes to OPK in this release include: +- `strnlen()` is removed from OPK to avoid issue caused by the terminating '\0'. +- Explicit call to `builtin_add_overflow()` is removed and `oemcrypto_overflow` + wrappers are used instead. +- Added non-NULL checks in `WTPI_UnwrapValidateAndInstallKeybox()`, + `OEMCrypto_OPK_SerializationVersion()`, and `OPKI_GetFromObjectTable()`. +- Validated the wrapped key size to be non-zero. +- Set OP-TEE serialized request size to the maximum size expected. +- HMACs are compared in constant time. +- Fixed pointer arithmetic with size_t to avoid unexpected truncation of the + calculated address. +- No-op for zero-sized subsample instead of aborting OPK. + +This release also contains a few updates to the OEMCrypto unit tests and fuzz +tests: +- Reduced clock skew in flaky duration tests. +- Removed device ID check since it is not required for v17. +- Added a test for zero subsample size. +- Cleaned up fuzz helper classes and added more fuzz test coverage. + ## [OPK Version 17.1.1][v17.1+opk-v17.1.1] This release fixes a flaw in the OPK code that could allow content that requires @@ -420,4 +475,7 @@ Public release for OEMCrypto API and ODK library version 16.4. [v17+test-updates+opk+mk]: https://widevine-partner.googlesource.com/oemcrypto/+/refs/tags/v17+test-updates+opk+mk [v17.1]: https://widevine-partner.googlesource.com/oemcrypto/+/refs/tags/v17.1 [v17.1+opk-v17.1.1]: https://widevine-partner.googlesource.com/oemcrypto/+/refs/tags/v17.1+opk-v17.1.1 +[v17.2]: https://widevine-partner.googlesource.com/oemcrypto/+/refs/tags/v17.2 [v18.1]: https://widevine-partner.googlesource.com/oemcrypto/+/refs/tags/v18.1 +[v18.3]: https://widevine-partner.googlesource.com/oemcrypto/+/refs/tags/v18.3 +[v18.4]: https://widevine-partner.googlesource.com/oemcrypto/+/refs/tags/v18.4 diff --git a/oemcrypto/include/OEMCryptoCENC.h b/oemcrypto/include/OEMCryptoCENC.h index 99e0f61..18d5474 100644 --- a/oemcrypto/include/OEMCryptoCENC.h +++ b/oemcrypto/include/OEMCryptoCENC.h @@ -3,7 +3,7 @@ // License Agreement. /** - * @mainpage OEMCrypto API v18.3 + * @mainpage OEMCrypto API v18.4 * * 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 @@ -136,6 +136,7 @@ * license to be reloaded. * * @defgroup entitled Entitlement License API + * Functions that are needed for entitled and entitlement licenses. * * [Entitlement licensing](../../index#entitlement) is a way to provide access * to content keys that may be stored elsewhere, such as in the content itself. @@ -1199,6 +1200,8 @@ OEMCryptoResult OEMCrypto_GenerateNonce(OEMCrypto_SESSION session, * @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE * @retval OEMCrypto_ERROR_SESSION_LOST_STATE * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED + * @retval OEMCrypto_ERROR_INVALID_KEY if the session's private key is not a + * DRM key. * * @buffer_size * OEMCrypto shall support message sizes as described in the section @@ -3355,6 +3358,8 @@ uint32_t OEMCrypto_MinorAPIVersion(void); * different TA builds. * - "build_timestamp" [string]: ISO 8601 formatted timestamp of the time the * TA was compiled, eg "YYYY-MM-DDTHH:MM:SS" + * - "is_factory_mode" [bool]: Whether this was built with FACTORY_MODE_ONLY + * defined * * While not required, another optional top level struct can be added to the * build information string to provide information about liboemcrypto.so: @@ -4973,7 +4978,9 @@ OEMCryptoResult OEMCrypto_GenerateCertificateKeyPair( OEMCrypto_PrivateKeyType* key_type); /** - * Get the serialized device information in CBOR map format. + * Get the serialized device information in CBOR map format. This is for devices + * that use Provisioning 4.0, with the device key uploading option in the + * factory. * * The device * information may contain, for example, device make and model, "fused" status, @@ -4982,7 +4989,9 @@ OEMCryptoResult OEMCrypto_GenerateCertificateKeyPair( * provisioning request is coming from the expected device in the fields, based * on the values previously uploaded and registered. * - * This method is used by provisioning 4 only. + * Devices that do not support Provisioning 4.0, or do not support + * Provisioning 4.0 Uploading Option should return + * OEMCrypto_ERROR_NOT_IMPLEMENTED. * * @param[out] device_info: pointer to the buffer that receives the serialized * device information in CBOR map format. @@ -5008,24 +5017,87 @@ OEMCryptoResult OEMCrypto_GetDeviceInformation(uint8_t* device_info, size_t* device_info_length); /** - * Get the serialized signed Certificate Signing Request (Csr) payload in - * COSE_Sign1 format. + * Get the serialized signed Certificate Signing Request (CSR) payload in + * COSE_Sign1 format. This is for devices that use Provisioning 4.0, with the + * device key uploading option in the factory. * - * The signed CSR payload contains challenge and device information. It is - * signed by the leaf cert of the boot certificate chain (BCC). It is only used - * in the factory, uploaded and validated during device registration. + * With the uploading option, the RKP factory extraction tool provided by Google + * makes a call to this function to collect the signed CSR payload for + * generating the CSR to be uploaded to the device database. The CSR payload is + * signed by the leaf cert of the Boot Certificate Chain. * - * This method is used by provisioning 4 only. + * The format of a CSR payload before COSE_Sign1 is a CBOR array described in + * Android IRemotelyProvisionedComponent.aidl (under "CsrPayload"): + * + * ~~~ + * CsrPayload = [ ; CBOR Array defining the payload for CSR. + * version: 3, ; The CsrPayload CDDL Schema version. + * CertificateType: "widevine" ; The type of certificate being requested. + * DeviceInfo, ; Defined in Android DeviceInfo.aidl + * KeysToSign: [] ; Empty list + * ] + * ~~~ + * + * The type of CertificateType is tstr and the value should always be + * "widevine". The type of KeysToSign is CBOR array and the value is not used, + * which should be left as an empty list. Note that the DeviceInfo above is a + * CBOR map structure defined in DeviceInfo.aidl, which can be constructed from + * the input |encoded_device_info|. DeviceInfo must be canonicalized according + * to the specification in RFC 7049. The required fields from DeviceInfo.aidl + * are: brand, manufacturer, product, model, device, vb_state, bootloader_state, + * vbmeta_digest, security_level. + * + * Once CsrPayload is prepared, together with |challenge| it is signed by the + * leaf cert of BCC, in the format of: + * + * ~~~ + * |signed_csr_payload| = SignedData<[ + * challenge: bstr .size (0..64), + * bstr .cbor CsrPayload, + * ]> + * ~~~ + * + * This function should output |signed_csr_payload| in the format of + * SignedData, which is a COSE_Sign1 CBOR and is defined in Android + * IRemotelyProvisionedComponent.aidl (under "SignedData"): + * + * ~~~ + * SignedData = [ + * protected: bstr .cbor { 1 : AlgorithmEdDSA / AlgorithmES256 / + * AlgorithmES384 }, + * unprotected: {}, + * payload: bstr .cbor Data / nil, + * signature: bstr ; PureEd25519(priv_key, Sig_structure) / + * ; ECDSA(priv_key, Sig_structure) + * ] + * ~~~ + * + * Also see OEMCrypto_GenerateCertificateKeyPair() for more details of + * SignedData and Sig_structure. + * + * Data in the payload field of SignedData is a CBOR array: + * + * ~~~ + * Data = [ + * challenge: bstr .size (0..64), + * bstr .cbor CsrPayload, + * ] + * ~~~ + * + * Devices that do not support Provisioning 4.0, or do not support + * Provisioning 4.0 Uploading Option should return + * OEMCrypto_ERROR_NOT_IMPLEMENTED. * * @param[in] challenge: pointer to the buffer containing a byte string to be - * signed. + * signed. It is generated by the RKP factory extraction tool. * @param[in] challenge_length: size of the challenge buffer. * @param[in] encoded_device_info: pointer to the buffer containing the - * serialized device information in CBOR map format. + * serialized device information in CBOR map format. It should be returned as + * `device_info` in a call to the function `OEMCrypto_GetDeviceInformation()`. * @param[in] encoded_device_info_length: size of the encoded_device_info * buffer. * @param[out] signed_csr_payload: pointer to the buffer that receives the - * serialized CSR payload in COSE_Sign1 format. + * serialized signed CSR payload in COSE_Sign1 format. * @param[in,out] signed_csr_payload_length: on input, size of the caller's * signed_csr_payload buffer. On output, the number of bytes written into the * buffer. diff --git a/oemcrypto/include/level3_file_system.h b/oemcrypto/include/level3_file_system.h index 232fbf8..8f8fbc3 100644 --- a/oemcrypto/include/level3_file_system.h +++ b/oemcrypto/include/level3_file_system.h @@ -12,6 +12,7 @@ #define LEVEL3_FILE_SYSTEM_H_ #include +#include "platform.h" namespace wvoec3 { diff --git a/oemcrypto/odk/include/OEMCryptoCENCCommon.h b/oemcrypto/odk/include/OEMCryptoCENCCommon.h index a4e5438..7da8b45 100644 --- a/oemcrypto/odk/include/OEMCryptoCENCCommon.h +++ b/oemcrypto/odk/include/OEMCryptoCENCCommon.h @@ -122,6 +122,7 @@ typedef enum OEMCrypto_Usage_Entry_Status { kInactiveUnused = 4, } OEMCrypto_Usage_Entry_Status; +/* Not used publicly. Not documented with Doxygen. */ typedef enum OEMCrypto_ProvisioningRenewalType { OEMCrypto_NoRenewal = 0, OEMCrypto_RenewalACert = 1, @@ -137,7 +138,9 @@ typedef enum OEMCrypto_LicenseType { OEMCrypto_LicenseType_MaxValue = OEMCrypto_EntitlementLicense, } OEMCrypto_LicenseType; -/* Private key type used in the provisioning response. */ +/** + * Private key type used in the provisioning response. + */ typedef enum OEMCrypto_PrivateKeyType { OEMCrypto_RSA_Private_Key = 0, OEMCrypto_ECC_Private_Key = 1, diff --git a/oemcrypto/odk/include/core_message_features.h b/oemcrypto/odk/include/core_message_features.h index 1365dd8..99566c9 100644 --- a/oemcrypto/odk/include/core_message_features.h +++ b/oemcrypto/odk/include/core_message_features.h @@ -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.3. + // number. The default is 18.4. uint32_t maximum_major_version = 18; - uint32_t maximum_minor_version = 3; + uint32_t maximum_minor_version = 4; bool operator==(const CoreMessageFeatures &other) const; bool operator!=(const CoreMessageFeatures &other) const { diff --git a/oemcrypto/odk/include/odk_structs.h b/oemcrypto/odk/include/odk_structs.h index 1debc12..d97c8ff 100644 --- a/oemcrypto/odk/include/odk_structs.h +++ b/oemcrypto/odk/include/odk_structs.h @@ -16,10 +16,10 @@ extern "C" { /* The version of this library. */ #define ODK_MAJOR_VERSION 18 -#define ODK_MINOR_VERSION 3 +#define ODK_MINOR_VERSION 4 /* ODK Version string. Date changed automatically on each release. */ -#define ODK_RELEASE_DATE "ODK v18.3 2023-07-07" +#define ODK_RELEASE_DATE "ODK v18.4 2023-08-03" /* The lowest version number for an ODK message. */ #define ODK_FIRST_VERSION 16 diff --git a/oemcrypto/odk/src/core_message_features.cpp b/oemcrypto/odk/src/core_message_features.cpp index b8cae56..c8a9c3b 100644 --- a/oemcrypto/odk/src/core_message_features.cpp +++ b/oemcrypto/odk/src/core_message_features.cpp @@ -19,7 +19,9 @@ CoreMessageFeatures CoreMessageFeatures::DefaultFeatures( uint32_t maximum_major_version) { CoreMessageFeatures features; features.maximum_major_version = maximum_major_version; - // The default minor version is the highest for each major version. + // The default minor version is the highest for each major version. This also + // needs to be updated with new version releases in + // ODK_InitializeSessionValues() when the minor version is being set. switch (maximum_major_version) { case 16: features.maximum_minor_version = 5; // 16.5 @@ -28,7 +30,7 @@ CoreMessageFeatures CoreMessageFeatures::DefaultFeatures( features.maximum_minor_version = 2; // 17.2 break; case 18: - features.maximum_minor_version = 3; // 18.3 + features.maximum_minor_version = 4; // 18.4 break; default: features.maximum_minor_version = 0; diff --git a/oemcrypto/odk/src/odk_assert.h b/oemcrypto/odk/src/odk_assert.h index 0517818..e0de19d 100644 --- a/oemcrypto/odk/src/odk_assert.h +++ b/oemcrypto/odk/src/odk_assert.h @@ -9,7 +9,7 @@ extern "C" { #endif -#if (__STDC_VERSION__ >= 201112L) +#if defined(_MSC_VER) || (__STDC_VERSION__ >= 201112L) #include #define odk_static_assert static_assert #else diff --git a/oemcrypto/odk/src/odk_timer.c b/oemcrypto/odk/src/odk_timer.c index 6994532..c837d78 100644 --- a/oemcrypto/odk/src/odk_timer.c +++ b/oemcrypto/odk/src/odk_timer.c @@ -264,7 +264,22 @@ OEMCryptoResult ODK_InitializeSessionValues(ODK_TimerLimits* timer_limits, ODK_InitializeClockValues(clock_values, 0); nonce_values->api_major_version = api_major_version; - nonce_values->api_minor_version = ODK_MINOR_VERSION; + // This needs to be updated with new version releases in the default features + // of core message features. + switch (nonce_values->api_major_version) { + case 16: + nonce_values->api_minor_version = 5; + break; + case 17: + nonce_values->api_minor_version = 2; + break; + case 18: + nonce_values->api_minor_version = 4; + break; + default: + nonce_values->api_minor_version = 0; + break; + } nonce_values->nonce = 0; nonce_values->session_id = session_id; diff --git a/oemcrypto/odk/test/odk_test.cpp b/oemcrypto/odk/test/odk_test.cpp index 71073fc..0c3f92a 100644 --- a/oemcrypto/odk/test/odk_test.cpp +++ b/oemcrypto/odk/test/odk_test.cpp @@ -1216,7 +1216,7 @@ std::vector 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, 3}, + {18, ODK_MAJOR_VERSION, ODK_MINOR_VERSION, 18, 4}, // 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}, @@ -1226,12 +1226,13 @@ std::vector TestCases() { {ODK_MAJOR_VERSION, 18, 1, 18, 1}, {ODK_MAJOR_VERSION, 18, 2, 18, 2}, {ODK_MAJOR_VERSION, 18, 3, 18, 3}, + {ODK_MAJOR_VERSION, 18, 4, 18, 4}, {0, 16, 3, 16, 3}, {0, 16, 4, 16, 4}, {0, 16, 5, 16, 5}, {0, 17, 1, 17, 1}, {0, 17, 2, 17, 2}, - {0, 18, 3, 18, 3}, // Change to 19 when the default version is updated. + {0, 18, 4, 18, 4}, // Change to 19 when the default version is updated. }; return test_cases; } diff --git a/oemcrypto/opk/oemcrypto_ta/oemcrypto.c b/oemcrypto/opk/oemcrypto_ta/oemcrypto.c index 79b5125..40f8a85 100644 --- a/oemcrypto/opk/oemcrypto_ta/oemcrypto.c +++ b/oemcrypto/opk/oemcrypto_ta/oemcrypto.c @@ -26,6 +26,9 @@ #include "oemcrypto_session_type.h" #include "oemcrypto_usage_table.h" #include "wtpi_abort_interface.h" +#ifdef SUPPORT_CAS +# include "wtpi_cas_interface.h" +#endif #include "wtpi_clock_interface_layer1.h" #include "wtpi_config_interface.h" #include "wtpi_crypto_and_key_management_interface_layer1.h" @@ -497,6 +500,13 @@ OEMCryptoResult OEMCrypto_Initialize(void) { strncpy((char*)g_counter_info.chipset_model, XSTR(OPK_CONFIG_SOC_MODEL_NAME), sizeof(g_counter_info.chipset_model)); +#ifdef SUPPORT_CAS + result = WTPI_InitializeCas(); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to initialize CAS with result: %u", result); + goto cleanup; + } +#endif g_opk_system_state = SYSTEM_INITIALIZED; result = OEMCrypto_SUCCESS; @@ -521,6 +531,9 @@ OEMCryptoResult OEMCrypto_Terminate(void) { OEMCryptoResult keybox_result = WTPI_TerminateKeybox(); OEMCryptoResult clock_result = WTPI_TerminateClock(); OEMCryptoResult key_management_result = WTPI_K1_TerminateKeyManagement(); +#ifdef SUPPORT_CAS + OEMCryptoResult cas_terminate_result = WTPI_TerminateCas(); +#endif OEMCryptoResult tee_result = WTPI_Terminate(); g_opk_system_state = SYSTEM_NOT_INITIALIZED; if (tee_result != OEMCrypto_SUCCESS) { @@ -548,6 +561,11 @@ OEMCryptoResult OEMCrypto_Terminate(void) { if (asymmetric_key_terminate_result != OEMCrypto_SUCCESS) { return asymmetric_key_terminate_result; } +#ifdef SUPPORT_CAS + if (cas_terminate_result != OEMCrypto_SUCCESS) { + return cas_terminate_result; + } +#endif return key_terminate_result; } @@ -1509,7 +1527,6 @@ OEMCryptoResult OEMCrypto_LoadEntitledContentKeys( LOGE("Not a entitlement license or no keys found"); return OEMCrypto_ERROR_INVALID_CONTEXT; } - for (size_t i = 0; i < key_array_length; i++) { if (!IsSubstrInRange(message_length, key_array[i].entitlement_key_id, false) || @@ -1526,7 +1543,6 @@ OEMCryptoResult OEMCrypto_LoadEntitledContentKeys( return OEMCrypto_ERROR_INVALID_CONTEXT; } } - for (size_t i = 0; i < key_array_length; i++) { const OEMCrypto_EntitledContentKeyObject key_object = key_array[i]; SymmetricKey* entitlement_key = OPKI_FindKeyFromTable( @@ -2506,6 +2522,13 @@ OEMCryptoResult OEMCrypto_BuildInformation(char* buffer, "\"tee_os_ver\":\"" XSTR(OPK_CONFIG_TEE_OS_VERSION) "\"," "\"form_factor\":\"" XSTR(OPK_CONFIG_DEVICE_FORM_FACTOR) "\"," "\"implementer\":\"" XSTR(OPK_CONFIG_IMPLEMENTER_NAME) "\"," + "\"is_factory_build\":\"" + #ifdef FACTORY_BUILD_ONLY + "true" + #else + "false" + #endif + "\"," "\"fused\":"; // Add fused state @@ -2520,6 +2543,7 @@ OEMCryptoResult OEMCrypto_BuildInformation(char* buffer, static const char kBuildInfo2[] = #if OPK_IS_DEBUG "," + "\"is_debug\":true," "\"opk_config\":" "{" "\"OPK_CONFIG_SECURITY_LEVEL\":\"" @@ -2588,6 +2612,8 @@ OEMCryptoResult OEMCrypto_BuildInformation(char* buffer, XSTR(MAX_ASYMMETRIC_SIGNATURE_SIZE)"\"" "}" #endif + "," + "\"is_debug\":false," "}"; const size_t build_info_length1 = strlen(kBuildInfo1); @@ -2794,21 +2820,17 @@ OEMCryptoResult OEMCrypto_GenerateRSASignature( RETURN_INVALID_CONTEXT_IF_ZERO(message_length); RETURN_INVALID_CONTEXT_IF_NULL(signature_length); - if (padding_scheme == kSign_PKCS1_Block1 && message_length > 83) { + if (padding_scheme != kSign_PKCS1_Block1) { + LOGE("Only PKCS1 block1 padding scheme allowed"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + if (message_length > 83) { LOGE("message_length is too long for the given padding_scheme."); return OEMCrypto_ERROR_SIGNATURE_FAILURE; } - if (session_context->state == SESSION_LOAD_OEM_RSA_KEY && - padding_scheme == kSign_RSASSA_PSS) { - // SignMessageWithOEMPrivateKey handles signature being NULL, so we - // intentionally do not check it here. - result = OEMCrypto_ERROR_NOT_IMPLEMENTED; - // TODO(b/225216277): implement this. - // SignMessageWithOEMPrivateKey(message, message_length, signature, - // signature_length); - if (result != OEMCrypto_SUCCESS) return result; - } else if (session_context->state == SESSION_LOAD_DRM_RSA_KEY) { + if (session_context->state == SESSION_LOAD_DRM_RSA_KEY) { if ((session_context->allowed_schemes & padding_scheme) != padding_scheme) { LOGE("Invalid padding scheme: %u", session_context->allowed_schemes); return OEMCrypto_ERROR_INVALID_KEY; @@ -3788,6 +3810,217 @@ OEMCryptoResult OEMCrypto_ReassociateEntitledKeySession( return OEMCrypto_SUCCESS; } +#ifdef SUPPORT_CAS +OEMCryptoResult OEMCrypto_LoadCasECMKeys( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + const OEMCrypto_EntitledContentKeyObject* even_key, + const OEMCrypto_EntitledContentKeyObject* odd_key) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (OPKI_GetSessionType(session) != SESSION_TYPE_ENTITLED_KEY) { + LOGE("Unexpected session type."); + return OEMCrypto_ERROR_INVALID_ENTITLED_KEY_SESSION; + } + OEMCryptoSession* session_context = NULL; + OEMCryptoEntitledKeySession* key_session = NULL; + OEMCryptoResult result = + GetSessionContext(session, &session_context, &key_session); + if (result != OEMCrypto_SUCCESS) return result; + ABORT_IF(session_context == NULL, + "Failed to get the entitlement session context."); + ABORT_IF(key_session == NULL, + "Failed to get the entitled key session context."); + result = OPKI_CheckStatePreCall(session_context, API_LOADENTITLEDCONTENTKEYS); + if (result != OEMCrypto_SUCCESS) return result; + + RETURN_INVALID_CONTEXT_IF_NULL(message); + RETURN_INVALID_CONTEXT_IF_ZERO(message_length); + + if (!even_key && !odd_key) { + LOGD("No even or odd keys found"); + return OEMCrypto_SUCCESS; + } + OEMCrypto_EntitledContentKeyObject key_array[2]; + memset(key_array, 0, sizeof(OEMCrypto_EntitledContentKeyObject) * 2); + if (even_key && odd_key) { + memcpy(&key_array[0], even_key, sizeof(OEMCrypto_EntitledContentKeyObject)); + memcpy(&key_array[1], odd_key, sizeof(OEMCrypto_EntitledContentKeyObject)); + } else { + if (even_key) { + memcpy(&key_array[0], even_key, + sizeof(OEMCrypto_EntitledContentKeyObject)); + } else if (odd_key) { + memcpy(&key_array[1], odd_key, + sizeof(OEMCrypto_EntitledContentKeyObject)); + } + } + + /* Validate entitled content keys. */ + if (session_context->license_type != OEMCrypto_EntitlementLicense || + session_context->num_entitlement_keys == 0) { + LOGE("Not an entitlement license or no keys found"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + static const size_t kKeyCount = 2; + OEMCrypto_EntitledContentKeyObject empty_key = {0}; + for (size_t i = 0; i < kKeyCount; i++) { + if (memcmp(&key_array[i], &empty_key, sizeof(empty_key)) == 0) continue; + if (!IsSubstrInRange(message_length, key_array[i].entitlement_key_id, + false) || + !IsSubstrInRange(message_length, key_array[i].content_key_id, false) || + !IsSubstrInRange(message_length, key_array[i].content_key_data, + false) || + !IsSubstrInRange(message_length, key_array[i].content_key_data_iv, + false) || + !IsSubstrInRange(message_length, key_array[i].content_iv, true) || + key_array[i].entitlement_key_id.length > KEY_ID_MAX_SIZE || + key_array[i].content_key_id.length > KEY_ID_MAX_SIZE || + key_array[i].content_key_data.length != KEY_SIZE_128 || + key_array[i].content_key_data_iv.length != KEY_IV_SIZE) { + LOGE("Invalid substring range for key index: %zu", i); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + } + + /* Load entitled content keys. */ + for (size_t i = 0; i < kKeyCount; i++) { + /* Skips NULL key. */ + if (memcmp(&key_array[i], &empty_key, sizeof(empty_key)) == 0) continue; + const bool is_even = (i % 2 == 0); + const OEMCrypto_EntitledContentKeyObject key_object = key_array[i]; + SymmetricKey* entitlement_key = OPKI_FindKeyFromTable( + session_context, false, message + key_object.entitlement_key_id.offset, + key_object.entitlement_key_id.length); + if (!OPKI_CheckKey(entitlement_key, ENTITLEMENT_KEY)) { + result = OEMCrypto_KEY_NOT_ENTITLED; + goto cleanup; + } + + /* Initialized the index to which the entitled content key to be loaded. */ + uint32_t entitled_content_key_index = + key_session->num_entitled_content_keys; + /* It prefers to reuse an existing content key index that is entitled by the + * same entitlement key if one exists already, but will allocate a new index + * if there are none that can be reused. + * The block below searches whether there is an existing content key + * entitled by the same entitlement key, with the same parity as the key to + * be loaded, and will reuse the index to load the new content key if there + * is one. */ + for (uint32_t k = 0; k < key_session->num_entitled_content_keys; k++) { + const EntitlementKeyInfo* key_info = &key_session->entitlement_keys[k]; + if (key_info->entitlement_key_id_size == entitlement_key->key_id_size && + memcmp(key_info->entitlement_key_id, entitlement_key->key_id, + key_info->entitlement_key_id_size) == 0 && + is_even == key_session->entitled_content_keys[k]->is_even_key) { + entitled_content_key_index = k; + result = OPKI_FreeKeyFromTable( + &key_session->entitled_content_keys[entitled_content_key_index]); + if (result != OEMCrypto_SUCCESS) goto cleanup; + break; + } + } + + const bool adding_new_content_key = + entitled_content_key_index == key_session->num_entitled_content_keys; + if (adding_new_content_key && + key_session->num_entitled_content_keys == CONTENT_KEYS_PER_SESSION) { + result = OEMCrypto_ERROR_INSUFFICIENT_RESOURCES; + goto cleanup; + } + SymmetricKey** content_key_ptr = + &key_session->entitled_content_keys[entitled_content_key_index]; + result = OPKI_CreateKey(content_key_ptr, CONTENT_KEY, + (KeySize)key_object.content_key_data.length); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to create CONTENT_KEY with result: %u", result); + goto cleanup; + } + SymmetricKey* content_key = *content_key_ptr; + + content_key->cipher_mode = key_object.cipher_mode; + content_key->is_even_key = is_even; + memcpy(content_key->key_id, message + key_object.content_key_id.offset, + key_object.content_key_id.length); + content_key->key_id_size = key_object.content_key_id.length; + content_key->session_key_index = entitled_content_key_index; + content_key->is_entitled_content_key = true; + + /* Associate the new content key with its entitlement key. Fill out + * entitlement key info of the new content key in key session. */ + memcpy(key_session->entitlement_keys[entitled_content_key_index] + .entitlement_key_id, + entitlement_key->key_id, entitlement_key->key_id_size); + key_session->entitlement_keys[entitled_content_key_index] + .entitlement_key_id_size = entitlement_key->key_id_size; + + if (adding_new_content_key) { + key_session->num_entitled_content_keys++; + } + + /* Check key usage before the key is consumed by descrambler. */ + result = + OPKI_CheckKeyControlBlock(&entitlement_key->key_control_block, + /*use_type=*/0, OPK_SECURE_OUTPUT_BUFFER); + if (result != OEMCrypto_SUCCESS) goto cleanup; + + result = WTPI_K1_AESDecryptAndCreateKeyHandle( + entitlement_key->key_handle, + message + key_object.content_key_data.offset, + key_object.content_key_data.length, + message + key_object.content_key_data_iv.offset, CONTENT_KEY, + &content_key->key_handle); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to decrypt and create content key handle with result %u", + result); + goto cleanup; + } + result = WTPI_InstallContentKey(key_session->key_slot_descriptor, + content_key->key_handle, + content_key->cipher_mode, is_even); + /* No need to keep the content key handle since the key is loaded in key + * slot. */ + WTPI_K1_FreeKeyHandle(content_key->key_handle); + content_key->key_handle = NULL; + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to install entitled content key with result %d", result); + goto cleanup; + } + + uint8_t content_iv[KEY_IV_SIZE] = {0}; + const size_t content_iv_length = key_object.content_iv.length > KEY_IV_SIZE + ? KEY_IV_SIZE + : key_object.content_iv.length; + memcpy(content_iv, message + key_object.content_iv.offset, + content_iv_length); + if (content_key->cipher_mode == OEMCrypto_CipherMode_CBC || + content_key->cipher_mode == OEMCrypto_CipherMode_CTR || + content_key->cipher_mode == OEMCrypto_CipherMode_OFB || + content_key->cipher_mode == OEMCrypto_CipherMode_SCTE) { + result = + WTPI_InstallContentIV(key_session->key_slot_descriptor, + &content_iv[0], content_iv_length, is_even); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to install entitled content IV with result %d", result); + goto cleanup; + } + } + } // for loop over key_array + result = OPKI_UpdatePlaybackTimeAndUsageEntryStatus(session_context); + if (result != OEMCrypto_SUCCESS) goto cleanup; + + result = OPKI_SetStatePostCall(session_context, API_LOADENTITLEDCONTENTKEYS); + +cleanup: + if (result != OEMCrypto_SUCCESS) { + FreeEntitledContentKeys(key_session); + FreeContentAndEntitlementKeys(session_context); + session_context->state = SESSION_INVALID; + } + return result; +} +#else OEMCryptoResult OEMCrypto_LoadCasECMKeys( OEMCrypto_SESSION session UNUSED, const uint8_t* message UNUSED, size_t message_length UNUSED, @@ -3795,6 +4028,7 @@ OEMCryptoResult OEMCrypto_LoadCasECMKeys( const OEMCrypto_EntitledContentKeyObject* odd_key UNUSED) { return OEMCrypto_ERROR_NOT_IMPLEMENTED; } +#endif OEMCryptoResult OEMCrypto_ProductionReady(void) { if (g_opk_system_state != SYSTEM_INITIALIZED) { @@ -3926,8 +4160,41 @@ OEMCryptoResult OEMCrypto_GetSignatureHashAlgorithm( return OPKI_GetSessionSignatureHashAlgorithm(session_context, algorithm); } +#ifdef SUPPORT_CAS +OEMCryptoResult OEMCrypto_GetOEMKeyToken(OEMCrypto_SESSION key_session, + uint8_t* key_token, + size_t* key_token_length) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (OPKI_GetSessionType(key_session) != SESSION_TYPE_ENTITLED_KEY) { + LOGE("Unexpected session type."); + return OEMCrypto_ERROR_INVALID_ENTITLED_KEY_SESSION; + } + OEMCryptoEntitledKeySession* key_session_context = NULL; + OEMCryptoResult result = + OPKI_GetEntitledKeySession(key_session, &key_session_context); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to get entitled key session with result: %u, session = %u", + result, key_session); + return result; + } + RETURN_INVALID_CONTEXT_IF_NULL(key_token_length); + const size_t required_length = WTPI_GetKeyTokenSize(); + if (*key_token_length < required_length) { + *key_token_length = required_length; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + RETURN_INVALID_CONTEXT_IF_NULL(key_token); + *key_token_length = required_length; + return WTPI_GetKeyToken(key_session_context->key_slot_descriptor, key_token, + key_token_length); +} +#else OEMCryptoResult OEMCrypto_GetOEMKeyToken(OEMCrypto_SESSION key_session UNUSED, uint8_t* key_token UNUSED, size_t* key_token_length UNUSED) { return OEMCrypto_ERROR_NOT_IMPLEMENTED; } +#endif diff --git a/oemcrypto/opk/oemcrypto_ta/oemcrypto_api_macros.h b/oemcrypto/opk/oemcrypto_ta/oemcrypto_api_macros.h index f178463..969b0e6 100644 --- a/oemcrypto/opk/oemcrypto_ta/oemcrypto_api_macros.h +++ b/oemcrypto/opk/oemcrypto_ta/oemcrypto_api_macros.h @@ -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 3 +#define API_MINOR_VERSION 4 #define OPK_PATCH_VERSION 0 #endif /* OEMCRYPTO_TA_OEMCRYPTO_API_MACROS_H_ */ diff --git a/oemcrypto/opk/oemcrypto_ta/oemcrypto_entitled_key_session.c b/oemcrypto/opk/oemcrypto_ta/oemcrypto_entitled_key_session.c index c0d9036..e451801 100644 --- a/oemcrypto/opk/oemcrypto_ta/oemcrypto_entitled_key_session.c +++ b/oemcrypto/opk/oemcrypto_ta/oemcrypto_entitled_key_session.c @@ -9,6 +9,9 @@ #include "odk_util.h" #include "oemcrypto_check_macros.h" #include "oemcrypto_key_table.h" +#ifdef SUPPORT_CAS +# include "wtpi_cas_interface.h" +#endif OEMCryptoResult OPKI_InitializeEntitledKeySession( OEMCryptoEntitledKeySession* session, OEMCrypto_SESSION key_session_id, @@ -22,7 +25,17 @@ OEMCryptoResult OPKI_InitializeEntitledKeySession( (DecryptHash){ .hash_error = OEMCrypto_SUCCESS, }, +#ifdef SUPPORT_CAS + .key_slot_descriptor = NULL, +#endif }; +#ifdef SUPPORT_CAS + OEMCryptoResult result = WTPI_AllocateKeySlotDescriptor( + key_session_id, &session->key_slot_descriptor); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to allocate key slot descriptor with result: %u", result); + } +#endif return OEMCrypto_SUCCESS; } @@ -35,6 +48,11 @@ OEMCryptoResult OPKI_TerminateEntitledKeySession( OPKI_FreeKeyFromTable(&session->entitled_content_keys[i]); if (result == OEMCrypto_SUCCESS) result = free_key_result; } +#ifdef SUPPORT_CAS + OEMCryptoResult free_key_slot_result = + WTPI_FreeKeySlotDescriptor(session->key_slot_descriptor); + if (result == OEMCrypto_SUCCESS) result = free_key_slot_result; +#endif *session = (OEMCryptoEntitledKeySession){0}; return result; } diff --git a/oemcrypto/opk/oemcrypto_ta/oemcrypto_entitled_key_session.h b/oemcrypto/opk/oemcrypto_ta/oemcrypto_entitled_key_session.h index e61462e..d44b94f 100644 --- a/oemcrypto/opk/oemcrypto_ta/oemcrypto_entitled_key_session.h +++ b/oemcrypto/opk/oemcrypto_ta/oemcrypto_entitled_key_session.h @@ -35,6 +35,11 @@ typedef struct OEMCryptoEntitledKeySession { /* the OEMCrypto session that this entitled key session is associated with. */ OEMCrypto_SESSION entitlement_session_id; DecryptHash decrypt_hash; +#ifdef SUPPORT_CAS + /* CAS only. Contains info of the key slot associated to this key session. The + * interpretation of key slot descriptor can be vendor-specific. */ + void* key_slot_descriptor; +#endif } OEMCryptoEntitledKeySession; /* Initializes entitled key session |session| with id |key_session_id| and the diff --git a/oemcrypto/opk/oemcrypto_ta/oemcrypto_key.c b/oemcrypto/opk/oemcrypto_ta/oemcrypto_key.c index cdf109f..b71b264 100644 --- a/oemcrypto/opk/oemcrypto_ta/oemcrypto_key.c +++ b/oemcrypto/opk/oemcrypto_ta/oemcrypto_key.c @@ -19,6 +19,9 @@ OEMCryptoResult OPKI_InitializeSymmetricKey(SymmetricKey* key, key->key_size = key_size; memset(&key->key_control_block, 0, sizeof(key->key_control_block)); key->is_entitled_content_key = false; +#ifdef SUPPORT_CAS + key->is_even_key = false; +#endif return OEMCrypto_SUCCESS; } diff --git a/oemcrypto/opk/oemcrypto_ta/oemcrypto_key.h b/oemcrypto/opk/oemcrypto_ta/oemcrypto_key.h index f303fcc..129959a 100644 --- a/oemcrypto/opk/oemcrypto_ta/oemcrypto_key.h +++ b/oemcrypto/opk/oemcrypto_ta/oemcrypto_key.h @@ -25,6 +25,10 @@ typedef struct SymmetricKey { uint32_t session_key_index; bool is_entitled_content_key; OEMCryptoCipherMode cipher_mode; +#ifdef SUPPORT_CAS + /* Key parity. CAS only. */ + bool is_even_key; +#endif } SymmetricKey; typedef struct AsymmetricKey { diff --git a/oemcrypto/opk/oemcrypto_ta/oemcrypto_session.c b/oemcrypto/opk/oemcrypto_ta/oemcrypto_session.c index 32bcb0c..1cb0d65 100644 --- a/oemcrypto/opk/oemcrypto_ta/oemcrypto_session.c +++ b/oemcrypto/opk/oemcrypto_ta/oemcrypto_session.c @@ -1189,6 +1189,24 @@ OEMCryptoResult OPKI_EnforceOutputRestrictions(KeyControlBlock control) { return OEMCrypto_SUCCESS; } +OEMCryptoResult OPKI_CheckKeyControlBlock(const KeyControlBlock* control, + uint32_t use_type, + OPK_OutputBuffer_Type buffer_type) { + RETURN_INVALID_CONTEXT_IF_NULL(control); + if (use_type && (!(control->control_bits & use_type))) { + /* Could not use this key for the given use type. */ + LOGE("Could not use this key for the given use type: %u", use_type); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (control->control_bits & CONTROL_DATA_PATH_SECURE) { + if (!WTPI_IsClosedPlatform() && + buffer_type == OPK_CLEAR_INSECURE_OUTPUT_BUFFER) { + return OEMCrypto_ERROR_DECRYPT_FAILED; + } + } + return OEMCrypto_SUCCESS; +} + OEMCryptoResult OPKI_CheckCurrentContentKeyUsage( OEMCryptoSession* session, OEMCryptoEntitledKeySession* key_session, uint32_t use_type, OPK_OutputBuffer_Type buffer_type) { @@ -1214,17 +1232,8 @@ OEMCryptoResult OPKI_CheckCurrentContentKeyUsage( result = OPKI_GetKeyControlBlock(current_content_key, session, key_session, &control); if (result != OEMCrypto_SUCCESS) return result; - if (use_type && (!(control.control_bits & use_type))) { - /* Could not use this key for the given use type. */ - LOGE("Could not use this key for the given use type: %u", use_type); - return OEMCrypto_ERROR_INVALID_CONTEXT; - } - if (control.control_bits & CONTROL_DATA_PATH_SECURE) { - if (!WTPI_IsClosedPlatform() && - buffer_type == OPK_CLEAR_INSECURE_OUTPUT_BUFFER) { - return OEMCrypto_ERROR_DECRYPT_FAILED; - } - } + result = OPKI_CheckKeyControlBlock(&control, use_type, buffer_type); + if (result != OEMCrypto_SUCCESS) return result; return OPKI_EnforceOutputRestrictions(control); } diff --git a/oemcrypto/opk/oemcrypto_ta/oemcrypto_session.h b/oemcrypto/opk/oemcrypto_ta/oemcrypto_session.h index a180c7e..4714159 100644 --- a/oemcrypto/opk/oemcrypto_ta/oemcrypto_session.h +++ b/oemcrypto/opk/oemcrypto_ta/oemcrypto_session.h @@ -325,22 +325,36 @@ NO_IGNORE_RESULT OEMCryptoResult OPKI_GetKeyControlBlock( NO_IGNORE_RESULT OEMCryptoResult OPKI_EnforceOutputRestrictions(KeyControlBlock control); +/* Checks whether |use_type| and |buffer_type| is allowed by the key control + block. Returns OEMCrypto_ERROR_INVALID_CONTEXT if |control| is NULL, or + |use_type| is not allowed by the key control block or if the replay mask is + set in the key control block, or if the license type doesn't match the one in + |key_session|. Returns OEMCrypto_DECRYPT_FAILED if the |buffer_type| is not + allowed on the device, OEMCrypto_ERROR_KEY_EXPIRED if the duration in the key + control block has passed, OEMCrypto_ERROR_INSUFFICIENT_HDCP if the HDCP + requirements are not met, OEMCrypto_ERROR_ANALOG_OUTPUT if the analog display + requirements are not met, and OEMCrypto_SUCCESS otherwise. Caller retains + ownership of |control|. */ +NO_IGNORE_RESULT OEMCryptoResult +OPKI_CheckKeyControlBlock(const KeyControlBlock* control, uint32_t use_type, + OPK_OutputBuffer_Type buffer_type); + /* Checks whether the current key (either the content key selected in the |session| or the entitled content key selected in the |key_session|) can be used in the operation given by |use_type| and with the given |buffer_type|, including whether the output protections required by the key can be enforced. - (e.g. This function is a superset of OPKI_EnforceOutputRestrictions().) - Returns OEMCrypto_ERROR_UNKNOWN_FAILURE if there is no valid current content - key, OEMCrypto_ERROR_INVALID_CONTEXT if |use_type| is not allowed by the - key control block or if the replay mask is set in the key control block, or - if the license type doesn't match the one in |key_session|. Returns - OEMCrypto_DECRYPT_FAILED if the |buffer_type| is not allowed on the device, - OEMCrypto_ERROR_KEY_EXPIRED if the duration in the key control block has - passed, OEMCrypto_ERROR_INSUFFICIENT_HDCP if the HDCP requirements are not - met, OEMCrypto_ERROR_ANALOG_OUTPUT if the analog display requirements are not - met, and OEMCrypto_SUCCESS otherwise. - Caller retains ownership of |session| and it must not be NULL. Caller retains - ownership of |key_session| when it is not NULL. */ + (e.g. This function is a superset of OPKI_EnforceOutputRestrictions() and + OPKI_CheckKeyControlBlock().) Returns OEMCrypto_ERROR_UNKNOWN_FAILURE if + there is no valid current content key, OEMCrypto_ERROR_INVALID_CONTEXT if + |use_type| is not allowed by the key control block or if the replay mask is + set in the key control block, or if the license type doesn't match the one in + |key_session|. Returns OEMCrypto_DECRYPT_FAILED if the |buffer_type| is not + allowed on the device, OEMCrypto_ERROR_KEY_EXPIRED if the duration in the key + control block has passed, OEMCrypto_ERROR_INSUFFICIENT_HDCP if the HDCP + requirements are not met, OEMCrypto_ERROR_ANALOG_OUTPUT if the analog display + requirements are not met, and OEMCrypto_SUCCESS otherwise. Caller retains + ownership of |session| and it must not be NULL. Caller retains ownership of + |key_session| when it is not NULL. */ NO_IGNORE_RESULT OEMCryptoResult OPKI_CheckCurrentContentKeyUsage( OEMCryptoSession* session, OEMCryptoEntitledKeySession* key_session, uint32_t use_type, OPK_OutputBuffer_Type buffer_type); diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_cas_interface.h b/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_cas_interface.h new file mode 100644 index 0000000..a48a451 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_cas_interface.h @@ -0,0 +1,146 @@ +/* Copyright 2023 Google LLC. All Rights Reserved. This file and proprietary + source code may only be used and distributed under the Widevine + License Agreement. */ + +#ifndef OEMCRYPTO_TA_WTPI_CAS_INTERFACE_H_ +#define OEMCRYPTO_TA_WTPI_CAS_INTERFACE_H_ + +#include "OEMCryptoCENC.h" +#include "wtpi_crypto_and_key_management_interface_layer1.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** @defgroup cas CAS + * + * This interface includes functions for CAS only. + * + * If a device is not using CAS, then these functions can return + * OEMCRYPTO_NOT_IMPLEMENTED. + * + * @{ + */ + +/** + * Initializes CAS. + * + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE otherwise + */ +OEMCryptoResult WTPI_InitializeCas(void); + +/** + * Terminates CAS. + * + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE otherwise + */ +OEMCryptoResult WTPI_TerminateCas(void); + +/** + * Allocates a key slot descriptor for |session_id|, and places the result in + * *|key_slot_descriptor|. Key slot descriptor is used to track key slot info, + * and store additional vendor-specific properties of the key slot, if any. The + * implementation of a key slot descriptor is vendor-specific: a. a key slot + * descriptor can directly point to the allocation of a key slot, or b. a key + * slot descriptor is allocated apart from a key slot, and holds a handle to the + * key slot + * + * Caller retains ownership of all pointers. + * + * @param[in] session_id: entitled key session id + * @param[out] key_slot_descriptor: key slot and properties holder + * + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES if key slot descriptor cannot + * be allocated + * @retval OEMCrypto_ERROR_INVALID_CONTEXT if |key_slot_descriptor| is NULL + */ +OEMCryptoResult WTPI_AllocateKeySlotDescriptor(OEMCrypto_SESSION session_id, + void** key_slot_descriptor); + +/** + * Frees key slot descriptor. + * + * Caller retains ownership of all pointers. + * + * @param[in] key_slot_descriptor: key slot and properties holder + * + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE otherwise + */ +OEMCryptoResult WTPI_FreeKeySlotDescriptor(void* key_slot_descriptor); + +/** + * Installs entitled content key to a key slot specified in + * |key_slot_descriptor|. + * + * Caller retains ownership of all pointers. + * + * @param[in] key_slot_descriptor: info of the destination key slot where the + * key is to be installed + * @param[in] key_handle: key to install + * @param[in] cipher_mode: the encryption mode of the media content + * @param[in] is_even: key parity flag + * + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_INVALID_CONTEXT any of the pointers are NULL, or + * |key_handle| is invalid + */ +OEMCryptoResult WTPI_InstallContentKey(void* key_slot_descriptor, + WTPI_K1_SymmetricKey_Handle key_handle, + OEMCryptoCipherMode cipher_mode, + bool is_even); + +/** + * Installs entitled content IV to a key slot specified in + * |key_slot_descriptor|. + * + * Caller retains ownership of all pointers. + * + * @param[in] key_slot_descriptor: info of the destination key slot where the IV + * is to be installed + * @param[in] iv: pointer to the initialization vector to be installed + * @param[in] iv_length: size of the initialization vector + * @param[in] is_even: key parity flag + * + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_INVALID_CONTEXT any of the pointers are NULL + */ +OEMCryptoResult WTPI_InstallContentIV(void* key_slot_descriptor, uint8_t* iv, + size_t iv_length, bool is_even); + +/** + * Get the key token from |key_slot_descriptor|, and places the result + * in |key_token| and sets |key_token_length| to the appropriate length. The key + * token is implementation-specific and is used by the key consumer to locate + * the key slot. It can be as simple as just an index into a shared key table, + * or it can hold more information as required by the key consumer. The key + * token is expected to leave the TEE and be used elsewhere, which is different + * from the key slot descriptor which never leaves the TEE. + * + * Caller retains ownership of all pointers. + * + * @param[in] key_slot_descriptor: key slot info and properties holder + * @param[out] key_token: key token that identifies the key slot + * @param[in,out] key_token_length: length of the key slot, in bytes + * + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_INVALID_CONTEXT any of the pointers are NULL + */ +OEMCryptoResult WTPI_GetKeyToken(void* key_slot_descriptor, uint8_t* key_token, + size_t* key_token_length); + +/** + * Returns the key token length for OEMCrypto_GetOEMKeyToken. + */ +size_t WTPI_GetKeyTokenSize(void); + +/// @} + +#ifdef __cplusplus +} +#endif + +#endif /* OEMCRYPTO_TA_WTPI_CAS_INTERFACE_H_ */ diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_reference/cose_util.c b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/cose_util.c index 59de36e..74431c4 100644 --- a/oemcrypto/opk/oemcrypto_ta/wtpi_reference/cose_util.c +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/cose_util.c @@ -141,12 +141,10 @@ static DiceResult DiceCoseEncodePublicKey(const uint8_t* public_key, switch (key_type) { case PROV40_ED25519_PRIVATE_KEY: - LOGE("first one"); return DiceCoseEncodePublicKeyEd25519(public_key, public_key_size, buffer_size, buffer, encoded_size); case DRM_ECC_PRIVATE_KEY: - LOGE("second one"); return DiceCoseEncodePublicKeyECDSA(public_key, public_key_size, buffer_size, buffer, encoded_size); default: diff --git a/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/util/odk_endian.c b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/odk_endian.c similarity index 100% rename from oemcrypto/opk/ports/optee/ta/common/wtpi_impl/util/odk_endian.c rename to oemcrypto/opk/oemcrypto_ta/wtpi_reference/odk_endian.c diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_clock_and_gn_layer1.c b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_clock_and_gn_layer1.c index 3265da3..59dcdce 100644 --- a/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_clock_and_gn_layer1.c +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_clock_and_gn_layer1.c @@ -66,10 +66,12 @@ static OEMCryptoResult InitializeDelta(void) { } static OEMCryptoResult InitializeData(void) { - gInitialized = true; size_t data_length = PERSISTENT_DATA_SIZE; uint8_t buffer[PERSISTENT_DATA_SIZE]; OEMCryptoResult status = WTPI_LoadPersistentData(buffer, &data_length); + if (status != OEMCrypto_SUCCESS && status != OPK_ERROR_NO_PERSISTENT_DATA) { + return status; + } uint8_t version_number = buffer[0]; if (status == OPK_ERROR_NO_PERSISTENT_DATA || data_length != PERSISTENT_DATA_SIZE || @@ -77,13 +79,14 @@ static OEMCryptoResult InitializeData(void) { // Note: In the future, we may wish to handle older version numbers. // This is the first initialization. Start the generation number at random // value and the clock at 0. + LOGD("New random generation number."); status = WTPI_C1_RandomBytes((uint8_t*)(&gSavedGenerationNumber), sizeof(uint64_t)); - LOGD("New random generation number."); if (status != OEMCrypto_SUCCESS) return status; gClockDelta = 0; gLastSaveTime = 0; status = WTPI_GetSecureTimer(&gLastTime); + if (status != OEMCrypto_SUCCESS) return status; // Some duration logic assumes a clock value of 0 can never happen, so let's // pick a minimum clock value. const uint64_t kMinimumClock = 10; @@ -92,7 +95,6 @@ static OEMCryptoResult InitializeData(void) { gClockDelta += kMinimumClock; gLastSaveTime += kMinimumClock; } - return status; } else { WTPI_PersistentData data; memcpy(&data, buffer + 1, sizeof(data)); @@ -101,8 +103,10 @@ static OEMCryptoResult InitializeData(void) { gSavedWallClock = data.previous_wall_clock; gLastSaveTime = gLastTime; status = InitializeDelta(); - return status; + if (status != OEMCrypto_SUCCESS) return status; } + gInitialized = true; + return OEMCrypto_SUCCESS; } static OEMCryptoResult SaveData(void) { @@ -181,6 +185,7 @@ OEMCryptoResult WTPI_GetTrustedTime(uint64_t* time_in_s) { } uint64_t hw_timer = 0; status = WTPI_GetSecureTimer(&hw_timer); + if (status != OEMCrypto_SUCCESS) return status; uint64_t now = hw_timer + gClockDelta; // If the hardware clock goes backwards, or the clock delta has not been // initialized by the saved wall clock, then now might be less than the last @@ -197,7 +202,8 @@ OEMCryptoResult WTPI_GetTrustedTime(uint64_t* time_in_s) { } *time_in_s = now; if (now > gLastSaveTime + PERIODIC_SAVE_TIME) { - SaveData(); + status = SaveData(); + if (status != OEMCrypto_SUCCESS) return status; } - return status; + return OEMCrypto_SUCCESS; } diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_crypto_and_key_management_layer1_openssl.c b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_crypto_and_key_management_layer1_openssl.c index 61b86ea..0dfce14 100644 --- a/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_crypto_and_key_management_layer1_openssl.c +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_crypto_and_key_management_layer1_openssl.c @@ -638,3 +638,17 @@ OEMCryptoResult WTPI_K1_PrepareExternalKeyHandle( size_t* out_buffer_length) { return OEMCrypto_ERROR_NOT_IMPLEMENTED; } + +OEMCryptoResult WTPI_K1_CopyKeyHandle(WTPI_K1_SymmetricKey_Handle key, + WTPI_K1_SymmetricKey_Handle* out) { + if (!IsKeyHandleValid(key)) return OEMCrypto_ERROR_INVALID_CONTEXT; + OEMCryptoResult result = PrepareCachedKey(key); + if (result != OEMCrypto_SUCCESS) return result; + SymmetricKeyType type; + result = GetKeyType(key, &type); + if (result != OEMCrypto_SUCCESS) return result; + KeySize key_size; + result = WTPI_K1_GetKeySize(key, &key_size); + if (result != OEMCrypto_SUCCESS) return result; + return WTPI_K1_CreateKeyHandle(key->cached_key, key_size, type, out); +} diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_useless/wtpi_cas.c b/oemcrypto/opk/oemcrypto_ta/wtpi_useless/wtpi_cas.c new file mode 100644 index 0000000..930ca25 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_useless/wtpi_cas.c @@ -0,0 +1,51 @@ +/* Copyright 2023 Google LLC. All Rights Reserved. This file and proprietary + source code may only be used and distributed under the Widevine License + Agreement. */ + +#include "wtpi_cas_interface.h" + +OEMCryptoResult WTPI_InitializeCas(void) { + // TODO + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult WTPI_TerminateCas(void) { + // TODO + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult WTPI_AllocateKeySlotDescriptor(OEMCrypto_SESSION session_id, + void** key_slot_descriptor) { + // TODO + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult WTPI_FreeKeySlotDescriptor(void* key_slot_descriptor) { + // TODO + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult WTPI_InstallContentKey(void* key_slot_descriptor, + WTPI_K1_SymmetricKey_Handle key_handle, + OEMCryptoCipherMode cipher_mode, + bool is_even) { + // TODO + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult WTPI_InstallContentIV(void* key_slot_descriptor, uint8_t* iv, + size_t iv_length, bool is_even) { + // TODO + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult WTPI_GetKeyToken(void* key_slot_descriptor, uint8_t* key_token, + size_t* key_token_length) { + // TODO + return OEMCrypto_SUCCESS; +} + +size_t WTPI_GetKeyTokenSize(void) { + // TODO + return 0; +} diff --git a/oemcrypto/opk/ports/linux/cas/Makefile b/oemcrypto/opk/ports/linux/cas/Makefile new file mode 100644 index 0000000..c92e5a9 --- /dev/null +++ b/oemcrypto/opk/ports/linux/cas/Makefile @@ -0,0 +1,53 @@ +# +# Copyright 2022 Google LLC. All Rights Reserved. This file and proprietary +# source code may only be used and distributed under the Widevine +# License Agreement. +# + +# $CDM_DIR must be defined as the path to the top level of the OPK release +ifndef CDM_DIR + $(error CDM_DIR is undefined) +endif + +.EXPORT_ALL_VARIABLES: + +ARCH := 64 +IS_ARM := 0 + +.PHONY: all +all: ree tee + +.PHONY: ree +ree: hello_world cas_unittests + +.PHONY: tee +tee: tee_simulator_cas + +.PHONY: liboemcrypto +liboemcrypto: + +$(MAKE) -C ree/liboemcrypto + +.PHONY: libtuner +libtuner: + +$(MAKE) -C ree/libtuner + +.PHONY: hello_world +hello_world: liboemcrypto libtuner + +$(MAKE) -C ree/hello_world + +.PHONY: cas_unittests +cas_unittests: liboemcrypto libtuner + +$(MAKE) -C ree/cas_unittests + +.PHONY: tee_simulator_cas +tee_simulator_cas: + +$(MAKE) -C tee/tee_simulator_cas + +.PHONY: clean +clean: + +$(MAKE) -C tee/tee_simulator_cas clean + +$(MAKE) -C ree/liboemcrypto clean + +$(MAKE) -C ree/libtuner clean + +$(MAKE) -C ree/hello_world clean + +$(MAKE) -C ree/cas_unittests clean + diff --git a/oemcrypto/opk/ports/linux/cas/README.md b/oemcrypto/opk/ports/linux/cas/README.md new file mode 100644 index 0000000..1d23708 --- /dev/null +++ b/oemcrypto/opk/ports/linux/cas/README.md @@ -0,0 +1,227 @@ +# OEMCrypto CAS demo on Linux + +## Quick Start + +to build: `CDM_DIR= ./scripts/build.sh` +to run: `CDM_DIR= ./scripts/run.sh` + +Can set CDM_DIR as a global variable, or inject every time you run. + +To build manually, call `CDM_DIR= make liboemcrypto -j32` +from this directory. Feel free to replace `liboemcrypto` with whichever make +recipe you want. + +## Overview + +This is a Linux-based demo of the OEMCrypto CAS functionality in the OPK. The +demo itself is very narrow in scope, and is not suitable for production without +significant modification. + +In particular, the "TA" portion runs as a separate process in the REE for ease +of debugging and inspection. At the very least, this code must be ported to a +TEE in order to run in a secure manner. + +This project demonstrates the connection from REE (liboemcrypto.so and +libtuner.so) to the TA (tee_simulator_cas) to call OEMCrypto functions +and the Tuner descrambler together. + +## Design + +This CAS demo is a modified version of the Linux OEMCrypto port +(oemcrypto/opk/ports/linux). It generally follows the same design as that port, +such as choosing to run the TA code in a separate Linux process rather than a +TEE. + +The CAS demo builds three types of output: + +1. _App level_ (hello_world, cas_unittests). These call OEMCrypto and Tuner +functions directly. A real system would use plugins, CDM, etc. +2. _Libraries_ (liboemcrypto.so and libtuner.so). These serialize the OEMCrypto +and Tuner function calls into message bytes to send to the TA. +3. _TA_ (tee_simulator_cas). This is a single application that combines all code +for message deserialization, OEMCrypto function execution, Tuner descrambling, +and data sharing for CAS ECM keys. Because it is a single binary, the shared +data can exist as global variables or statically linked functions. A real +system would likely separate the OEMCrypto, descrambling, and shared data into +individual trusted apps. + +The app and libraries are expected to run in a REE no matter what. The "TA" +portion should realistically run in a TEE, but for the purposes of this demo +all TA code runs in a separate Linux process. This avoids the need to support +TEE-specific communication protocols -- instead, the two Linux apps use POSIX +shared mem and semaphores to pass messages back and forth. + +### Apps + +hello_world calls basic OEMCrypto and Tuner functions to prove that the +serialization and deserialization paths work and can communicate with the +tee_simulator_cas process. + +cas_unittests is intended to run the expected OEMCrypto setup functions before +calling Tuner_Decrypt() on real content. However, this is incomplete until we +can cleanly extract sample content and matching entitlement keys for a local +test (no network access). + +### Libraries + +liboemcrypto.so is almost the exact same as an OEMCrypto implementation for a +DRM-only (not CAS) target. Like a typical DRM use case, this serializes all +OEMCrypto functions for sending to the TEE. The one minor difference is the +addition of a CAS-specific field in some data structures. + +libtuner.so is new, specific to this demo. It uses the same serialization +scheme as liboemcrypto.so, but for a single Decrypt() function. In a real +system this might not be necessary at all, as a platform-specific descrambler +can take its place. The `tuner_ree_serialization` files provide the REE-side +serialization code used in this library, while the *_tee_* equivalent is used +in the TA. + +### TA + +The TA code runs in a single application called `tee_simulator_cas`. This is +similar to the name of the Linux OPK port, which is "tee simulator". This +application runs in Linux userspace as a while loop, watching when POSIX +shared memory contains messages from liboemcrypto.so and libtuner.so. Then those +messages are deserialized and executed as either OEMCrypto or Tuner functions. + +Apart from the while loop and POSIX resource management, the rest of the code +is in C and should be portable to any platform-specific use case with the OPK. + +The while loop and POSIX messaging scheme is not intended to be used on a real +device. It is a shortcut to run easily on Linux. + +One limitation of having all TA code run in a single application is that +incoming messages are received in a single thread. So liboemcrypto.so and +libtuner.so functions can be called from the app in multiple threads, but they +will be received and executed sequentially, potentially blocking the calling +threads until previous function execution is complete. This is a limitation of +the OPK C functions used in message deserialization and temporary memory +allocation -- both OEMCrypto and Tuner deserialization paths have to use the +same statically allocated resouces in this case, which we cannot easily thread +proof. + +## How to modify this demo for a real world use case + +This demo cuts corners in a few ways and is not usable as-is for production. A +few modifications are necessary: + +1. Separation of OEMCrypto, Descrambler, and data share in TEE. Add platform- +specific TA entry points for each. +2. Completely rewrite data_share_impl.c to reflect platform-specific key +descriptors, key tokens, key table, and concurrency. Possible changes to +wtpi_cas.c to match. +3. Replace libtuner.so and Tuner_Decrypt() with actual tuner integration if +applicable. + +More details in sections below + +### Move OEMCrypto, Tuner, data share to TEE + +A real system would likely have completely separate OEMCrypto, Tuner, and data +share portions (3 parts) running in a TEE. In order to access the shared data, +TEE-specific mechanisms would need to be used. Using the code in this demo as a +reference, those three parts would be divided roughly as follows: + +1. OEMCrypto: all `opk_base_ta_sources`, `wtpi_impl_sources`, +`boringssl_sources`, and `dice_sources` for a typical OEMCrypto build. Also +`wtpi_cas.c` to access shared_data. +2. Tuner: `tuner_impl.c`, `tuner_tee_serialization.c` +3. Data share: `data_share_impl.c` + +The source code in each portion above will need to be modified to access the +shared data in a TEE-specific way. + +For example, running in OP-TEE would require the shared data to be a separate +TA. OEMCrypto would call the wtpi_cas functions to access the data, and those +implementations would use TEE_OpenTASession()/TEE_InvokeTACommand()/ +TA_CloseTASession() GlobalPlatform methods to access that TA. The Tuner would +use the same functions to acess the shared data from its side. + +Similarly, the `tee_simulator_cas/tee_simulator.cpp` file would need to be +completely replaced with TEE-specific entry points for the OEMCrypto and Tuner +portions. + +A Tuner TA using these files might not be necessary, as it might already exist +on some platforms. + +### More secure and portable Key Tokens + +The data returned by OEMCrypto_GetOEMKeyToken() is visible to the REE. As such, +it should not convey any information about the underlying key data, and should +be generally tamperproof. The demo implementation here only uses the key table +index and does not check for tampering. + +In particular, the functions in `data_share.h` will need updated implementations +to match any new key token and key descriptor design. + +This demo leverages the OEMCrypto code to take advantage of the common key +handle creation and management. Since the TA code executes in the same binary, +an OEMCrypto key handle can be directly used in the Tuner decrypt function. If +everything is separated, the key handle may not be directly shareable the way +it is in this demo. + +### Possible changes to data share mutex + +The demo uses a data share with a global mutex to guard against simultaneous +reads and writes. Some TEEs may not be able to use the same POSIX mutex +structures, and may need to rely on other mechanisms. Some TEE's already allow +only one TA accessor at a time, effectively accomplishing the same end goal as a +mutex. + +### REE/TEE communication + +The current serialization interface uses POSIX shared memory to communicate +between the REE and TEE. This will need to be changed to a TEE-specific +mechanism. In particular, the TOS functions (the files under +`oemcrypto/opk/ports/linux/common/`) will need to be modified. See the OP-TEE +port or Trusty port `tos_` files for example implementations of +this REE/TEE communication layer in a real world example. + +### Integration with more realistic REE code + +This demo uses Google Test on the REE to call the OEMCrypto and Tuner functions. +This is not realistic. The MediaCAS plugin would be a better exerciser of the +code, either in VTS tests or other high-level integration suite. + +### Improved data share management + +This demo uses a rudimentary data share implementation that only writes more +information without freeing up unused slots. None of the shared key data from +OEMCrypto will be free'd, so eventually the key table will run out of space, +even when sessions are closed. An improved implementation would use +WTPI_FreeKeySlotDescriptor() to clean up a slot on session close. + +## Folder structure + +oemcrypto/opk/ports/linux/cas +├── Makefile +├── README.md +├── ree +│   ├── cas_unittests +│   │   ├── cas_tests.cpp +│   │   └── Makefile +│   ├── hello_world +│   │   ├── main.c +│   │   └── Makefile +│   ├── liboemcrypto +│   │   └── Makefile +│   ├── libtuner +│   │   └── Makefile +│   ├── tuner_ree_serialization.c +│   └── tuner_ree_serialization.h +├── scripts +│   ├── build.sh +│   └── run.sh +├── tee +│   ├── tee_simulator_cas +│   │   ├── data_share.h +│   │   ├── data_share_impl.c +│   │   ├── Makefile +│   │   ├── tee_simulator.cpp +│   │   ├── tuner_impl.c +│   │   └── wtpi_cas.c +│   ├── tuner_tee_serialization.c +│   └── tuner_tee_serialization.h +└── tuner_hal.h + +9 directories, 21 files diff --git a/oemcrypto/opk/ports/linux/cas/ree/cas_unittests/Makefile b/oemcrypto/opk/ports/linux/cas/ree/cas_unittests/Makefile new file mode 100644 index 0000000..04ca79e --- /dev/null +++ b/oemcrypto/opk/ports/linux/cas/ree/cas_unittests/Makefile @@ -0,0 +1,41 @@ +# +# Copyright 2023 Google LLC. All Rights Reserved. This file and proprietary +# source code may only be used and distributed under the Widevine +# License Agreement. +# + +# This file expects the following definitions +# - CDM_DIR: absolute path to top of CDM repo. + +# Place outputs in $CDM_DIR/out/linux/cas// +project := $(shell basename $(CURDIR)) +srcdir := $(shell realpath --relative-to=$(CURDIR) $(CDM_DIR)) +builddir := $(srcdir)/out/linux/cas/$(project)/ +output = $(project) + +# All file locations are relative to the $CDM_DIR path. +OPK_REPO_TOP := +include $(srcdir)/oemcrypto/opk/build/ree-sources.mk + +srcs += \ + $(oemcrypto_unittests_sources) \ + +incs += \ + $(oemcrypto_unittests_includes) \ + $(OPK_REPO_TOP)/oemcrypto/opk/ports/linux/cas \ + $(OPK_REPO_TOP)/oemcrypto/opk/ports/linux/cas/ree \ + +ldflags = \ + -lrt -ldl \ + -L$(builddir)/../liboemcrypto/ -loemcrypto \ + -L$(builddir)/../libtuner/ -ltuner \ + -static-libstdc++ \ + +cppflags += \ + -DOPENSSL_NO_ASM \ + -Wnon-virtual-dtor \ + -DCAS_TEST \ + +include ../../../rules.mk + + diff --git a/oemcrypto/opk/ports/linux/cas/ree/hello_world/Makefile b/oemcrypto/opk/ports/linux/cas/ree/hello_world/Makefile new file mode 100644 index 0000000..7d22b9e --- /dev/null +++ b/oemcrypto/opk/ports/linux/cas/ree/hello_world/Makefile @@ -0,0 +1,35 @@ +# +# Copyright 2023 Google LLC. All Rights Reserved. This file and proprietary +# source code may only be used and distributed under the Widevine +# License Agreement. +# + +# This file expects the following definitions +# - CDM_DIR: absolute path to top of CDM repo. + +# Place outputs in $CDM_DIR/out/linux/cas// +project := $(shell basename $(CURDIR)) +srcdir := $(shell realpath --relative-to=$(CURDIR) $(CDM_DIR)) +builddir := $(srcdir)/out/linux/cas/$(project)/ +output = $(project) + +# All file locations are relative to the $CDM_DIR path. +OPK_REPO_TOP := +include $(srcdir)/oemcrypto/opk/build/ree-sources.mk + +srcs += \ + $(OPK_REPO_TOP)/oemcrypto/opk/ports/linux/cas/ree/hello_world/main.c \ + +incs += \ + $(OPK_REPO_TOP)/oemcrypto/include \ + $(OPK_REPO_TOP)/oemcrypto/opk/ports/linux/cas \ + $(OPK_REPO_TOP)/oemcrypto/opk/ports/linux/cas/ree \ + +ldflags = \ + -lrt -ldl \ + -L$(builddir)/../liboemcrypto/ -loemcrypto \ + -L$(builddir)/../libtuner/ -ltuner \ + +include ../../../rules.mk + + diff --git a/oemcrypto/opk/ports/linux/cas/ree/hello_world/main.c b/oemcrypto/opk/ports/linux/cas/ree/hello_world/main.c new file mode 100644 index 0000000..87ef32f --- /dev/null +++ b/oemcrypto/opk/ports/linux/cas/ree/hello_world/main.c @@ -0,0 +1,31 @@ +/* + * Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary + * source code may only be used and distributed under the Widevine + * License Agreement. + */ + +#include +#include "OEMCryptoCENC.h" +#include "tuner_hal.h" + +int main(void) { + printf("Hello world\n"); + + OEMCryptoResult res; + res = OEMCrypto_Initialize(); + printf("OEMCrypto_Initialize() returned %d\n", res); + + // TODO: get expected call hierarchy for example usage + // OEMCrypto_CreateEntitledKeySession() + // OEMCrypto_LoadKeys() + // OEMCrypto_LoadCasECMKeys() + // OEMCrypto_GetOEMKeyToken() + + // TODO: populate with test data + TunerHalResult res2 = TunerHal_Decrypt(NULL, 0, 0, NULL, 0, NULL); + printf("TunerHal_Decrypt() returned %d\n", res2); + + OEMCrypto_Terminate(); + + return 0; +} diff --git a/oemcrypto/opk/ports/linux/cas/ree/liboemcrypto/Makefile b/oemcrypto/opk/ports/linux/cas/ree/liboemcrypto/Makefile new file mode 100644 index 0000000..3d5c0d1 --- /dev/null +++ b/oemcrypto/opk/ports/linux/cas/ree/liboemcrypto/Makefile @@ -0,0 +1,39 @@ +# +# Copyright 2023 Google LLC. All Rights Reserved. This file and proprietary +# source code may only be used and distributed under the Widevine +# License Agreement. +# + +# This file expects the following definitions +# - CDM_DIR: absolute path to top of CDM repo. + +# Place outputs in $CDM_DIR/out/linux/cas// +project := $(shell basename $(CURDIR)) +srcdir := $(shell realpath --relative-to=$(CURDIR) $(CDM_DIR)) +builddir := $(srcdir)/out/linux/cas/$(project)/ +output = $(project).so + +# All file locations are relative to the $CDM_DIR path. +OPK_REPO_TOP := +include $(CDM_DIR)/oemcrypto/opk/build/ree-sources.mk + +srcs += \ + $(OPK_REPO_TOP)/oemcrypto/opk/ports/linux/host/liboemcrypto/load_library.cpp \ + $(OPK_REPO_TOP)/oemcrypto/opk/ports/linux/common/tos_transport.cpp \ + $(OPK_REPO_TOP)/oemcrypto/opk/ports/linux/common/tos_secure_buffers.c \ + $(OPK_REPO_TOP)/oemcrypto/opk/ports/linux/common/tos_logging.cpp \ + $(OPK_REPO_TOP)/oemcrypto/opk/ports/linux/common/tos_shared_memory.cpp \ + $(liboemcrypto_sources) \ + +incs += \ + $(OPK_REPO_TOP)/oemcrypto/opk/ports/linux/ta/oemcrypto_ta/include \ + $(OPK_REPO_TOP)/oemcrypto/opk/ports/linux/host/liboemcrypto \ + $(liboemcrypto_includes) \ + +cflags += \ + -DWV_POSIX_RESOURCE_ID=\"tee_simulator_cas\" \ + +ldflags = -lpthread -shared -lrt + +include ../../../rules.mk + diff --git a/oemcrypto/opk/ports/linux/cas/ree/libtuner/Makefile b/oemcrypto/opk/ports/linux/cas/ree/libtuner/Makefile new file mode 100644 index 0000000..4c85ccd --- /dev/null +++ b/oemcrypto/opk/ports/linux/cas/ree/libtuner/Makefile @@ -0,0 +1,43 @@ +# +# Copyright 2023 Google LLC. All Rights Reserved. This file and proprietary +# source code may only be used and distributed under the Widevine +# License Agreement. +# + +# This file expects the following definitions +# - CDM_DIR: absolute path to top of CDM repo. + +# Place outputs in $CDM_DIR/out/linux/cas// +project := $(shell basename $(CURDIR)) +srcdir := $(shell realpath --relative-to=$(CURDIR) $(CDM_DIR)) +builddir := $(srcdir)/out/linux/cas/$(project)/ +output = $(project).so + +# All file locations are relative to the $CDM_DIR path. +OPK_REPO_TOP := +include $(CDM_DIR)/oemcrypto/opk/build/ree-sources.mk + +srcs += \ + $(OPK_REPO_TOP)/oemcrypto/opk/ports/linux/common/tos_transport.cpp \ + $(OPK_REPO_TOP)/oemcrypto/opk/ports/linux/common/tos_secure_buffers.c \ + $(OPK_REPO_TOP)/oemcrypto/opk/ports/linux/common/tos_logging.cpp \ + $(OPK_REPO_TOP)/oemcrypto/opk/ports/linux/common/tos_shared_memory.cpp \ + $(OPK_REPO_TOP)/oemcrypto/opk/ports/linux/cas/ree/tuner_ree_serialization.c \ + $(liboemcrypto_sources) \ + $(OPK_REPO_TOP)/oemcrypto/opk/ports/linux/host/liboemcrypto/load_library.cpp \ + +incs += \ + $(OPK_REPO_TOP)/oemcrypto/opk/ports/linux/cas/ \ + $(liboemcrypto_includes) \ + $(oemcrypto_dir)/opk/oemcrypto_ta \ + $(oemcrypto_dir)/include \ + +# Here the liboemcrypto.so and libtuner.so transport layers share the same +# destination, which is a POSIX shm with the tag "tee_simulator_cas" +cflags += \ + -DWV_POSIX_RESOURCE_ID=\"tee_simulator_cas\" \ + +ldflags = -lpthread -shared -lrt + +include ../../../rules.mk + diff --git a/oemcrypto/opk/ports/linux/cas/ree/tuner_ree_serialization.c b/oemcrypto/opk/ports/linux/cas/ree/tuner_ree_serialization.c new file mode 100644 index 0000000..3e3bc17 --- /dev/null +++ b/oemcrypto/opk/ports/linux/cas/ree/tuner_ree_serialization.c @@ -0,0 +1,135 @@ + +/* + * Copyright 2023 Google LLC. All Rights Reserved. This file and proprietary + * source code may only be used and distributed under the Widevine License + * Agreement. + */ + +#include +#include +#include + +#include "GEN_common_serializer.h" +#include "GEN_ree_serializer.h" +#include "OEMCryptoCENC.h" +#include "api_support.h" +#include "bump_allocator.h" +#include "log_macros.h" +#include "message_debug.h" +#include "ree_special_cases.h" +#include "ree_version.h" +#include "shared_buffer_allocator.h" +#include "tos_shared_memory_interface.h" +#include "tos_transport_interface.h" +#include "tuner_ree_serialization.h" + +#define TUNER_HAL_DECRYPT_API_VALUE 12345 + +TunerHalResult TunerHal_Decrypt( + const uint8_t* key_token, + size_t key_token_length, + KeyParityType key_parity, + const OEMCrypto_SampleDescription* samples, // an array of samples. + size_t samples_length, // the number of samples. + const OEMCrypto_CENCEncryptPatternDesc* pattern) { + pthread_mutex_lock(&api_lock); + TunerHalResult result = TUNER_HAL_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = + // OPK_Pack_TunerHal_Decrypt_Request(session, samples, samples_length, pattern); + OPK_Pack_TunerHal_Decrypt_Request(key_token, key_token_length, key_parity, samples, samples_length, pattern); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + if (ODK_Message_GetStatus(&request) == MESSAGE_STATUS_BUFFER_TOO_LARGE) { + api_result = TUNER_HAL_ERROR_BUFFER_TOO_LARGE; + } else { + api_result = TUNER_HAL_ERROR_UNKNOWN_FAILURE; + } + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_TunerHal_Decrypt_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = TUNER_HAL_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +ODK_Message OPK_Pack_TunerHal_Decrypt_Request( + const uint8_t* key_token, + size_t key_token_length, + KeyParityType key_parity, + const OEMCrypto_SampleDescription* samples, // an array of samples. + size_t samples_length, // the number of samples. + const OEMCrypto_CENCEncryptPatternDesc* pattern) { + uint32_t api_value = + TUNER_HAL_DECRYPT_API_VALUE; // TODO: how will this interact on the TEE + // side? + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + + OPK_Pack_size_t(&msg, &key_token_length); + OPK_PackMemory(&msg, (const uint8_t*)key_token, + OPK_ToLengthType(key_token_length)); + + OPK_Pack_uint32_t(&msg, &key_parity); + + OPK_Pack_size_t(&msg, &samples_length); + /* pack object array with packer function OPK_Pack_OEMCrypto_SampleDescription + */ + ODK_Message* const odk_message = &msg; + const void* const objs = (const void*)samples; + const LengthType count = OPK_ToLengthType(samples_length); + const size_t size = sizeof(OEMCrypto_SampleDescription); + const bool is_null = objs == NULL || OPK_LengthIsNull(count); + if (!OPK_PackBoolValue(odk_message, is_null)) { + for (size_t i = 0; i < OPK_ToSizeT(count); i++) { + OPK_Pack_OEMCrypto_SampleDescription( + odk_message, (const OEMCrypto_SampleDescription*)(objs + i * size)); + } + } + OPK_PackNullable_OEMCrypto_CENCEncryptPatternDesc(&msg, pattern); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_TunerHal_Decrypt_Response(ODK_Message* msg, + TunerHalResult* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != TUNER_HAL_DECRYPT_API_VALUE) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_TunerHalResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +bool Is_Valid_TunerHalResult(uint32_t value) { + switch (value) { + case 0: /* TUNER_HAL_SUCCESS */ + case 1: /* TUNER_HAL_ERROR_UNKNOWN_FAILURE */ + case 2: /* TUNER_HAL_ERROR_BUFFER_TOO_LARGE */ + case 3: /* TUNER_HAL_ERROR_NOT_IMPLEMENTED */ + + return true; + default: + return false; + } +} \ No newline at end of file diff --git a/oemcrypto/opk/ports/linux/cas/ree/tuner_ree_serialization.h b/oemcrypto/opk/ports/linux/cas/ree/tuner_ree_serialization.h new file mode 100644 index 0000000..8d3bfd8 --- /dev/null +++ b/oemcrypto/opk/ports/linux/cas/ree/tuner_ree_serialization.h @@ -0,0 +1,17 @@ + +#ifndef _TUNER_REE_SERIALIZATION_H_ +#define _TUNER_REE_SERIALIZATION_H_ + +#include "tuner_hal.h" + +ODK_Message OPK_Pack_TunerHal_Decrypt_Request( + const uint8_t* key_token, size_t key_token_length, KeyParityType key_parity, + const OEMCrypto_SampleDescription* samples, size_t samples_length, + const OEMCrypto_CENCEncryptPatternDesc* pattern); + +void OPK_Unpack_TunerHal_Decrypt_Response(ODK_Message* msg, + TunerHalResult* result); + +bool Is_Valid_TunerHalResult(uint32_t value); + +#endif \ No newline at end of file diff --git a/oemcrypto/opk/ports/linux/cas/scripts/build.sh b/oemcrypto/opk/ports/linux/cas/scripts/build.sh new file mode 100755 index 0000000..a25eb06 --- /dev/null +++ b/oemcrypto/opk/ports/linux/cas/scripts/build.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# Builds OEMCrypto CAS demo test file and TA simulator + +if [ -z "$CDM_DIR" ]; then + echo "CDM_DIR must be set to the top of the repo" + exit +fi + +# relative to CDM_DIR +PROJECT_DIR=./oemcrypto/opk/ports/linux/cas + +build() { + pushd $CDM_DIR + make -j$(nproc) -C $PROJECT_DIR cas_unittests hello_world tee_simulator_cas $1 + popd +} + +build PROVISIONING_METHOD=OEMCrypto_Keybox + +# build PROVISIONING_METHOD=OEMCrypto_BootCertificateChain + diff --git a/oemcrypto/opk/ports/linux/cas/scripts/run.sh b/oemcrypto/opk/ports/linux/cas/scripts/run.sh new file mode 100755 index 0000000..3e296f8 --- /dev/null +++ b/oemcrypto/opk/ports/linux/cas/scripts/run.sh @@ -0,0 +1,43 @@ +#!/bin/bash +# Runs OEMCrypto CAS demo test +# +# Race conditions sometimes occur, causing the executables to hang at the +# beginning of each test suite, so don't rely on these for CI/CD or production. + +if [ -z "$CDM_DIR" ]; then + echo "CDM_DIR must be set to the top of the repo" + exit +fi + +kill_ta_simulators() { + tee_simulator_cas_pid=$(ps aux | grep '[t]ee_simulator_cas' | awk '{print $2}') + + if [ -n "$tee_simulator_cas_pid" ]; then + echo "Terminating CAS TEE simulator (PID $tee_simulator_cas_pid)" + kill $tee_simulator_cas_pid + fi +} + +run_tests() { + builddir=$CDM_DIR/out/linux/cas + + export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$builddir/liboemcrypto:$builddir/libtuner + + $builddir/tee_simulator_cas/tee_simulator_cas & + + trap kill_ta_simulators EXIT + + sleep 1 + tee_simulator_cas_pid=$(ps aux | grep '[t]ee_simulator_cas' | awk '{print $2}') + if [[ -f "/proc/$tee_simulator_cas_pid/stat" ]]; then + $builddir/cas_unittests/cas_unittests --gtest_filter="*OEMCryptoCasDemoTest*" + # $builddir/hello_world/hello_world + else + echo "CAS TEE simulator did not start." + FINAL_RESULT=43 + fi +} + +run_tests + +exit $FINAL_RESULT diff --git a/oemcrypto/opk/ports/linux/cas/tee/tee_simulator_cas/Makefile b/oemcrypto/opk/ports/linux/cas/tee/tee_simulator_cas/Makefile new file mode 100644 index 0000000..1931349 --- /dev/null +++ b/oemcrypto/opk/ports/linux/cas/tee/tee_simulator_cas/Makefile @@ -0,0 +1,64 @@ +# +# Copyright 2023 Google LLC. All Rights Reserved. This file and proprietary +# source code may only be used and distributed under the Widevine +# License Agreement. +# + +# This Makefile is not intended to be invoked on its own. It is possible +# though. Be sure to set the following variables. +# - CDM_DIR: path to top of CDM repo + +# Place outputs in $CDM_DIR/out/linux/cas// +project := $(shell basename $(CURDIR)) +srcdir := $(shell realpath --relative-to=$(CURDIR) $(CDM_DIR)) +builddir := $(srcdir)/out/linux/cas/$(project)/ +output = $(project) + +# Define this for tee-sources.mk, which uses it as a prefix for source file +# locations +OPK_REPO_TOP := +# tee-sources.mk provides opk_base_ta_sources and opk_base_ta_includes +include $(CDM_DIR)/oemcrypto/opk/build/tee-sources.mk +include $(CDM_DIR)/oemcrypto/opk/build/ree-sources.mk + +# Definitions for wtpi_impl sources.mk +wtpi_impl_dir := $(OPK_REPO_TOP)/oemcrypto/opk/ports/linux/ta/common/wtpi_impl + +# Provides wtpi_impl_sources and wtpi_impl_includes +include $(CDM_DIR)/oemcrypto/opk/ports/linux/ta/common/wtpi_impl/sources.mk + +srcs += \ + $(OPK_REPO_TOP)/oemcrypto/opk/ports/linux/cas/tee/tee_simulator_cas/tee_simulator.cpp \ + $(OPK_REPO_TOP)/oemcrypto/opk/ports/linux/ta/common/clock.cpp \ + $(tos_impl_dir)/tos_shared_memory.cpp \ + $(OPK_REPO_TOP)/oemcrypto/opk/ports/linux/cas/tee/tuner_tee_serialization.c \ + $(OPK_REPO_TOP)/oemcrypto/opk/ports/linux/cas/tee/tee_simulator_cas/data_share_impl.c \ + $(OPK_REPO_TOP)/oemcrypto/opk/ports/linux/cas/tee/tee_simulator_cas/tuner_impl.c \ + $(OPK_REPO_TOP)/oemcrypto/opk/ports/linux/cas/tee/tee_simulator_cas/wtpi_cas.c \ + $(opk_base_ta_sources) \ + $(wtpi_impl_sources) \ + $(boringssl_sources) \ + $(dice_sources) \ + +incs += \ + $(OPK_REPO_TOP)/oemcrypto/opk/ports/linux/common \ + $(OPK_REPO_TOP)/util/include \ + $(OPK_REPO_TOP)/oemcrypto/opk/ports/linux/cas/tee/tee_simulator_cas \ + $(OPK_REPO_TOP)/oemcrypto/opk/ports/linux/cas \ + $(OPK_REPO_TOP)/oemcrypto/opk/ports/linux/cas/tee \ + $(opk_base_ta_includes) \ + $(wtpi_impl_includes) \ + $(boringssl_includes) \ + $(dice_includes) \ + +cflags += \ + -DWTPI_BUILD_INFO=\"$(WTPI_BUILD_INFO)\" \ + -DSUPPORT_CAS \ + -DWV_POSIX_RESOURCE_ID=\"$(project)\" \ + -D_DEFAULT_SOURCE + +ldflags = \ + -lrt -lpthread \ + +include ../../../rules.mk + diff --git a/oemcrypto/opk/ports/linux/cas/tee/tee_simulator_cas/data_share.h b/oemcrypto/opk/ports/linux/cas/tee/tee_simulator_cas/data_share.h new file mode 100644 index 0000000..8b623e0 --- /dev/null +++ b/oemcrypto/opk/ports/linux/cas/tee/tee_simulator_cas/data_share.h @@ -0,0 +1,75 @@ +/* Copyright 2023 Google LLC. All Rights Reserved. This file and proprietary + source code may only be used and distributed under the Widevine License + Agreement. */ + +#ifndef _CAS_DATA_SHARE_H_ +#define _CAS_DATA_SHARE_H_ + +#include +#include +#include +#include "OEMCryptoCENC.h" +#include "wtpi_config_macros.h" +#include "wtpi_crypto_and_key_management_interface_layer1.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define MAX_NUMBER_OF_CAS_KEYTABLE_ENTRIES \ + (CONTENT_KEYS_PER_SESSION * MAX_NUMBER_OF_ENTITLED_KEY_SESSIONS) + +typedef uint32_t CasKeyDescriptor; + +// Sets up key table. Returns false if already initialized. +// Called by OEMCrypto (writer) +bool CasKeyTable_Init(); + +// Tears down key table. Returns false if already uninitialized. +// Called by OEMCrypto (writer) +bool CasKeyTable_Close(); + +// Claims an empty key slot. Returns true if successful, false if unable to +// find an empty key slot. Writes a pointer to a CasKeyDescriptor object that +// can be used to reference the slot later. +// Returns false if key table not initialized. +// Called by OEMCrypto (writer) +bool CasKeyTable_ClaimSlot(CasKeyDescriptor** slot_descriptor); + +// Writes a key and/or IV to a slot's even or odd space. If a key or IV has +// already been written, returns false. +// If WTPI_K1_SymmetricKey_Handle is NULL, it is ignored. If iv is NULL, it is +// ignored. +// Called by OEMCrypto (writer) +bool CasKeyTable_WriteKey(CasKeyDescriptor descriptor, bool is_even, + const WTPI_K1_SymmetricKey_Handle key, + OEMCryptoCipherMode cipher_mode); +bool CasKeyTable_WriteIV(CasKeyDescriptor descriptor, bool is_even, + const uint8_t* iv, size_t iv_length); + +// Reads a key and iv from a slot's even or odd space. If a key or cipher mode +// have not been written yet, returns false. If an IV has not been written, then +// iv_length is set to 0 but this function returns true. +// Called by Tuner (reader) +bool CasKeyTable_Read(CasKeyDescriptor descriptor, bool is_even, + WTPI_K1_SymmetricKey_Handle* key, + OEMCryptoCipherMode* cipher_mode, uint8_t* iv, + size_t* iv_length); + +// Converts a descriptor to a token. Tokens can be exported to the REE. +// Returns false if pointers are NULL or the conversion fails. +// Called by OEMCrypto +bool CasKeyTable_DescriptorToToken(CasKeyDescriptor descriptor, uint8_t* token, + size_t* token_length); + +// Converts a token to a descriptor. +// Returns false if pointers are NULL or the conversion fails. +// Called by Tuner +bool CasKeyTable_TokenToDescriptor(const uint8_t* token, size_t token_length, + CasKeyDescriptor* descriptor); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/oemcrypto/opk/ports/linux/cas/tee/tee_simulator_cas/data_share_impl.c b/oemcrypto/opk/ports/linux/cas/tee/tee_simulator_cas/data_share_impl.c new file mode 100644 index 0000000..129920b --- /dev/null +++ b/oemcrypto/opk/ports/linux/cas/tee/tee_simulator_cas/data_share_impl.c @@ -0,0 +1,212 @@ +/* Copyright 2023 Google LLC. All Rights Reserved. This file and proprietary + source code may only be used and distributed under the Widevine License + Agreement. */ + +#include +#include +#include +#include +#include "data_share.h" +#include "oemcrypto_api_macros.h" + +OEMCryptoResult WTPI_K1_CopyKeyHandle(WTPI_K1_SymmetricKey_Handle key, + WTPI_K1_SymmetricKey_Handle* out); + +typedef struct KeyData { + WTPI_K1_SymmetricKey_Handle key; + uint8_t iv[16]; + OEMCryptoCipherMode cipher_mode; + bool key_written; + bool iv_written; +} KeyData; + +typedef struct KeySlot { + KeyData even; + KeyData odd; + CasKeyDescriptor descriptor; + bool available; +} KeySlot; + +typedef struct CasKeyTable { + KeySlot keys[MAX_NUMBER_OF_CAS_KEYTABLE_ENTRIES]; + size_t next_free_index; + bool is_initialized; +} CasKeyTable; + +static CasKeyTable g_cas_key_table = {0}; +static pthread_mutex_t g_cas_key_table_lock = PTHREAD_MUTEX_INITIALIZER; + +// Convert CasKeyDescriptor to index into table +bool Helper_DescriptorToIndex(CasKeyDescriptor descriptor, size_t* index) { + size_t result = (uint32_t)descriptor; + if (result >= MAX_NUMBER_OF_CAS_KEYTABLE_ENTRIES) { + return false; + } + + *index = result; + return true; +} + +bool CasKeyTable_DescriptorToToken(CasKeyDescriptor descriptor, uint8_t* token, + size_t* token_length) { + if (token == NULL || token_length == NULL) return false; + if (*token_length < sizeof(CasKeyDescriptor)) return false; + + memcpy(token, &descriptor, sizeof(CasKeyDescriptor)); + *token_length = sizeof(CasKeyDescriptor); + + return true; +} + +bool CasKeyTable_TokenToDescriptor(const uint8_t* token, size_t token_length, + CasKeyDescriptor* descriptor) { + if (token == NULL || descriptor == NULL || + token_length != sizeof(CasKeyDescriptor)) { + return false; + } + + memcpy(descriptor, token, sizeof(CasKeyDescriptor)); + + return true; +} + +// Boilerplate check to see if table is initialized (or not) before proceeding. +// Be sure to lock the global mutex before calling this. +#define CAS_RETURN_IF_INIT_IS(INITIALIZED) \ + do { \ + if (g_cas_key_table.is_initialized == INITIALIZED) { \ + pthread_mutex_unlock(&g_cas_key_table_lock); \ + return false; \ + } \ + } while (0) + +bool CasKeyTable_Init() { + pthread_mutex_lock(&g_cas_key_table_lock); + CAS_RETURN_IF_INIT_IS(true); + + memset(&g_cas_key_table, 0, sizeof(g_cas_key_table)); + g_cas_key_table.is_initialized = true; + pthread_mutex_unlock(&g_cas_key_table_lock); + return true; +} + +bool CasKeyTable_Close() { + pthread_mutex_lock(&g_cas_key_table_lock); + CAS_RETURN_IF_INIT_IS(false); + + memset(&g_cas_key_table, 0, sizeof(g_cas_key_table)); + g_cas_key_table.is_initialized = false; + pthread_mutex_unlock(&g_cas_key_table_lock); + return true; +} + +bool CasKeyTable_WriteKey(CasKeyDescriptor descriptor, bool is_even, + const WTPI_K1_SymmetricKey_Handle key, + OEMCryptoCipherMode cipher_mode) { + pthread_mutex_lock(&g_cas_key_table_lock); + CAS_RETURN_IF_INIT_IS(false); + + size_t index = 0; + if (!Helper_DescriptorToIndex(descriptor, &index)) { + return false; + } + + KeySlot* slot = &(g_cas_key_table.keys[index]); + KeyData* data = is_even ? &(slot->even) : &(slot->odd); + + if (key != NULL) { + WTPI_K1_SymmetricKey_Handle key_handle_copy; + OEMCryptoResult result = WTPI_K1_CopyKeyHandle(key, &key_handle_copy); + if (result != OEMCrypto_SUCCESS) return false; + data->key = key_handle_copy; + data->cipher_mode = cipher_mode; + data->key_written = true; + } + + pthread_mutex_unlock(&g_cas_key_table_lock); + return true; +} + +bool CasKeyTable_WriteIV(CasKeyDescriptor descriptor, bool is_even, + const uint8_t* iv, size_t iv_length) { + pthread_mutex_lock(&g_cas_key_table_lock); + CAS_RETURN_IF_INIT_IS(false); + + size_t index = 0; + if (!Helper_DescriptorToIndex(descriptor, &index)) { + return false; + } + + KeySlot* slot = &(g_cas_key_table.keys[index]); + KeyData* data = is_even ? &(slot->even) : &(slot->odd); + + if (iv != NULL) { + if (iv_length > sizeof(data->iv)) { + // iv_length is too long + return false; + } + memcpy(data->iv, iv, iv_length); + data->iv_written = true; + } + + pthread_mutex_unlock(&g_cas_key_table_lock); + return true; +} + +bool CasKeyTable_Read(CasKeyDescriptor descriptor, bool is_even, + WTPI_K1_SymmetricKey_Handle* key, + OEMCryptoCipherMode* cipher_mode, uint8_t* iv, + size_t* iv_length) { + pthread_mutex_lock(&g_cas_key_table_lock); + CAS_RETURN_IF_INIT_IS(false); + + size_t index = 0; + if (!Helper_DescriptorToIndex(descriptor, &index)) { + return false; + } + + KeySlot* slot = &(g_cas_key_table.keys[index]); + KeyData* data = is_even ? &(slot->even) : &(slot->odd); + + if (!data || !data->key_written) return false; + + if (key == NULL) return false; + *key = data->key; + + if (cipher_mode == NULL) return false; + *cipher_mode = data->cipher_mode; + + if (data->iv_written) { + if (iv == NULL || iv_length == NULL) return false; + if (*iv_length < sizeof(data->iv)) { + *iv_length = sizeof(data->iv); + return false; + } + + memcpy(iv, data->iv, sizeof(data->iv)); + *iv_length = sizeof(data->iv); + } + + pthread_mutex_unlock(&g_cas_key_table_lock); + + return true; +} + +bool CasKeyTable_ClaimSlot(CasKeyDescriptor** slot_descriptor) { + pthread_mutex_lock(&g_cas_key_table_lock); + CAS_RETURN_IF_INIT_IS(false); + + size_t i = g_cas_key_table.next_free_index; + if (i >= MAX_NUMBER_OF_CAS_KEYTABLE_ENTRIES) { + // out of space + return false; + } + + g_cas_key_table.keys[i].descriptor = i; + *slot_descriptor = &(g_cas_key_table.keys[i].descriptor); + + g_cas_key_table.next_free_index++; + + pthread_mutex_unlock(&g_cas_key_table_lock); + return true; +} diff --git a/oemcrypto/opk/ports/linux/cas/tee/tee_simulator_cas/tee_simulator.cpp b/oemcrypto/opk/ports/linux/cas/tee/tee_simulator_cas/tee_simulator.cpp new file mode 100644 index 0000000..e31aa94 --- /dev/null +++ b/oemcrypto/opk/ports/linux/cas/tee/tee_simulator_cas/tee_simulator.cpp @@ -0,0 +1,112 @@ +/* + * Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary + * source code may only be used and distributed under the Widevine + * License Agreement. + */ +#include +#include +#include +#include + +#include "log_macros.h" +#include "odk_message.h" +#include "opk_dispatcher.h" +#include "opk_init.h" +#include "string.h" +#include "tuner_tee_serialization.h" +#include "tos_transport.h" +#include "tos_transport_interface.h" + +static pthread_t main_thread_tid; +static bool thread_running = false; + +void signalHandler(int signum) { + // TODO(fredgc): this doesn't actually kill anything because the main loop is + // stuck waiting for a new message. + thread_running = false; + // This exits, but then we skip the OPK_Terminate call. + exit(0); +} + +// The request message data must be copied into a local buffer +// so the contents can't be modified while being parsed. +static uint8_t local_buffer[OPK_TRANSPORT_MESSAGE_SIZE]; + +static void* MainLoop(void* arg) { + OPK_Initialize(); + thread_running = true; + while (thread_running) { + ODK_Message request, response; + OPK_TransportStatus transport_status = + TOS_Transport_ReceiveRequest(&request); + if (transport_status != OPK_TRANSPORT_STATUS_OK) { + LOGE("Receive request failed: status=%s", + OPK_TransportStatus_Str(transport_status)); + continue; + } + // + // !! IMPORTANT NOTE !! + // The request payload buffer MUST be copied out to TEE local + // storage so it can't be tampered with while being + // parsed. Failure to do so could introduce security + // vulnerabilities. + // + size_t payload_size = ODK_Message_GetSize(&request); + memcpy(local_buffer, ODK_Message_GetBase(&request), payload_size); + ODK_Message safe_request = + ODK_Message_Create(local_buffer, sizeof(local_buffer)); + ODK_Message_SetSize(&safe_request, payload_size); + + ODK_MessageStatus message_status = + OPK_DispatchMessage(&safe_request, &response); + if (message_status != MESSAGE_STATUS_OK) { + LOGE("Dispatch failed: status=%s", OPK_MessageStatus_Str(message_status)); + // Try tuner-specific dispatch just to be sure + message_status = Tuner_DispatchMessage(&safe_request, &response); + + if (message_status != MESSAGE_STATUS_OK) { + LOGE("Tuner dispatch failed: status=%s", + OPK_MessageStatus_Str(message_status)); + } + } + transport_status = TOS_Transport_SendResponse(&response); + if (transport_status != OPK_TRANSPORT_STATUS_OK) { + LOGE("Send response failed: status=%s", + OPK_TransportStatus_Str(transport_status)); + continue; + } + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + } + OPK_Terminate(); + return nullptr; +} + +int main(int argc, char** argv) { + pthread_attr_t p_attr; + + int result = pthread_attr_init(&p_attr); + if (result != 0) { + LOGF("Failed pthread_attr_init: %s", strerror(errno)); + abort(); + } + + LOGI("Reset shared resource state"); + system("rm /dev/shm/*opk*"); + + LOGI("Starting the main loop thread"); + result = pthread_create(&main_thread_tid, &p_attr, &MainLoop, nullptr); + if (result != 0) { + LOGF("Failed to spawn a thread: %s", strerror(errno)); + abort(); + } + signal(SIGTERM, signalHandler); + LOGI("Main is waiting for the main loop thread to exit"); + void* retval; + result = pthread_join(main_thread_tid, &retval); + if (result != 0) { + LOGF("Failed to join main thread: %s", strerror(errno)); + abort(); + } + return 0; +} diff --git a/oemcrypto/opk/ports/linux/cas/tee/tee_simulator_cas/tuner_impl.c b/oemcrypto/opk/ports/linux/cas/tee/tee_simulator_cas/tuner_impl.c new file mode 100644 index 0000000..b4b48fb --- /dev/null +++ b/oemcrypto/opk/ports/linux/cas/tee/tee_simulator_cas/tuner_impl.c @@ -0,0 +1,76 @@ + +#include "OEMCryptoCENC.h" +#include "data_share.h" +#include "tuner_hal.h" +#include "wtpi_crypto_and_key_management_interface_layer1.h" +#include "wtpi_decrypt_sample_interface.h" +#include "wtpi_logging_interface.h" + +// This Tuner_Decrypt implementation takes heavy advantage of the shared +// OEMCrypto and WTPI implementations in this demo. Since everything is +// compiled into the same binary, we have access to all the OEMCrypto helpers. +// For example, the key management is built in. A key handle created by the +// WTPI helpers is immediately usable since everything is in the same memory +// space. A separate Tuner TA would need to figure out how to make key handles +// portable from OEMCrypto. + +TunerHalResult TunerHal_Decrypt( + const uint8_t* key_token, size_t key_token_length, KeyParityType key_parity, + const OEMCrypto_SampleDescription* samples, // an array of samples. + size_t samples_length, // the number of samples. + const OEMCrypto_CENCEncryptPatternDesc* pattern) { + // Argument checks + if (key_token == NULL || samples == NULL || pattern == NULL) { + return TUNER_HAL_ERROR_UNKNOWN_FAILURE; + } + if (key_parity != TunerHal_KeyParityType_OddKey && + key_parity != TunerHal_KeyParityType_EvenKey) { + return TUNER_HAL_ERROR_UNKNOWN_FAILURE; + } + + // Get key slot + CasKeyDescriptor descriptor; + if (!CasKeyTable_TokenToDescriptor(key_token, key_token_length, + &descriptor)) { + return TUNER_HAL_ERROR_UNKNOWN_FAILURE; + } + + // Turn key slot data into key handle + bool is_even = key_parity == TunerHal_KeyParityType_EvenKey ? true : false; + WTPI_K1_SymmetricKey_Handle key_handle = NULL; + uint8_t iv[16]; + size_t iv_length = 16; + OEMCryptoCipherMode cipher_mode = OEMCrypto_CipherMode_CTR; // default CTR + + if (!CasKeyTable_Read(descriptor, is_even, &key_handle, &cipher_mode, iv, + &iv_length)) { + return TUNER_HAL_ERROR_UNKNOWN_FAILURE; + } + + for (size_t i = 0; i < samples_length; i++) { + OPK_OutputBuffer output_buffer; + size_t output_offset; + if (OEMCrypto_SUCCESS != + OPK_ParseDestBufferDesc(&samples[i].buffers.output_descriptor, + &output_buffer, &output_offset)) { + return TUNER_HAL_ERROR_UNKNOWN_FAILURE; + } + + // TODO: bounds check output buffer, check license limits, etc + + // NOTE: + // This demo impl re-uses WTPI_DecryptSample to perform CTR decryption. This + // uses the SampleDescription IV instead of the entitled key IV. Since the + // incoming sample description is const, we have to modify it before + // calling TunerHal_Decrypt() for this demo. + OEMCryptoResult res = + WTPI_DecryptSample(key_handle, &samples[i], pattern, &output_buffer, + output_offset, cipher_mode); + if (res != OEMCrypto_SUCCESS) { + LOGE("Decrypt returned %d", res); + return TUNER_HAL_ERROR_UNKNOWN_FAILURE; + } + } + + return TUNER_HAL_SUCCESS; +} diff --git a/oemcrypto/opk/ports/linux/cas/tee/tee_simulator_cas/wtpi_cas.c b/oemcrypto/opk/ports/linux/cas/tee/tee_simulator_cas/wtpi_cas.c new file mode 100644 index 0000000..964d8b0 --- /dev/null +++ b/oemcrypto/opk/ports/linux/cas/tee/tee_simulator_cas/wtpi_cas.c @@ -0,0 +1,92 @@ +/* Copyright 2023 Google LLC. All Rights Reserved. This file and proprietary + source code may only be used and distributed under the Widevine License + Agreement. */ + +#include +#include "data_share.h" +#include "oemcrypto_check_macros.h" +#include "wtpi_cas_interface.h" + +OEMCryptoResult WTPI_InitializeCas(void) { + if (!CasKeyTable_Init()) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult WTPI_TerminateCas(void) { + if (!CasKeyTable_Close()) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult WTPI_AllocateKeySlotDescriptor(OEMCrypto_SESSION session_id, + void** key_slot_descriptor) { + RETURN_INVALID_CONTEXT_IF_NULL(key_slot_descriptor); + + // Rely on CasKeyTable_ClaimSlot() using a pre allocated table, return pointer + // to descriptor in table key slot + CasKeyDescriptor* descriptor = NULL; + if (!CasKeyTable_ClaimSlot(&descriptor)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + *key_slot_descriptor = descriptor; + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult WTPI_FreeKeySlotDescriptor(void* key_slot_descriptor) { + RETURN_INVALID_CONTEXT_IF_NULL(key_slot_descriptor); + + // Do nothing + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult WTPI_InstallContentKey(void* key_slot_descriptor, + WTPI_K1_SymmetricKey_Handle key_handle, + OEMCryptoCipherMode cipher_mode, + bool is_even) { + RETURN_INVALID_CONTEXT_IF_NULL(key_slot_descriptor); + RETURN_INVALID_CONTEXT_IF_NULL(key_handle); + CasKeyDescriptor* descriptor = (CasKeyDescriptor*)key_slot_descriptor; + if (!CasKeyTable_WriteKey(*descriptor, is_even, key_handle, cipher_mode)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult WTPI_InstallContentIV(void* key_slot_descriptor, uint8_t* iv, + size_t iv_length, bool is_even) { + RETURN_INVALID_CONTEXT_IF_NULL(key_slot_descriptor); + RETURN_INVALID_CONTEXT_IF_ZERO(iv_length); + + CasKeyDescriptor* descriptor = (CasKeyDescriptor*)key_slot_descriptor; + if (!CasKeyTable_WriteIV(*descriptor, is_even, iv, iv_length)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult WTPI_GetKeyToken(void* key_slot_descriptor, uint8_t* key_token, + size_t* key_token_length) { + RETURN_INVALID_CONTEXT_IF_NULL(key_slot_descriptor); + RETURN_INVALID_CONTEXT_IF_NULL(key_token); + RETURN_INVALID_CONTEXT_IF_NULL(key_token_length); + + size_t required_size = WTPI_GetKeyTokenSize(); + + if (*key_token_length < required_size) { + *key_token_length = required_size; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + + *key_token_length = required_size; + CasKeyDescriptor* descriptor = (CasKeyDescriptor*)key_slot_descriptor; + if (!CasKeyTable_DescriptorToToken(*descriptor, key_token, + key_token_length)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + return OEMCrypto_SUCCESS; +} + +size_t WTPI_GetKeyTokenSize(void) { return sizeof(CasKeyDescriptor); } diff --git a/oemcrypto/opk/ports/linux/cas/tee/tuner_tee_serialization.c b/oemcrypto/opk/ports/linux/cas/tee/tuner_tee_serialization.c new file mode 100644 index 0000000..e75233b --- /dev/null +++ b/oemcrypto/opk/ports/linux/cas/tee/tuner_tee_serialization.c @@ -0,0 +1,153 @@ +/* + * Copyright 2023 Google LLC. All Rights Reserved. This file and proprietary + * source code may only be used and distributed under the Widevine License + * Agreement. + */ + +#include + +#include "GEN_common_serializer.h" +#include "GEN_tee_serializer.h" +#include "OEMCryptoCENC.h" +#include "bump_allocator.h" +#include "log_macros.h" +#include "marshaller_base.h" +#include "message_debug.h" +#include "odk_overflow.h" +#include "oemcrypto_wall_clock.h" +#include "opk_dispatcher.h" + +#include "shared_buffer_allocator.h" +#include "tee_special_cases.h" +#include "tos_shared_memory_interface.h" +#include "tos_transport_interface.h" + +#include "tuner_tee_serialization.h" + +static ODK_Message CreateEmptyMessage(void) { + static uint8_t buffer[1]; + ODK_Message msg = ODK_Message_Create(buffer, sizeof(buffer)); + OPK_PackEOM(&msg); + return msg; +} + +static ODK_Message NullMessage(void) { return ODK_Message_Create(NULL, 0); } + +void OPK_Init_OEMCrypto_CENCEncryptPatternDesc( + OEMCrypto_CENCEncryptPatternDesc* obj); + +/* See opk_dispatcher.h for definition of OPK_DispatchMessage() */ +ODK_MessageStatus Tuner_DispatchMessage(ODK_Message* request, + ODK_Message* response) { + if (request == NULL || response == NULL) + return MESSAGE_STATUS_NULL_POINTER_ERROR; + *response = NullMessage(); + uint32_t api_value; + OPK_Unpack_uint32_t(request, &api_value); + uint64_t timestamp; + OPK_Unpack_uint64_t(request, ×tamp); + OPK_SetWallClockTime(timestamp); + OPK_BumpAllocator_Reset(); + OPK_SharedBuffer_Reset(); + ODK_Message_Reset(request); + switch (api_value) { + case 12345: /* TunerHal_Decrypt */ + size_t key_token_length; + OPK_Init_size_t((size_t*)&key_token_length); + size_t* wrapped_key_token_length = (size_t*)OPK_VarAlloc(sizeof(size_t)); + OPK_Init_size_t(wrapped_key_token_length); + uint8_t* key_token; + OPK_InitPointer((uint8_t**)&key_token); + KeyParityType key_parity; + OPK_Init_uint32_t((uint32_t*)&key_parity); + size_t samples_length; + OPK_Init_size_t((size_t*)&samples_length); + OEMCrypto_SampleDescription* samples; + OPK_InitPointer((uint8_t**)&samples); + OEMCrypto_CENCEncryptPatternDesc* pattern = + (OEMCrypto_CENCEncryptPatternDesc*)OPK_VarAlloc( + sizeof(OEMCrypto_CENCEncryptPatternDesc)); + OPK_Init_OEMCrypto_CENCEncryptPatternDesc( + (OEMCrypto_CENCEncryptPatternDesc*)pattern); + // OPK_Unpack_DecryptCENC_Request(request, &session, &samples, + // &samples_length, &pattern); + OPK_Unpack_TunerHal_Decrypt_Request(request, &key_token, + &key_token_length, &key_parity, + &samples, &samples_length, &pattern); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + TunerHalResult result; + OPK_Init_uint32_t((uint32_t*)&result); + result = TunerHal_Decrypt(key_token, key_token_length, key_parity, + samples, samples_length, pattern); + *response = OPK_Pack_TunerHal_Decrypt_Response(result); + break; + default: + return MESSAGE_STATUS_API_VALUE_ERROR; + } + return ODK_Message_GetStatus(response); + +handle_invalid_request: + LOGE("invalid request"); + *response = CreateEmptyMessage(); + return MESSAGE_STATUS_OK; +} + +void OPK_Unpack_TunerHal_Decrypt_Request( + ODK_Message* msg, uint8_t** key_token, size_t* key_token_length, + KeyParityType* key_parity, OEMCrypto_SampleDescription** samples, + size_t* samples_length, OEMCrypto_CENCEncryptPatternDesc** pattern) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 12345) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + + OPK_Unpack_size_t(msg, key_token_length); + OPK_UnpackInPlace(msg, (uint8_t**)key_token, + OPK_FromSizeTPtr(key_token_length)); + OPK_Unpack_uint32_t(msg, key_parity); + + OPK_Unpack_size_t(msg, samples_length); + /* unpack object array with unpacker function + * OPK_Unpack_OEMCrypto_SampleDescription */ + ODK_Message* odk_message = msg; + void** address = (void**)samples; + LengthType count = OPK_FromSizeTPtr(samples_length); + size_t size = sizeof(OEMCrypto_SampleDescription); + if (address) { + *address = NULL; + } + if (!OPK_UnpackIsNull(odk_message)) { + if (address && !OPK_LengthIsNull(count)) { + size_t bytes_to_unpack = 0; + if (odk_mul_overflow_ux(OPK_ToSizeT(count), size, &bytes_to_unpack)) { + ODK_MESSAGE_SETSTATUS(odk_message, MESSAGE_STATUS_PARSE_ERROR); + } else { + *address = OPK_BumpAllocate(bytes_to_unpack); + if (!*address) { + ODK_MESSAGE_SETSTATUS(odk_message, MESSAGE_STATUS_OUT_OF_MEMORY); + } else { + for (size_t i = 0; i < OPK_ToSizeT(count); i++) { + OPK_Unpack_OEMCrypto_SampleDescription( + odk_message, + (OEMCrypto_SampleDescription*)((*address) + size * i)); + } + } + } + } + } + OPK_UnpackNullable_OEMCrypto_CENCEncryptPatternDesc(msg, pattern); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_TunerHal_Decrypt_Response(TunerHalResult result) { + uint32_t api_value = 12345; /* from _oecc8 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} diff --git a/oemcrypto/opk/ports/linux/cas/tee/tuner_tee_serialization.h b/oemcrypto/opk/ports/linux/cas/tee/tuner_tee_serialization.h new file mode 100644 index 0000000..c072559 --- /dev/null +++ b/oemcrypto/opk/ports/linux/cas/tee/tuner_tee_serialization.h @@ -0,0 +1,27 @@ +#ifndef TUNER_TEE_SERIALIZER_H_ +#define TUNER_TEE_SERIALIZER_H_ + +#include "log_macros.h" +#include "opk_serialization_base.h" +#include "tuner_hal.h" + +#ifdef __cplusplus +extern "C" { +#endif + +ODK_Message OPK_Pack_TunerHal_Decrypt_Response(TunerHalResult result); +void OPK_Unpack_TunerHal_Decrypt_Request( + ODK_Message* msg, uint8_t** key_token, size_t* key_token_length, + KeyParityType* key_parity, OEMCrypto_SampleDescription** samples, + size_t* samples_length, OEMCrypto_CENCEncryptPatternDesc** pattern); + +// void OPK_Init_OEMCrypto_CENCEncryptPatternDesc( +// OEMCrypto_CENCEncryptPatternDesc* obj); + +/* See opk_dispatcher.h for definition of OPK_DispatchMessage() */ +ODK_MessageStatus Tuner_DispatchMessage(ODK_Message* request, + ODK_Message* response); +#ifdef __cplusplus +} // extern "C" +#endif +#endif /* TUNER_TEE_SERIALIZER_H_ */ diff --git a/oemcrypto/opk/ports/linux/cas/tuner_hal.h b/oemcrypto/opk/ports/linux/cas/tuner_hal.h new file mode 100644 index 0000000..be03385 --- /dev/null +++ b/oemcrypto/opk/ports/linux/cas/tuner_hal.h @@ -0,0 +1,40 @@ +/* Copyright 2023 Google LLC. All Rights Reserved. This file and proprietary + source code may only be used and distributed under the Widevine License + Agreement. */ + +#ifndef _TUNER_HAL_H_ +#define _TUNER_HAL_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum TunerHalResult { + TUNER_HAL_SUCCESS = 0, + TUNER_HAL_ERROR_UNKNOWN_FAILURE = 1, + TUNER_HAL_ERROR_BUFFER_TOO_LARGE = 2, + TUNER_HAL_ERROR_NOT_IMPLEMENTED = 3, +} TunerHalResult; + +typedef enum KeyParityType { + TunerHal_KeyParityType_NotEncrypted, // Should never be used. + TunerHal_KeyParityType_OddKey, + TunerHal_KeyParityType_EvenKey, + TunerHal_KeyParityType_MaxValue = TunerHal_KeyParityType_EvenKey, +} KeyParityType; + +TunerHalResult TunerHal_Decrypt( + const uint8_t* key_token, size_t key_token_length, KeyParityType key_parity, + const OEMCrypto_SampleDescription* samples, // an array of samples. + size_t samples_length, // the number of samples. + const OEMCrypto_CENCEncryptPatternDesc* pattern); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/oemcrypto/opk/ports/linux/ta/common/wtpi_impl/wtpi_test_impl.gyp b/oemcrypto/opk/ports/linux/ta/common/wtpi_impl/wtpi_test_impl.gyp index 92be777..26e4d8c 100644 --- a/oemcrypto/opk/ports/linux/ta/common/wtpi_impl/wtpi_test_impl.gyp +++ b/oemcrypto/opk/ports/linux/ta/common/wtpi_impl/wtpi_test_impl.gyp @@ -30,6 +30,7 @@ 'wtpi_provisioning_4_linux.c', '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', @@ -48,11 +49,6 @@ 'wtpi_device_renewal_layer2_test.c', ], }], - ['use_provisioning_40', { - 'defines': [ - 'OPK_CONFIG_PROVISIONING_METHOD=OEMCrypto_BootCertificateChain', - ], - }], ], 'dependencies': [ '<(oemcrypto_ta_dir)/oemcrypto_ta.gyp:oemcrypto_ta_linux_tee', diff --git a/oemcrypto/opk/ports/linux/ta/oemcrypto_ta/Makefile b/oemcrypto/opk/ports/linux/ta/oemcrypto_ta/Makefile index 8cf9ede..9d9de96 100644 --- a/oemcrypto/opk/ports/linux/ta/oemcrypto_ta/Makefile +++ b/oemcrypto/opk/ports/linux/ta/oemcrypto_ta/Makefile @@ -28,21 +28,21 @@ wtpi_impl_dir := $(OPK_REPO_TOP)/oemcrypto/opk/ports/linux/ta/common/wtpi_impl include $(CDM_DIR)/oemcrypto/opk/ports/linux/ta/common/wtpi_impl/sources.mk srcs += \ - $(OPK_REPO_TOP)/oemcrypto/opk/ports/linux/ta/common/tee_simulator.cpp \ - $(OPK_REPO_TOP)/oemcrypto/opk/ports/linux/ta/common/clock.cpp \ - $(tos_impl_dir)/tos_shared_memory.cpp \ + $(OPK_REPO_TOP)/oemcrypto/opk/ports/linux/ta/common/tee_simulator.cpp \ + $(OPK_REPO_TOP)/oemcrypto/opk/ports/linux/ta/common/clock.cpp \ + $(tos_impl_dir)/tos_shared_memory.cpp \ $(opk_base_ta_sources) \ $(wtpi_impl_sources) \ $(boringssl_sources) \ - $(dice_sources) \ + $(dice_sources) incs += \ - $(OPK_REPO_TOP)/oemcrypto/opk/ports/linux/common \ - $(OPK_REPO_TOP)/util/include \ + $(OPK_REPO_TOP)/oemcrypto/opk/ports/linux/common \ + $(OPK_REPO_TOP)/util/include \ $(opk_base_ta_includes) \ $(wtpi_impl_includes) \ $(boringssl_includes) \ - $(dice_includes) \ + $(dice_includes) cflags += \ -DWV_POSIX_RESOURCE_ID=\"$(project)\" \ @@ -53,12 +53,14 @@ cflags += \ -DOPK_CONFIG_DEVICE_FORM_FACTOR=$(DEVICE_FORM_FACTOR) \ -DOPK_CONFIG_IMPLEMENTER_NAME=$(IMPLEMENTER) \ -DOPK_CONFIG_PROVISIONING_METHOD=$(PROVISIONING_METHOD) \ - -D_DEFAULT_SOURCE + -D_DEFAULT_SOURCE \ + -fstack-protector-strong # -D_DEBUG ldflags = \ - -lrt -lpthread \ + -lrt \ + -lpthread include ../../rules.mk diff --git a/oemcrypto/opk/ports/optee/README.md b/oemcrypto/opk/ports/optee/README.md index 77b2817..6ecb3b7 100644 --- a/oemcrypto/opk/ports/optee/README.md +++ b/oemcrypto/opk/ports/optee/README.md @@ -8,15 +8,22 @@ manifest (eg to build OPK against an NXP chip target, use the correct NXP manifest when cloning and building OP-TEE). 1. Download and build OP-TEE following their online documentation. Set that - destination to the environment variable OPTEE_DIR. If the GCC toolchain is - separate from the one included in $OPTEE_DIR/toolchains, set that path to - OPTEE_TOOLCHAIN_DIR. -2. Set up the third_party directory with the required dependencies. All of these + destination to the environment variable OPTEE_DIR. For security purposes, + prefer OP-TEE version 3.20.0 or later since targeted stack protection with + `-fstack-protector-strong` is enabled by default and `__stack_chk_guard` is + initialized to a secure, random value at runtime. On AArch64 targets, build + OP-TEE with `CFG_CORE_BTI=y`, `CFG_CORE_PAUTH=y`, `CFG_TA_BTI=y`, + and `CFG_TA_PAUTH=y` to enable branch protection with + `-mbranch-protection=pac-ret+leaf+bti`. Branch protection requires that the + GCC toolchain is compiled with `--enable-standard-branch-protection`. +2. If the GCC toolchain is separate from the one included in + $OPTEE_DIR/toolchains, set that path to OPTEE_TOOLCHAIN_DIR. +3. Set up the third_party directory with the required dependencies. All of these dependencies are for unit tests. Run `oemcrypto/opk/setup.sh` to download all of the required libraries to `$CDM_DIR/third_party`. -3. From the top level of this repo (CDM), run `make -j32 -C +4. From the top level of this repo (CDM), run `make -j32 -C ./oemcrypto/opk/ports/optee host ta` -4. Run on QEMU +5. Run on QEMU ``` export QEMU=$OPTEE_DIR/qemu/build/arm-softmmu/qemu-system-arm && \ export GTEST_FILTER="-*Reboot*" && \ diff --git a/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/sources.mk b/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/sources.mk index aeb8fc2..356dd1b 100644 --- a/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/sources.mk +++ b/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/sources.mk @@ -38,8 +38,8 @@ dice_includes += \ wtpi_impl_sources += \ $(wtpi_impl_dir)/util/ta_log.c \ $(wtpi_impl_dir)/util/der_parse.c \ - $(wtpi_impl_dir)/util/odk_endian.c \ $(wtpi_impl_dir)/util/device_info.c \ + $(wtpi_ref_dir)/odk_endian.c \ $(wtpi_ref_dir)/cose_util.c \ $(wtpi_impl_dir)/wtpi_abort.c \ $(wtpi_impl_dir)/wtpi_clock_layer2.c \ @@ -55,6 +55,7 @@ wtpi_impl_sources += \ $(wtpi_impl_dir)/wtpi_root_of_trust_layer1.c \ $(wtpi_stub_dir)/wtpi_root_of_trust_layer2.c \ $(wtpi_stub_dir)/wtpi_secure_buffer_access.c \ + $(wtpi_stub_dir)/wtpi_cas.c \ $(wtpi_stub_dir)/wtpi_device_key_access.c \ $(wtpi_stub_dir)/wtpi_fused.c \ $(wtpi_ref_dir)/renewal_util.c \ diff --git a/oemcrypto/opk/serialization/ree/ree_version.c b/oemcrypto/opk/serialization/ree/ree_version.c index 7720311..1a14df4 100644 --- a/oemcrypto/opk/serialization/ree/ree_version.c +++ b/oemcrypto/opk/serialization/ree/ree_version.c @@ -12,6 +12,14 @@ #include "message_debug.h" #include "version.h" +#include "oemcrypto_api_macros.h" +#include "odk_structs.h" + +// Constant strings that can be searched in the output binary +// Eg. `strings liboemcrypto.so | grep "OPK v"` +const char kVersionInfo[] = "OPK v" XSTR(API_MAJOR_VERSION) "." XSTR(API_MINOR_VERSION) "." XSTR(OPK_PATCH_VERSION); +const char kOdkReleaseDate[] = XSTR(ODK_RELEASE_DATE); + /* * Cached copies of the REE and TEE serialization versions */ diff --git a/oemcrypto/test/fuzz_tests/oemcrypto_create_and_remove_entitled_key_session_fuzz.cc b/oemcrypto/test/fuzz_tests/oemcrypto_create_and_remove_entitled_key_session_fuzz.cc deleted file mode 100644 index 07c0bd4..0000000 --- a/oemcrypto/test/fuzz_tests/oemcrypto_create_and_remove_entitled_key_session_fuzz.cc +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2022 Google LLC. All Rights Reserved. This file and proprietary -// source code may only be used and distributed under the Widevine -// License Agreement. - -#include "FuzzedDataProvider.h" -#include "OEMCryptoCENC.h" -#include "oemcrypto_fuzz_helper.h" - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - wvoec::RedirectStdoutToFile(); - - wvoec::SessionFuzz session_fuzz; - session_fuzz.Initialize(); - - FuzzedDataProvider fuzzed_data(data, size); - - uint32_t key_session; - uint32_t* const key_session_ptr = - fuzzed_data.ConsumeBool() ? &key_session : nullptr; - - OEMCrypto_CreateEntitledKeySession(session_fuzz.session().session_id(), - key_session_ptr); - - if (key_session_ptr == nullptr || fuzzed_data.ConsumeBool()) { - key_session = fuzzed_data.ConsumeIntegral(); - } - - OEMCrypto_RemoveEntitledKeySession(key_session); - - session_fuzz.Terminate(); - return 0; -} diff --git a/oemcrypto/test/fuzz_tests/oemcrypto_entitled_key_session_fuzz.cc b/oemcrypto/test/fuzz_tests/oemcrypto_entitled_key_session_fuzz.cc new file mode 100644 index 0000000..78323dd --- /dev/null +++ b/oemcrypto/test/fuzz_tests/oemcrypto_entitled_key_session_fuzz.cc @@ -0,0 +1,136 @@ +// Copyright 2023 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine +// License Agreement. + +#include + +#include "FuzzedDataProvider.h" +#include "OEMCryptoCENC.h" +#include "oemcrypto_fuzz_helper.h" + +namespace { + +enum class ApiMethod { + kOpenSession, + kCloseSession, + kCreateEntitledKeySession, + kReassociateEntitledKeySession, + kRemoveEntitledKeySession, + kMaxValue = kRemoveEntitledKeySession, +}; + +struct Session { + OEMCrypto_SESSION value; + std::vector::const_iterator iterator; +}; + +Session PickSession(FuzzedDataProvider& fuzzed_data, + const std::vector& sessions) { + Session session; + + session.iterator = + sessions.cbegin() + + fuzzed_data.ConsumeIntegralInRange(0, sessions.size()); + + if (session.iterator != sessions.cend()) { + session.value = *session.iterator; + } else { + session.value = fuzzed_data.ConsumeIntegral(); + } + + return session; +} + +} // namespace + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + wvoec::RedirectStdoutToFile(); + + wvoec::SessionUtil session_util; + wvoec::InitializeFuzz(session_util); + + // Contains all open and some closed OEMCrypto sessions. + std::vector oec_sessions; + + // Contains all current and some removed key sessions. + std::vector key_sessions; + + FuzzedDataProvider fuzzed_data(data, size); + + while (fuzzed_data.remaining_bytes() > 0) { + switch (fuzzed_data.ConsumeEnum()) { + case ApiMethod::kOpenSession: { + OEMCrypto_SESSION session = 0; + const OEMCryptoResult result = OEMCrypto_OpenSession(&session); + + if (result == OEMCrypto_SUCCESS) { + oec_sessions.push_back(session); + } + + break; + } + + case ApiMethod::kCloseSession: { + const Session session = PickSession(fuzzed_data, oec_sessions); + + const OEMCryptoResult result = OEMCrypto_CloseSession(session.value); + + if (result == OEMCrypto_SUCCESS && + session.iterator != oec_sessions.cend() && + fuzzed_data.ConsumeBool()) { + oec_sessions.erase(session.iterator); + } + + break; + } + + case ApiMethod::kCreateEntitledKeySession: { + const OEMCrypto_SESSION oec_session = + PickSession(fuzzed_data, oec_sessions).value; + + OEMCrypto_SESSION key_session_data = 0; + OEMCrypto_SESSION* const key_session = + fuzzed_data.ConsumeBool() ? &key_session_data : nullptr; + + const OEMCryptoResult result = + OEMCrypto_CreateEntitledKeySession(oec_session, key_session); + + if (result == OEMCrypto_SUCCESS) { + key_sessions.push_back(*key_session); + } + + break; + } + + case ApiMethod::kReassociateEntitledKeySession: { + const OEMCrypto_SESSION key_session = + PickSession(fuzzed_data, key_sessions).value; + + const OEMCrypto_SESSION oec_session = + PickSession(fuzzed_data, oec_sessions).value; + + OEMCrypto_ReassociateEntitledKeySession(key_session, oec_session); + + break; + } + + case ApiMethod::kRemoveEntitledKeySession: { + const Session key_session = PickSession(fuzzed_data, key_sessions); + + const OEMCryptoResult result = + OEMCrypto_RemoveEntitledKeySession(key_session.value); + + if (result == OEMCrypto_SUCCESS && + key_session.iterator != key_sessions.cend() && + fuzzed_data.ConsumeBool()) { + key_sessions.erase(key_session.iterator); + } + + break; + } + } + } + + OEMCrypto_Terminate(); + return 0; +} diff --git a/oemcrypto/test/fuzz_tests/oemcrypto_opk_fuzztests.gyp b/oemcrypto/test/fuzz_tests/oemcrypto_opk_fuzztests.gyp index f5ac011..fe60be9 100644 --- a/oemcrypto/test/fuzz_tests/oemcrypto_opk_fuzztests.gyp +++ b/oemcrypto/test/fuzz_tests/oemcrypto_opk_fuzztests.gyp @@ -21,12 +21,6 @@ 'oemcrypto_copy_buffer_fuzz.cc', ], }, - { - 'target_name': 'oemcrypto_opk_create_and_remove_entitled_key_session_fuzz', - 'sources': [ - 'oemcrypto_create_and_remove_entitled_key_session_fuzz.cc', - ], - }, { 'target_name': 'oemcrypto_opk_deactivate_usage_entry_fuzz', 'sources': [ @@ -66,6 +60,12 @@ '<(oemcrypto_dir)/opk/ports/trusty/serialization_adapter/shared_memory.c', ], }, + { + 'target_name': 'oemcrypto_opk_entitled_key_session_fuzz', + 'sources': [ + 'oemcrypto_entitled_key_session_fuzz.cc', + ], + }, { 'target_name': 'oemcrypto_opk_generate_certificate_key_pair_first_stage_fuzz', 'sources': [ diff --git a/oemcrypto/test/fuzz_tests/partner_oemcrypto_fuzztests.gyp b/oemcrypto/test/fuzz_tests/partner_oemcrypto_fuzztests.gyp index c970e50..4ebf783 100644 --- a/oemcrypto/test/fuzz_tests/partner_oemcrypto_fuzztests.gyp +++ b/oemcrypto/test/fuzz_tests/partner_oemcrypto_fuzztests.gyp @@ -19,12 +19,6 @@ 'oemcrypto_copy_buffer_fuzz.cc', ], }, - { - 'target_name': 'oemcrypto_create_and_remove_entitled_key_session_fuzz', - 'sources': [ - 'oemcrypto_create_and_remove_entitled_key_session_fuzz.cc', - ], - }, { 'target_name': 'oemcrypto_deactivate_usage_entry_fuzz', 'sources': [ @@ -43,6 +37,12 @@ 'oemcrypto_decrypt_hash_fuzz.cc', ], }, + { + 'target_name': 'oemcrypto_entitled_key_session_fuzz', + 'sources': [ + 'oemcrypto_entitled_key_session_fuzz.cc', + ], + }, { 'target_name': 'oemcrypto_generate_certificate_key_pair_first_stage_fuzz', 'sources': [ diff --git a/oemcrypto/test/oec_device_features.cpp b/oemcrypto/test/oec_device_features.cpp index 2264053..cfb742b 100644 --- a/oemcrypto/test/oec_device_features.cpp +++ b/oemcrypto/test/oec_device_features.cpp @@ -148,14 +148,6 @@ void DeviceFeatures::Initialize() { std::string DeviceFeatures::RestrictFilter(const std::string& initial_filter) { std::string filter = initial_filter; // clang-format off - if (!uses_keybox) FilterOut(&filter, "*KeyboxTest*"); - // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for - // provisioning 4. Disabled here temporarily. - if (!loads_certificate || - provisioning_method == OEMCrypto_BootCertificateChain) - FilterOut(&filter, "OEMCryptoLoadsCert*"); - if (!generic_crypto) FilterOut(&filter, "*GenericCrypto*"); - if (derive_key_method == NO_METHOD) FilterOut(&filter, "*SessionTest*"); if (api_version < 17) FilterOut(&filter, "*API17*"); if (api_version < 18) FilterOut(&filter, "*API18*"); // clang-format on diff --git a/oemcrypto/test/oec_session_util.cpp b/oemcrypto/test/oec_session_util.cpp index 9e442fb..99f1d64 100644 --- a/oemcrypto/test/oec_session_util.cpp +++ b/oemcrypto/test/oec_session_util.cpp @@ -82,30 +82,6 @@ class FuzzedData { size_t source_size_; }; -// Encrypt a block of data using CTR mode. -void EncryptCTR(const vector& in_buffer, const uint8_t* key, - const uint8_t* starting_iv, vector* out_buffer) { - ASSERT_NE(nullptr, key); - ASSERT_NE(nullptr, starting_iv); - ASSERT_NE(nullptr, out_buffer); - AES_KEY aes_key; - AES_set_encrypt_key(key, AES_BLOCK_SIZE * 8, &aes_key); - out_buffer->resize(in_buffer.size()); - - uint8_t iv[AES_BLOCK_SIZE]; // Current iv. - - memcpy(iv, &starting_iv[0], AES_BLOCK_SIZE); - size_t l = 0; // byte index into encrypted subsample. - while (l < in_buffer.size()) { - uint8_t aes_output[AES_BLOCK_SIZE]; - AES_encrypt(iv, aes_output, &aes_key); - for (size_t n = 0; n < AES_BLOCK_SIZE && l < in_buffer.size(); n++, l++) { - (*out_buffer)[l] = aes_output[n] ^ in_buffer[l]; - } - ctr128_inc64(1, iv); - } -} - // Uses OEMCrypto to decrypt some random data in 'cenc' mode. This function // assumes that the correct key is already selected in the session. It requires // the plaintext of that key so that it can encrypt the test data. It resizes @@ -138,6 +114,31 @@ OEMCryptoResult DecryptCTR(const vector& key_handle, } // namespace + +// Encrypt a block of data using CTR mode. +void EncryptCTR(const vector& in_buffer, const uint8_t* key, + const uint8_t* starting_iv, vector* out_buffer) { + ASSERT_NE(nullptr, key); + ASSERT_NE(nullptr, starting_iv); + ASSERT_NE(nullptr, out_buffer); + AES_KEY aes_key; + AES_set_encrypt_key(key, AES_BLOCK_SIZE * 8, &aes_key); + out_buffer->resize(in_buffer.size()); + + uint8_t iv[AES_BLOCK_SIZE]; // Current iv. + + memcpy(iv, &starting_iv[0], AES_BLOCK_SIZE); + size_t l = 0; // byte index into encrypted subsample. + while (l < in_buffer.size()) { + uint8_t aes_output[AES_BLOCK_SIZE]; + AES_encrypt(iv, aes_output, &aes_key); + for (size_t n = 0; n < AES_BLOCK_SIZE && l < in_buffer.size(); n++, l++) { + (*out_buffer)[l] = aes_output[n] ^ in_buffer[l]; + } + ctr128_inc64(1, iv); + } +} + int GetRandBytes(unsigned char* buf, size_t num) { // returns 1 on success, -1 if not supported, or 0 if other failure. return RAND_bytes(buf, static_cast(num)); @@ -792,9 +793,6 @@ void LicenseRoundTrip::FillAndVerifyCoreRequest( // for L3 release only. EXPECT_LE(3, core_request_.api_minor_version); EXPECT_GE(5, core_request_.api_minor_version); - } else if (global_features.api_version == ODK_MAJOR_VERSION) { - // We do not expect older tests to work with a newer OEMCrypto. - EXPECT_GE(ODK_MINOR_VERSION, core_request_.api_minor_version); } if (expect_request_has_correct_nonce_) { EXPECT_EQ(session()->nonce(), core_request_.nonce); @@ -1234,6 +1232,12 @@ void EntitledMessage::MakeOneKey(size_t entitlement_key_index) { sizeof(key_data->content_key_data_iv))); offsets->content_key_data_iv = FindSubstring( key_data->content_key_data_iv, sizeof(key_data->content_key_data_iv)); + + EXPECT_EQ(1, GetRandBytes(key_data->content_iv, + sizeof(key_data->content_iv))); + key_data->content_iv_length = sizeof(key_data->content_iv); + offsets->content_iv = FindSubstring( + key_data->content_iv, key_data->content_iv_length); } OEMCrypto_EntitledContentKeyObject* EntitledMessage::entitled_key_array() { @@ -1367,8 +1371,8 @@ void EntitledMessage::LoadCasKeys(bool load_even, bool load_odd, // Convert the OEMCrypto_EntitledContentKeyObject to // OEMCrypto_EntitledCasKeyObject. Only the first two key object is used. - OEMCrypto_EntitledContentKeyObject even_key; - OEMCrypto_EntitledContentKeyObject odd_key; + OEMCrypto_EntitledContentKeyObject even_key = {}; + OEMCrypto_EntitledContentKeyObject odd_key = {}; bool has_even = load_even && num_keys_ >= 1; bool has_odd = load_odd && num_keys_ >= 2; if (has_even) { @@ -1376,14 +1380,14 @@ void EntitledMessage::LoadCasKeys(bool load_even, bool load_odd, even_key.content_key_id = entitled_key_array_[0].content_key_id; even_key.content_key_data_iv = entitled_key_array_[0].content_key_data_iv; even_key.content_key_data = entitled_key_array_[0].content_key_data; - even_key.content_iv.length = 0; + even_key.content_iv = entitled_key_array_[0].content_iv; } if (has_odd) { odd_key.entitlement_key_id = entitled_key_array_[1].entitlement_key_id; odd_key.content_key_id = entitled_key_array_[1].content_key_id; odd_key.content_key_data_iv = entitled_key_array_[1].content_key_data_iv; odd_key.content_key_data = entitled_key_array_[1].content_key_data; - odd_key.content_iv.length = 0; + even_key.content_iv = entitled_key_array_[1].content_iv; } OEMCryptoResult sts = OEMCrypto_LoadCasECMKeys( diff --git a/oemcrypto/test/oec_session_util.h b/oemcrypto/test/oec_session_util.h index e13bb51..0bf13b0 100644 --- a/oemcrypto/test/oec_session_util.h +++ b/oemcrypto/test/oec_session_util.h @@ -110,6 +110,8 @@ struct EntitledContentKeyData { uint8_t content_key_data_iv[KEY_IV_SIZE]; uint8_t content_key_data[KEY_SIZE]; uint8_t encrypted_content_key_data[KEY_SIZE]; + uint8_t content_iv[KEY_IV_SIZE]; + size_t content_iv_length; size_t key_index; // Index into the license's key array. Only for testing. }; @@ -121,6 +123,10 @@ void GenerateSimpleSampleDescription(const std::vector& in, OEMCrypto_SampleDescription* sample, OEMCrypto_SubSampleDescription* subsample); +// Encrypt a block of data using CTR mode. +void EncryptCTR(const vector& in_buffer, const uint8_t* key, + const uint8_t* starting_iv, vector* out_buffer); + // Increment counter for AES-CTR. The CENC spec specifies we increment only // the low 64 bits of the IV counter, and leave the high 64 bits alone. This // is different from the OpenSSL implementation, so we implement the CTR loop @@ -597,7 +603,7 @@ class RenewalRoundTrip class EntitledMessage { public: EntitledMessage(LicenseRoundTrip* license_messages) - : license_messages_(license_messages), num_keys_() {} + : license_messages_(license_messages) {} void FillKeyArray(); void MakeOneKey(size_t entitlement_key_index); void SetEntitledKeySession(uint32_t key_session) { @@ -631,13 +637,13 @@ class EntitledMessage { void VerifyDecrypt(); LicenseRoundTrip* license_messages_; - uint32_t num_keys_; + uint32_t num_keys_ = 0; // Clear Entitlement key data. This is the backing data for // |entitled_key_array_|. - EntitledContentKeyData entitled_key_data_[kMaxNumKeys]; + EntitledContentKeyData entitled_key_data_[kMaxNumKeys] = {}; // Entitled key object. Pointers are backed by |entitled_key_data_|. - OEMCrypto_EntitledContentKeyObject entitled_key_array_[kMaxNumKeys]; - uint32_t entitled_key_session_; + OEMCrypto_EntitledContentKeyObject entitled_key_array_[kMaxNumKeys] = {}; + uint32_t entitled_key_session_ = 0; }; class Session { diff --git a/oemcrypto/test/oemcrypto_basic_test.cpp b/oemcrypto/test/oemcrypto_basic_test.cpp index 7b07053..c53a7a8 100644 --- a/oemcrypto/test/oemcrypto_basic_test.cpp +++ b/oemcrypto/test/oemcrypto_basic_test.cpp @@ -156,7 +156,7 @@ TEST_F(OEMCryptoClientTest, FreeUnallocatedSecureBufferNoFailure) { */ TEST_F(OEMCryptoClientTest, VersionNumber) { const std::string log_message = - "OEMCrypto unit tests for API 18.3. Tests last updated 2023-07-07"; + "OEMCrypto unit tests for API 18.4. Tests last updated 2023-08-07"; cout << " " << log_message << "\n"; cout << " " << "These tests are part of Android U." @@ -165,7 +165,7 @@ 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, 3); + EXPECT_EQ(ODK_MINOR_VERSION, 4); EXPECT_EQ(kCurrentAPI, static_cast(ODK_MAJOR_VERSION)); OEMCrypto_Security_Level level = OEMCrypto_SecurityLevel(); EXPECT_GT(level, OEMCrypto_Level_Unknown); @@ -200,6 +200,13 @@ TEST_F(OEMCryptoClientTest, VersionNumber) { if (build_info.size() != buf_length) { build_info.resize(buf_length); } + const std::string comma = ","; + const std::string pretty_comma = ",\n "; + std::string::size_type pos = 0; + while ((pos = build_info.find(comma, pos)) != std::string::npos) { + build_info.replace(pos, comma.size(), pretty_comma); + pos += pretty_comma.size(); + } cout << " BuildInformation: " << build_info << endl; OEMCrypto_WatermarkingSupport support = OEMCrypto_GetWatermarkingSupport(); cout << " WatermarkingSupport: " << support << endl; diff --git a/oemcrypto/test/oemcrypto_cast_test.cpp b/oemcrypto/test/oemcrypto_cast_test.cpp index 6dc2552..d3c38d4 100644 --- a/oemcrypto/test/oemcrypto_cast_test.cpp +++ b/oemcrypto/test/oemcrypto_cast_test.cpp @@ -11,21 +11,36 @@ using ::testing::Range; namespace wvoec { -// The alternate padding is only required for cast receivers, but all devices -// should forbid the alternate padding for regular certificates. -TEST_F(OEMCryptoLoadsCertificateAlternates, DisallowForbiddenPaddingAPI09) { - LoadWithAllowedSchemes(kSign_RSASSA_PSS, - true); // Use default padding scheme - DisallowForbiddenPadding(kSign_PKCS1_Block1, 50); -} - -// The alternate padding is only required for cast receivers, but if a device -// does load an alternate certificate, it should NOT use it for generating -// a license request signature. +/** 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) { // Try to load an RSA key with alternative padding schemes. This signing // scheme is used by cast receivers. - LoadWithAllowedSchemes(kSign_PKCS1_Block1, false); + LoadCastCertificateKey(false); + // If the device is a cast receiver, then this scheme is required. + if (global_features.cast_receiver) { + ASSERT_TRUE(key_loaded_); + } + // If the key loaded with no error, then we will verify that it is not used + // for forbidden padding schemes. + if (key_loaded_) { + if (global_features.cast_receiver) { + // A signature with a valid size should succeed. + TestSignature(kSign_PKCS1_Block1, 83); + TestSignature(kSign_PKCS1_Block1, 50); + } + // A signature with padding that is too big should fail. + DisallowForbiddenPaddingDRMKey(kSign_PKCS1_Block1, 84); // too big. + } +} + +/** The alternate padding is only required for cast receivers, but if a device + * does load an alternate certificate, it should NOT be used as a DRM cert + * key. */ +TEST_F(OEMCryptoLoadsCertificateAlternates, ForbidUseAsDRMCert) { + // Try to load an RSA key with alternative padding schemes. This signing + // scheme is used by cast receivers. + LoadCastCertificateKey(false); // If the device is a cast receiver, then this scheme is required. if (global_features.cast_receiver) { ASSERT_TRUE(key_loaded_); @@ -34,15 +49,41 @@ TEST_F(OEMCryptoLoadsCertificateAlternates, TestSignaturePKCS1) { // for forbidden padding schemes. if (key_loaded_) { // The other padding scheme should fail. - DisallowForbiddenPadding(kSign_RSASSA_PSS, 83); + DisallowForbiddenPaddingDRMKey(kSign_RSASSA_PSS, 83); DisallowDeriveKeys(); - if (global_features.cast_receiver) { - // A signature with a valid size should succeed. - TestSignature(kSign_PKCS1_Block1, 83); - TestSignature(kSign_PKCS1_Block1, 50); - } - // A signature with padding that is too big should fail. - DisallowForbiddenPadding(kSign_PKCS1_Block1, 84); // too big. + } +} + +/** A Cast receiver certificate private key cannot be used with the function + * PrepAndSignLicenseRequest. + */ +TEST_F(OEMCryptoLoadsCertificateAlternates, ForbidPrepAndSign) { + // Try to load an RSA key with alternative padding schemes. This signing + // scheme is used by cast receivers. + LoadCastCertificateKey(false); + // If the device is a cast receiver, then this scheme is required. + if (global_features.cast_receiver) { + ASSERT_TRUE(key_loaded_); + } + // If the key loaded with no error, then we will verify that it is not used + // for forbidden padding schemes. + if (key_loaded_) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(s.LoadWrappedRsaDrmKey(wrapped_drm_key_)); + s.GenerateNonce(); + + size_t core_message_length = 100; + std::vector message(128, 0); + std::vector signature(256, 0); + size_t signature_length = signature.size(); + + OEMCryptoResult result = OEMCrypto_PrepAndSignLicenseRequest( + s.session_id(), message.data(), message.size(), &core_message_length, + signature.data(), &signature_length); + ASSERT_EQ(OEMCrypto_ERROR_INVALID_KEY, result); + const vector zero(signature.size(), 0); + ASSERT_EQ(signature, zero); // Signature should not have been computed. } } @@ -275,7 +316,7 @@ TEST_F(OEMCryptoCastReceiverTest, SupportsCertificatesAPI13) { // # PKCS#1 v1.5 Signature Example 15.1 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_1) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "f45d55f35551e975d6a8dc7ea9f48859" "3940cc75694a278f27e578a163d839b3" @@ -314,7 +355,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_1) { // # PKCS#1 v1.5 Signature Example 15.2 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_2) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "c14b4c6075b2f9aad661def4ecfd3cb9" "33c623f4e63bf53410d2f016d1ab98e2" @@ -349,7 +390,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_2) { // # PKCS#1 v1.5 Signature Example 15.3 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_3) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "d02371ad7ee48bbfdb2763de7a843b94" "08ce5eb5abf847ca3d735986df84e906" @@ -390,7 +431,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_3) { // # PKCS#1 v1.5 Signature Example 15.4 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_4) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "29035584ab7e0226a9ec4b02e8dcf127" "2dc9a41d73e2820007b0f6e21feccd5b" @@ -419,7 +460,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_4) { // # PKCS#1 v1.5 Signature Example 15.5 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_5) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex("bda3a1c79059eae598308d3df609"); vector signature = wvutil::a2b_hex( "a156176cb96777c7fb96105dbd913bc4" @@ -444,7 +485,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_5) { // # PKCS#1 v1.5 Signature Example 15.6 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_6) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "c187915e4e87da81c08ed4356a0cceac" "1c4fb5c046b45281b387ec28f1abfd56" @@ -476,7 +517,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_6) { // # PKCS#1 v1.5 Signature Example 15.7 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_7) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "abfa2ecb7d29bd5bcb9931ce2bad2f74" "383e95683cee11022f08e8e7d0b8fa05" @@ -509,7 +550,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_7) { // # PKCS#1 v1.5 Signature Example 15.8 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_8) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "df4044a89a83e9fcbf1262540ae3038b" "bc90f2b2628bf2a4467ac67722d8546b" @@ -548,7 +589,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_8) { // # PKCS#1 v1.5 Signature Example 15.9 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_9) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "ea941ff06f86c226927fcf0e3b11b087" "2676170c1bfc33bda8e265c77771f9d0" @@ -585,7 +626,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_9) { // # PKCS#1 v1.5 Signature Example 15.10 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_10) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "d8b81645c13cd7ecf5d00ed2c91b9acd" "46c15568e5303c4a9775ede76b48403d" @@ -615,7 +656,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_10) { // # PKCS#1 v1.5 Signature Example 15.11 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_11) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "e5739b6c14c92d510d95b826933337ff" "0d24ef721ac4ef64c2bad264be8b44ef" @@ -649,7 +690,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_11) { // # PKCS#1 v1.5 Signature Example 15.12 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_12) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "7af42835917a88d6b3c6716ba2f5b0d5" "b20bd4e2e6e574e06af1eef7c81131be" @@ -690,7 +731,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_12) { // # PKCS#1 v1.5 Signature Example 15.13 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_13) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "ebaef3f9f23bdfe5fa6b8af4c208c189" "f2251bf32f5f137b9de4406378686b3f" @@ -719,7 +760,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_13) { // # PKCS#1 v1.5 Signature Example 15.14 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_14) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "c5a2711278761dfcdd4f0c99e6f5619d" "6c48b5d4c1a80982faa6b4cf1cf7a60f" @@ -755,7 +796,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_14) { // # PKCS#1 v1.5 Signature Example 15.15 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_15) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "9bf8aa253b872ea77a7e23476be26b23" "29578cf6ac9ea2805b357f6fc3ad130d" @@ -794,7 +835,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_15) { // # PKCS#1 v1.5 Signature Example 15.16 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_16) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "32474830e2203754c8bf0681dc4f842a" "fe360930378616c108e833656e5640c8" @@ -835,7 +876,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_16) { // # PKCS#1 v1.5 Signature Example 15.17 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_17) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "008e59505eafb550aae5e845584cebb0" "0b6de1733e9f95d42c882a5bbeb5ce1c" @@ -864,7 +905,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_17) { // # PKCS#1 v1.5 Signature Example 15.18 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_18) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "6abc54cf8d1dff1f53b17d8160368878" "a8788cc6d22fa5c2258c88e660b09a89" @@ -894,7 +935,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_18) { // # PKCS#1 v1.5 Signature Example 15.19 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_19) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "af2d78152cf10efe01d274f217b177f6" "b01b5e749f1567715da324859cd3dd88" @@ -931,7 +972,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_19) { // # PKCS#1 v1.5 Signature Example 15.20 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_20) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "40ee992458d6f61486d25676a96dd2cb" "93a37f04b178482f2b186cf88215270d" @@ -974,4 +1015,4 @@ TEST_P(OEMCryptoSessionTestLoadCasKeysWithHDCP, CasOnlyLoadCasKeysAPI17) { } INSTANTIATE_TEST_SUITE_P(TestHDCP, OEMCryptoSessionTestLoadCasKeysWithHDCP, Range(1, 6)); -} // namespace wvoec \ No newline at end of file +} // namespace wvoec diff --git a/oemcrypto/test/oemcrypto_cast_test.h b/oemcrypto/test/oemcrypto_cast_test.h index b6e2116..c812ebe 100644 --- a/oemcrypto/test/oemcrypto_cast_test.h +++ b/oemcrypto/test/oemcrypto_cast_test.h @@ -25,36 +25,6 @@ std::string MaybeHex(const std::vector& data); // This test attempts to use alternate algorithms for loaded device certs. class OEMCryptoLoadsCertificateAlternates : public OEMCryptoLoadsCertificate { protected: - void DisallowForbiddenPadding(RSA_Padding_Scheme scheme, size_t size) { - OEMCryptoResult sts; - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(s.LoadWrappedRsaDrmKey(wrapped_drm_key_)); - - // Sign a Message - vector licenseRequest(size); - GetRandBytes(licenseRequest.data(), licenseRequest.size()); - size_t signature_length = 256; - vector signature(signature_length); - sts = OEMCrypto_GenerateRSASignature( - s.session_id(), licenseRequest.data(), licenseRequest.size(), - signature.data(), &signature_length, scheme); - // Allow OEMCrypto to request a full buffer. - if (sts == OEMCrypto_ERROR_SHORT_BUFFER) { - ASSERT_NE(static_cast(0), signature_length); - signature.assign(signature_length, 0); - sts = OEMCrypto_GenerateRSASignature( - s.session_id(), licenseRequest.data(), licenseRequest.size(), - signature.data(), &signature_length, scheme); - } - - EXPECT_NE(OEMCrypto_SUCCESS, sts) - << "Signed with forbidden padding scheme=" << (int)scheme - << ", size=" << (int)size; - const vector zero(signature.size(), 0); - ASSERT_EQ(zero, signature); // signature should not be computed. - } - void TestSignature(RSA_Padding_Scheme scheme, size_t size) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -105,7 +75,9 @@ class OEMCryptoLoadsCertificateAlternates : public OEMCryptoLoadsCertificate { } // If force is true, we assert that the key loads successfully. - void LoadWithAllowedSchemes(uint32_t schemes, bool force) { + void LoadCastCertificateKey(bool force) { + // Padding scheme used to sign cast data. + constexpr uint32_t schemes = kSign_PKCS1_Block1; // prov 2 or prov 3 if (global_features.provisioning_method == OEMCrypto_Keybox || global_features.provisioning_method == OEMCrypto_OEMCertificate) { diff --git a/oemcrypto/test/oemcrypto_decrypt_test.cpp b/oemcrypto/test/oemcrypto_decrypt_test.cpp index 22c810c..10c3254 100644 --- a/oemcrypto/test/oemcrypto_decrypt_test.cpp +++ b/oemcrypto/test/oemcrypto_decrypt_test.cpp @@ -540,6 +540,17 @@ TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptMaxSubsample) { ASSERT_NO_FATAL_FAILURE(TestDecryptCENC()); } +TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptZeroSizeSubSample) { + ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({ + {10, 10}, + {0, 0}, + })); + ASSERT_NO_FATAL_FAILURE(LoadLicense()); + ASSERT_NO_FATAL_FAILURE(MakeBuffers()); + ASSERT_NO_FATAL_FAILURE(EncryptData()); + ASSERT_NO_FATAL_FAILURE(TestDecryptCENC()); +} + // There are probably no frames this small, but we should handle them anyway. TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptSmallBuffer) { ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({ diff --git a/oemcrypto/test/oemcrypto_decrypt_test.h b/oemcrypto/test/oemcrypto_decrypt_test.h index 9f9b500..36a249c 100644 --- a/oemcrypto/test/oemcrypto_decrypt_test.h +++ b/oemcrypto/test/oemcrypto_decrypt_test.h @@ -98,6 +98,10 @@ class OEMCryptoSessionTestsDecryptTests protected: void SetUp() override { OEMCryptoLicenseTestAPI16::SetUp(); + if (wvoec::global_features.derive_key_method == + wvoec::DeviceFeatures::NO_METHOD) { + GTEST_SKIP() << "Test for devices that can derive session keys only."; + } pattern_ = ::testing::get<0>(GetParam()); cipher_mode_ = ::testing::get<1>(GetParam()); decrypt_inplace_ = ::testing::get<2>(GetParam()).decrypt_inplace; diff --git a/oemcrypto/test/oemcrypto_license_test.cpp b/oemcrypto/test/oemcrypto_license_test.cpp index fedd8f9..1f47fd3 100644 --- a/oemcrypto/test/oemcrypto_license_test.cpp +++ b/oemcrypto/test/oemcrypto_license_test.cpp @@ -84,10 +84,6 @@ void TestMaxKeys(SessionUtil* util, size_t num_keys_per_session) { } } -TEST_F(OEMCryptoSessionTestKeyboxTest, TestKeyboxIsValid) { - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_IsKeyboxValid()); -} - TEST_F(OEMCryptoSessionTests, OEMCryptoMemoryPrepareLicenseRequestForHugeRequestMessageLength) { TestPrepareLicenseRequestForHugeBufferLengths( @@ -967,4 +963,4 @@ INSTANTIATE_TEST_SUITE_P(TestAPI16, OEMCryptoRefreshTestAPI16, Range(kCoreMessagesAPI, kCurrentAPI + 1)); /// @} -} // namespace wvoec \ No newline at end of file +} // namespace wvoec diff --git a/oemcrypto/test/oemcrypto_license_test.h b/oemcrypto/test/oemcrypto_license_test.h index a286050..8d7339b 100644 --- a/oemcrypto/test/oemcrypto_license_test.h +++ b/oemcrypto/test/oemcrypto_license_test.h @@ -39,6 +39,10 @@ class OEMCryptoSessionTests : public OEMCryptoClientTest { void SetUp() override { OEMCryptoClientTest::SetUp(); + if (wvoec::global_features.derive_key_method == + wvoec::DeviceFeatures::NO_METHOD) { + GTEST_SKIP() << "Test for devices that can derive session keys only."; + } EnsureTestROT(); if (global_features.usage_table) { CreateUsageTableHeader(); @@ -92,8 +96,6 @@ class OEMCryptoSessionTests : public OEMCryptoClientTest { } }; -class OEMCryptoSessionTestKeyboxTest : public OEMCryptoSessionTests {}; - // This class is for testing a single license with the default API version // of 16. class OEMCryptoLicenseTestAPI16 : public OEMCryptoSessionTests { @@ -407,4 +409,4 @@ class OEMCryptoRefreshTestAPI16 : public OEMCryptoRefreshTest {}; } // namespace wvoec -#endif // CDM_OEMCRYPTO_LICENSE_TEST_ \ No newline at end of file +#endif // CDM_OEMCRYPTO_LICENSE_TEST_ diff --git a/oemcrypto/test/oemcrypto_provisioning_test.cpp b/oemcrypto/test/oemcrypto_provisioning_test.cpp index 5504e76..47d7228 100644 --- a/oemcrypto/test/oemcrypto_provisioning_test.cpp +++ b/oemcrypto/test/oemcrypto_provisioning_test.cpp @@ -119,36 +119,24 @@ TEST_F(OEMCryptoProv30Test, OEMCertValid) { ASSERT_NO_FATAL_FAILURE(s.LoadOEMCert(kVerify)); // Load and verify. } -// This verifies that the OEM Certificate cannot be used for other RSA padding -// schemes. Those schemes should only be used by cast receiver certificates. -TEST_F(OEMCryptoProv30Test, OEMCertForbiddenPaddingScheme) { +/** This verifies that the OEM Certificate cannot be used with + * GenerateRSASignature. + */ +TEST_F(OEMCryptoProv30Test, OEMCertForbidGenerateRSASignature1) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(s.LoadOEMCert()); - OEMCryptoResult sts; - // Sign a Message - vector data(500); - GetRandBytes(data.data(), data.size()); - size_t signature_length = 0; - // We need a size one vector to pass as a pointer. - vector signature(1, 0); - vector zero(1, 0); + DisallowForbiddenPadding(s.session_id(), kSign_PKCS1_Block1, 80); +} - sts = OEMCrypto_GenerateRSASignature(s.session_id(), data.data(), data.size(), - signature.data(), &signature_length, - kSign_PKCS1_Block1); - if (OEMCrypto_ERROR_SHORT_BUFFER == sts) { - // The OEMCrypto could complain about buffer length first, so let's - // resize and check if it's writing to the signature again. - signature.resize(signature_length, 0); - zero.resize(signature_length, 0); - sts = OEMCrypto_GenerateRSASignature(s.session_id(), data.data(), - data.size(), signature.data(), - &signature_length, kSign_PKCS1_Block1); - } - EXPECT_NE(OEMCrypto_SUCCESS, sts) - << "OEM Cert Signed with forbidden kSign_PKCS1_Block1."; - ASSERT_EQ(zero, signature); // signature should not be computed. +/** This verifies that the OEM Certificate cannot be used with + * GenerateRSASignature. + */ +TEST_F(OEMCryptoProv30Test, OEMCertForbidGenerateRSASignature2) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(s.LoadOEMCert()); + DisallowForbiddenPadding(s.session_id(), kSign_RSASSA_PSS, 80); } // Calling OEMCrypto_GetOEMPublicCertificate should not change the session's @@ -186,6 +174,46 @@ TEST_F(OEMCryptoProv30Test, GetCertOnlyAPI16) { ASSERT_EQ(OEMCrypto_SUCCESS, license_messages.LoadResponse()); } +/** This verifies that the OEM Certificate cannot be used with + * GenerateRSASignature. + */ +TEST_F(OEMCryptoProv40Test, OEMCertForbidGenerateRSASignature1) { + // Create an OEM Cert and save it for later. + Session s1; + ASSERT_NO_FATAL_FAILURE(s1.open()); + ASSERT_NO_FATAL_FAILURE(CreateProv4OEMKey(&s1)); + ASSERT_EQ(s1.IsPublicKeySet(), true); + s1.close(); + Session s2; + ASSERT_NO_FATAL_FAILURE(s2.open()); + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_InstallOemPrivateKey( + s2.session_id(), oem_key_type_, + reinterpret_cast(wrapped_oem_key_.data()), + wrapped_oem_key_.size())); + DisallowForbiddenPadding(s2.session_id(), kSign_PKCS1_Block1, 80); +} + +/** This verifies that the OEM Certificate cannot be used with + * GenerateRSASignature. + */ +TEST_F(OEMCryptoProv40Test, OEMCertForbidGenerateRSASignature2) { + // Create an OEM Cert and save it for later. + Session s1; + ASSERT_NO_FATAL_FAILURE(s1.open()); + ASSERT_NO_FATAL_FAILURE(CreateProv4OEMKey(&s1)); + ASSERT_EQ(s1.IsPublicKeySet(), true); + s1.close(); + Session s2; + ASSERT_NO_FATAL_FAILURE(s2.open()); + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_InstallOemPrivateKey( + s2.session_id(), oem_key_type_, + reinterpret_cast(wrapped_oem_key_.data()), + wrapped_oem_key_.size())); + DisallowForbiddenPadding(s2.session_id(), kSign_RSASSA_PSS, 80); +} + // This verifies that the device really does claim to have BCC. // It should be filtered out for devices that have a keybox or factory OEM // cert. @@ -539,7 +567,7 @@ TEST_F(OEMCryptoProv40Test, InstallOemPrivateKeyCanBeUsed) { * cert. */ TEST_F(OEMCryptoProv40Test, OEMPrivateKeyCannotBeDRMKey) { - // Create an OEM Cert and save it for alter. + // Create an OEM Cert and save it for later. Session s1; ASSERT_NO_FATAL_FAILURE(s1.open()); ASSERT_NO_FATAL_FAILURE(CreateProv4OEMKey(&s1)); @@ -654,7 +682,22 @@ TEST_P(OEMCryptoProv40CastTest, ProvisionCastWorks) { INSTANTIATE_TEST_SUITE_P(Prov4CastProvisioningBasic, OEMCryptoProv40CastTest, testing::Values(true, false)); +// Verify that you cannot use GenerateRSASignature with a normal DRM Cert. +// that function needs a cast cert. +TEST_F(OEMCryptoLoadsCertificate, ForbidRSASignatureForDRMKey1) { + DisallowForbiddenPadding(session_.session_id(), kSign_RSASSA_PSS, 80); +} + +TEST_F(OEMCryptoLoadsCertificate, ForbidRSASignatureForDRMKey2) { + DisallowForbiddenPadding(session_.session_id(), kSign_PKCS1_Block1, 80); +} + TEST_F(OEMCryptoLoadsCertificate, PrepAndSignLicenseRequestCounterAPI18) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } ASSERT_NO_FATAL_FAILURE(CreateWrappedDRMKey()); Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -675,6 +718,11 @@ TEST_F(OEMCryptoLoadsCertificate, PrepAndSignLicenseRequestCounterAPI18) { // This test verifies that we can create a wrapped RSA key, and then reload it. TEST_F(OEMCryptoLoadsCertificate, LoadRSASessionKey) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } ASSERT_NO_FATAL_FAILURE(CreateWrappedDRMKey()); Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -682,6 +730,11 @@ TEST_F(OEMCryptoLoadsCertificate, LoadRSASessionKey) { } TEST_F(OEMCryptoLoadsCertificate, SignProvisioningRequest) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } Session s; ASSERT_NO_FATAL_FAILURE(s.open()); if (global_features.provisioning_method == OEMCrypto_OEMCertificate) { @@ -696,6 +749,11 @@ TEST_F(OEMCryptoLoadsCertificate, SignProvisioningRequest) { // This tests a large message size. The size is larger than we required in v15. TEST_F(OEMCryptoLoadsCertificate, SignLargeProvisioningRequestAPI16) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } Session s; ASSERT_NO_FATAL_FAILURE(s.open()); if (global_features.provisioning_method == OEMCrypto_OEMCertificate) { @@ -714,6 +772,11 @@ TEST_F(OEMCryptoLoadsCertificate, SignLargeProvisioningRequestAPI16) { // unencrypted key is not found in the wrapped key. The wrapped key should be // encrypted. TEST_F(OEMCryptoLoadsCertificate, CertificateProvision) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } Session s; ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); provisioning_messages.PrepareSession(keybox_); @@ -730,6 +793,11 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvision) { // Verify that RewrapDeviceRSAKey checks pointers are within the provisioning // message. TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange1_API16) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } Session s; ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); provisioning_messages.PrepareSession(keybox_); @@ -747,6 +815,11 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange1_API16) { // Verify that RewrapDeviceRSAKey checks pointers are within the provisioning // message. TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange2_API16) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } Session s; ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); provisioning_messages.PrepareSession(keybox_); @@ -764,6 +837,11 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange2_API16) { // Verify that RewrapDeviceRSAKey checks pointers are within the provisioning // message. TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange3_API16) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } Session s; ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); provisioning_messages.PrepareSession(keybox_); @@ -783,6 +861,11 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange3_API16) { // Verify that RewrapDeviceRSAKey checks pointers are within the provisioning // message. TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange4_API16) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } Session s; ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); provisioning_messages.PrepareSession(keybox_); @@ -802,6 +885,11 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange4_API16) { // Verify that RewrapDeviceRSAKey checks pointers are within the provisioning // message. TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange5Prov30_API16) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } if (global_features.provisioning_method != OEMCrypto_OEMCertificate) { GTEST_SKIP() << "Test for Prov 3.0 devices only."; } @@ -825,6 +913,14 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange5Prov30_API16) { // TODO(b/144186970): This test should also run on Prov 3.0 devices. TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadSignatureKeyboxTestAPI16) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } + if (global_features.provisioning_method != OEMCrypto_Keybox) { + GTEST_SKIP() << "Test for Prov 2.0 devices only."; + } Session s; ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); provisioning_messages.PrepareSession(keybox_); @@ -839,6 +935,11 @@ TEST_F(OEMCryptoLoadsCertificate, // Test that RewrapDeviceRSAKey verifies the nonce is current. TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadNonce_API16) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } Session s; ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); provisioning_messages.PrepareSession(keybox_); @@ -853,6 +954,11 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadNonce_API16) { // Test that RewrapDeviceRSAKey verifies the RSA key is valid. TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRSAKey) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } Session s; ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); provisioning_messages.PrepareSession(keybox_); @@ -868,6 +974,14 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRSAKey) { // TODO(b/144186970): This test should also run on Prov 3.0 devices. TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRSAKeyKeyboxTestAPI16) { + if (global_features.provisioning_method != OEMCrypto_Keybox) { + GTEST_SKIP() << "Test for Prov 2.0 devices only."; + } + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } Session s; ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); provisioning_messages.PrepareSession(keybox_); @@ -887,6 +1001,11 @@ TEST_F(OEMCryptoLoadsCertificate, // Test that RewrapDeviceRSAKey accepts the maximum message size. TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionLargeBuffer) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } Session s; ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); const size_t max_size = GetResourceValue(kLargeMessageSize); @@ -904,6 +1023,11 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionLargeBuffer) { // Test that a wrapped RSA key can be loaded. TEST_F(OEMCryptoLoadsCertificate, LoadWrappedRSAKey) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } ASSERT_NO_FATAL_FAILURE(CreateWrappedDRMKey()); Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -912,6 +1036,15 @@ TEST_F(OEMCryptoLoadsCertificate, LoadWrappedRSAKey) { class OEMCryptoLoadsCertVariousKeys : public OEMCryptoLoadsCertificate { public: + void SetUp() override { + OEMCryptoLoadsCertificate::SetUp(); + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } + } + void TestKey(const uint8_t* key, size_t key_length) { encoded_rsa_key_.assign(key, key + key_length); ASSERT_NO_FATAL_FAILURE(CreateWrappedDRMKey()); @@ -987,6 +1120,11 @@ TEST_F(OEMCryptoLoadsCertVariousKeys, TestEulerZeroNormalDer) { // This tests that two sessions can use different RSA keys simultaneously. TEST_F(OEMCryptoLoadsCertificate, TestMultipleRSAKeys) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } ASSERT_NO_FATAL_FAILURE(CreateWrappedDRMKey()); Session s1; // Session s1 loads the default rsa key, but doesn't use it // until after s2 uses its key. @@ -1023,6 +1161,11 @@ TEST_F(OEMCryptoLoadsCertificate, TestMultipleRSAKeys) { // This tests the maximum number of DRM private keys that OEMCrypto can load TEST_F(OEMCryptoLoadsCertificate, TestMaxDRMKeys) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } const size_t max_total_keys = GetResourceValue(kMaxTotalDRMPrivateKeys); std::vector> sessions; std::vector> licenses; @@ -1090,6 +1233,11 @@ TEST_F(OEMCryptoLoadsCertificate, TestMaxDRMKeys) { // Devices that load certificates, should at least support RSA 2048 keys. TEST_F(OEMCryptoLoadsCertificate, SupportsCertificatesAPI13) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } ASSERT_NE(0u, OEMCrypto_Supports_RSA_2048bit & OEMCrypto_SupportedCertificates()) << "Supported certificates is only " << OEMCrypto_SupportedCertificates(); @@ -1098,6 +1246,11 @@ TEST_F(OEMCryptoLoadsCertificate, SupportsCertificatesAPI13) { // This test is not run by default, because it takes a long time and // is used to measure RSA performance, not test functionality. TEST_F(OEMCryptoLoadsCertificate, RSAPerformance) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } const std::chrono::milliseconds kTestDuration(5000); OEMCryptoResult sts; std::chrono::steady_clock clock; @@ -1199,7 +1352,9 @@ TEST_F(OEMCryptoLoadsCertificate, RSAPerformance) { delta_time / std::chrono::milliseconds(1) / count; OEMCrypto_Security_Level level = OEMCrypto_SecurityLevel(); - printf("PERF:head, security, provision (ms), lic req(ms), derive keys(ms)\n"); + printf( + "PERF:head, security, provision (ms), lic req(ms), derive " + "keys(ms)\n"); printf("PERF:stat, %u, %8.3f, %8.3f, %8.3f\n", static_cast(level), provision_time, license_request_time, derive_keys_time); @@ -1225,4 +1380,4 @@ TEST_F(OEMCryptoUsesCertificate, GenerateDerivedKeysLargeBuffer) { enc_context.data(), enc_context.size())); } -} // namespace wvoec \ No newline at end of file +} // namespace wvoec diff --git a/oemcrypto/test/oemcrypto_provisioning_test.h b/oemcrypto/test/oemcrypto_provisioning_test.h index b44369c..8877b17 100644 --- a/oemcrypto/test/oemcrypto_provisioning_test.h +++ b/oemcrypto/test/oemcrypto_provisioning_test.h @@ -17,62 +17,84 @@ namespace wvoec { -// Tests using this class are only used for devices with a keybox. They are not -// run for devices with an OEM Certificate. -class OEMCryptoKeyboxTest : public OEMCryptoClientTest { - void SetUp() override { - OEMCryptoClientTest::SetUp(); - OEMCryptoResult sts = OEMCrypto_IsKeyboxValid(); - // If the production keybox is valid, use it for these tests. Most of the - // other tests will use a test keybox anyway, but it's nice to check the - // device ID for the real keybox if we can. - if (sts == OEMCrypto_SUCCESS) return; - printf("Production keybox is NOT valid. All tests use test keybox.\n"); - ASSERT_EQ( - OEMCrypto_SUCCESS, - OEMCrypto_LoadTestKeybox(reinterpret_cast(&kTestKeybox), - sizeof(kTestKeybox))); - } -}; - -// This class is for tests that have an OEM Certificate instead of a keybox. -class OEMCryptoProv30Test : public OEMCryptoClientTest { - void SetUp() override { - OEMCryptoClientTest::SetUp(); - if (global_features.provisioning_method != OEMCrypto_OEMCertificate) { - GTEST_SKIP() << "Test for Prov 3.0 devices only."; - } - } -}; - -// This class is for tests that have boot certificate chain instead of a keybox. -class OEMCryptoProv40Test : public OEMCryptoClientTest { - void SetUp() override { - OEMCryptoClientTest::SetUp(); - if (global_features.provisioning_method != OEMCrypto_BootCertificateChain) { - GTEST_SKIP() << "Test for Prov 4.0 devices only."; - } - } -}; - -class OEMCryptoProv40CastTest : public OEMCryptoClientTest, - public testing::WithParamInterface { - void SetUp() override { - OEMCryptoClientTest::SetUp(); - if (!global_features.cast_receiver) { - GTEST_SKIP() << "Test for cast devices only."; - } - if (global_features.provisioning_method != OEMCrypto_BootCertificateChain) { - GTEST_SKIP() << "Test for Prov 4.0 devices only."; - } - } -}; - // // Certificate Root of Trust Tests // -class OEMCryptoLoadsCertificate : public OEMCryptoSessionTestKeyboxTest { +// These tests are run by all L1 devices that load and use certificates. It is +// also run by a few L3 devices that use a baked in certificate, but cannot load +// a certificate. +class OEMCryptoUsesCertificate : public OEMCryptoSessionTests { protected: + void SetUp() override { + OEMCryptoSessionTests::SetUp(); + ASSERT_NO_FATAL_FAILURE(session_.open()); + if (global_features.derive_key_method == + DeviceFeatures::LOAD_TEST_RSA_KEY) { + ASSERT_NO_FATAL_FAILURE(session_.SetRsaPublicKeyFromPrivateKeyInfo( + encoded_rsa_key_.data(), encoded_rsa_key_.size())); + } else { + InstallTestDrmKey(&session_); + } + } + + void TearDown() override { + ASSERT_NO_FATAL_FAILURE(session_.close()); + OEMCryptoSessionTests::TearDown(); + } + + Session session_; +}; + +/** These tests cover all systems that can load a DRM Certificate. That includes + * Provisioning 2, 3 and 4. */ +class OEMCryptoLoadsCertificate : public OEMCryptoUsesCertificate { + protected: + void SetUp() override { + OEMCryptoUsesCertificate::SetUp(); + if (!global_features.loads_certificate) { + GTEST_SKIP() << "Test for devices that load a DRM certificate only."; + } + } + + /** Verify that the specified padding scheme does not work with the DRM + * key and the function OEMCrypto_GenerateRSASignature. */ + void DisallowForbiddenPaddingDRMKey(RSA_Padding_Scheme scheme, size_t size) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(s.LoadWrappedRsaDrmKey(wrapped_drm_key_)); + DisallowForbiddenPadding(s.session_id(), scheme, size); + } + + /** Verify that the specified padding scheme does not work with whichever key + * is currently loaded into the specified session and the function + * OEMCrypto_GenerateRSASignature. */ + void DisallowForbiddenPadding(OEMCrypto_SESSION session, + RSA_Padding_Scheme scheme, size_t size) { + OEMCryptoResult sts; + // Sign a Message + vector message(size); + GetRandBytes(message.data(), message.size()); + size_t signature_length = 256; + vector signature(signature_length); + sts = OEMCrypto_GenerateRSASignature(session, message.data(), + message.size(), signature.data(), + &signature_length, scheme); + // Allow OEMCrypto to request a full buffer. + if (sts == OEMCrypto_ERROR_SHORT_BUFFER) { + ASSERT_NE(static_cast(0), signature_length); + signature.assign(signature_length, 0); + sts = OEMCrypto_GenerateRSASignature(session, message.data(), + message.size(), signature.data(), + &signature_length, scheme); + } + + EXPECT_NE(OEMCrypto_SUCCESS, sts) + << "Signed with forbidden padding scheme=" << (int)scheme + << ", size=" << (int)size; + const vector zero(signature.size(), 0); + ASSERT_EQ(zero, signature); // signature should not be computed. + } + void TestPrepareProvisioningRequestForHugeBufferLengths( const std::function f, bool check_status) { @@ -139,31 +161,63 @@ class OEMCryptoLoadsCertificate : public OEMCryptoSessionTestKeyboxTest { } }; -// These tests are run by all L1 devices that load and use certificates. It is -// also run by a few L3 devices that use a baked in certificate, but cannot load -// a certificate. -class OEMCryptoUsesCertificate : public OEMCryptoLoadsCertificate { +// Tests using this class are only used for devices with a keybox. They are not +// run for devices with an OEM Certificate. +class OEMCryptoKeyboxTest : public OEMCryptoLoadsCertificate { protected: void SetUp() override { OEMCryptoLoadsCertificate::SetUp(); - ASSERT_NO_FATAL_FAILURE(session_.open()); - if (global_features.derive_key_method == - DeviceFeatures::LOAD_TEST_RSA_KEY) { - ASSERT_NO_FATAL_FAILURE(session_.SetRsaPublicKeyFromPrivateKeyInfo( - encoded_rsa_key_.data(), encoded_rsa_key_.size())); - } else { - InstallTestDrmKey(&session_); + if (global_features.provisioning_method != OEMCrypto_Keybox) { + GTEST_SKIP() << "Test for Prov 2.0 devices only."; + } + OEMCryptoResult sts = OEMCrypto_IsKeyboxValid(); + // If the production keybox is valid, use it for these tests. Most of the + // other tests will use a test keybox anyway, but it's nice to check the + // device ID for the real keybox if we can. + if (sts == OEMCrypto_SUCCESS) return; + printf("Production keybox is NOT valid. All tests use test keybox.\n"); + ASSERT_EQ( + OEMCrypto_SUCCESS, + OEMCrypto_LoadTestKeybox(reinterpret_cast(&kTestKeybox), + sizeof(kTestKeybox))); + ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_IsKeyboxValid()) + << "After loading Test keybox, the keybox was still not valid."; + } +}; + +// This class is for tests that have an OEM Certificate instead of a keybox. +class OEMCryptoProv30Test : public OEMCryptoLoadsCertificate { + protected: + void SetUp() override { + OEMCryptoLoadsCertificate::SetUp(); + if (global_features.provisioning_method != OEMCrypto_OEMCertificate) { + GTEST_SKIP() << "Test for Prov 3.0 devices only."; } } +}; - void TearDown() override { - ASSERT_NO_FATAL_FAILURE(session_.close()); - OEMCryptoLoadsCertificate::TearDown(); +// This class is for tests that have boot certificate chain instead of a keybox. +class OEMCryptoProv40Test : public OEMCryptoLoadsCertificate { + protected: + void SetUp() override { + OEMCryptoLoadsCertificate::SetUp(); + if (global_features.provisioning_method != OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for Prov 4.0 devices only."; + } } +}; - Session session_; +class OEMCryptoProv40CastTest : public OEMCryptoProv40Test, + public testing::WithParamInterface { + protected: + void SetUp() override { + OEMCryptoProv40Test::SetUp(); + if (!global_features.cast_receiver) { + GTEST_SKIP() << "Test for cast devices only."; + } + } }; } // namespace wvoec -#endif // CDM_OEMCRYPTO_PROVISIONING_TEST_ \ No newline at end of file +#endif // CDM_OEMCRYPTO_PROVISIONING_TEST_ diff --git a/oemcrypto/test/oemcrypto_security_test.cpp b/oemcrypto/test/oemcrypto_security_test.cpp index af9b046..48cd73b 100644 --- a/oemcrypto/test/oemcrypto_security_test.cpp +++ b/oemcrypto/test/oemcrypto_security_test.cpp @@ -606,6 +606,12 @@ TEST_P(OEMCryptoSessionTestsDecryptTests, TEST_F(OEMCryptoLoadsCertificate, OEMCryptoMemoryLoadProvisioningForHugeSignatureLength) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } auto oemcrypto_function = [&](size_t signature_size) { Session s; ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); @@ -638,6 +644,12 @@ TEST_F(OEMCryptoLoadsCertificate, TEST_F(OEMCryptoLoadsCertificate, OEMCryptoMemoryLoadProvisioningForHugeWrappedRsaKeyLength) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } auto oemcrypto_function = [&](size_t buffer_length) { Session s; ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); @@ -663,6 +675,12 @@ TEST_F(OEMCryptoLoadsCertificate, TEST_F(OEMCryptoLoadsCertificate, OEMCryptoMemoryLoadDrmPrivateKeyForHugeWrappedRsaKeyLength) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } ASSERT_NO_FATAL_FAILURE(CreateWrappedDRMKey()); auto oemcrypto_function = [&](size_t wrapped_rsa_key_length) { Session s; @@ -685,6 +703,12 @@ TEST_F(OEMCryptoLoadsCertificate, TEST_F( OEMCryptoLoadsCertificate, OEMCryptoMemoryLoadDrmPrivateKeyForHugeWrappedRsaKeyLengthStartingFromLength1) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } ASSERT_NO_FATAL_FAILURE(CreateWrappedDRMKey()); auto oemcrypto_function = [&](size_t wrapped_rsa_key_length) { Session s; @@ -765,8 +789,14 @@ TEST_F(OEMCryptoUsesCertificate, TEST_F(OEMCryptoLoadsCertificateAlternates, OEMCryptoMemoryGenerateRSASignatureForHugeBuffer) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } OEMCryptoResult sts; - LoadWithAllowedSchemes(kSign_PKCS1_Block1, false); + LoadCastCertificateKey(false); // If the device is a cast receiver, then this scheme is required. if (global_features.cast_receiver) { ASSERT_TRUE(key_loaded_); @@ -782,6 +812,7 @@ TEST_F(OEMCryptoLoadsCertificateAlternates, sts = OEMCrypto_GenerateRSASignature(s.session_id(), message_buffer.data(), message_buffer.size(), nullptr, &signature_length, kSign_PKCS1_Block1); + if (sts == OEMCrypto_ERROR_NOT_IMPLEMENTED) return; ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); ASSERT_NE(static_cast(0), signature_length); vector signature(signature_length); @@ -799,7 +830,13 @@ TEST_F(OEMCryptoLoadsCertificateAlternates, TEST_F(OEMCryptoLoadsCertificateAlternates, OEMCryptoMemoryGenerateRSASignatureForHugeSignatureLength) { - LoadWithAllowedSchemes(kSign_PKCS1_Block1, false); + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } + LoadCastCertificateKey(false); // If the device is a cast receiver, then this scheme is required. if (global_features.cast_receiver) { ASSERT_TRUE(key_loaded_); diff --git a/oemcrypto/test/oemcrypto_test.cpp b/oemcrypto/test/oemcrypto_test.cpp index 4452e93..626fe2d 100644 --- a/oemcrypto/test/oemcrypto_test.cpp +++ b/oemcrypto/test/oemcrypto_test.cpp @@ -443,6 +443,9 @@ TEST_P(OEMCryptoEntitlementLicenseTest, */ TEST_P(OEMCryptoEntitlementLicenseTest, LoadEntitlementKeysOemcryptoSessionAPI17) { + if (!global_features.supports_cas) { + GTEST_SKIP() << "OEMCrypto does not support CAS"; + } LoadEntitlementLicense(); uint32_t key_session_id = 0; ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_CreateEntitledKeySession( @@ -724,6 +727,9 @@ TEST_P(OEMCryptoLicenseTest, SelectKeyEntitledKeyNotThereAPI17) { * id. */ TEST_P(OEMCryptoLicenseTest, SelectKeyEntitlementKeyAPI17) { + if (!global_features.supports_cas) { + GTEST_SKIP() << "OEMCrypto does not support CAS"; + } license_messages_.set_license_type(OEMCrypto_EntitlementLicense); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); @@ -799,6 +805,9 @@ TEST_P(OEMCryptoLicenseTest, // This verifies that multiple entitled key sessions can be created. They can // load and select keys independently. TEST_P(OEMCryptoLicenseTest, EntitledKeySessionMultipleKeySessionsAPI17) { + if (!global_features.supports_cas) { + GTEST_SKIP() << "OEMCrypto does not support CAS"; + } license_messages_.set_license_type(OEMCrypto_EntitlementLicense); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); @@ -826,9 +835,7 @@ TEST_P(OEMCryptoLicenseTest, EntitledKeySessionMultipleKeySessionsAPI17) { session_.session_id(), &key_session_id_2); // For DRM, but not for CAS, we allow there to be only a single entitled // session. - if (!global_features.supports_cas && - (key_session_id_2 == key_session_id_1 || - status == OEMCrypto_ERROR_TOO_MANY_SESSIONS)) { + if (status == OEMCrypto_ERROR_TOO_MANY_SESSIONS) { GTEST_SKIP() << "Skipping test because multiple entitled sessions not supported."; } @@ -1420,6 +1427,12 @@ INSTANTIATE_TEST_SUITE_P(TestAll, OEMCryptoLicenseOverflowTest, TEST_F(OEMCryptoLoadsCertificate, OEMCryptoMemoryLoadProvisioningForHugeResponseLength) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } TestLoadProvisioningForHugeBufferLengths( [](size_t message_size, ProvisioningRoundTrip* provisioning_messages) { provisioning_messages->set_message_size(message_size); @@ -1429,6 +1442,12 @@ TEST_F(OEMCryptoLoadsCertificate, TEST_F(OEMCryptoLoadsCertificate, OEMCryptoMemoryLoadProvisioningForHugeCoreMessageLength) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } TestLoadProvisioningForHugeBufferLengths( [](size_t message_size, ProvisioningRoundTrip* provisioning_messages) { provisioning_messages->set_core_message_size(message_size); @@ -1438,6 +1457,12 @@ TEST_F(OEMCryptoLoadsCertificate, TEST_F(OEMCryptoLoadsCertificate, OEMCryptoMemoryLoadProvisioningForHugeCoreMessageEncPrivateKeyLength) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } TestLoadProvisioningForHugeBufferLengths( [](size_t length, ProvisioningRoundTrip* provisioning_messages) { provisioning_messages->core_response().enc_private_key.length = length; @@ -1447,6 +1472,12 @@ TEST_F(OEMCryptoLoadsCertificate, TEST_F(OEMCryptoLoadsCertificate, OEMCryptoMemoryLoadProvisioningForHugeCoreMessageEncPrivateKeyOffset) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } TestLoadProvisioningForHugeBufferLengths( [](size_t offset, ProvisioningRoundTrip* provisioning_messages) { provisioning_messages->core_response().enc_private_key.offset = offset; @@ -1457,6 +1488,12 @@ TEST_F(OEMCryptoLoadsCertificate, TEST_F( OEMCryptoLoadsCertificate, OEMCryptoMemoryLoadProvisioningForOutOfRangeCoreMessageEncPrivateKeyLength) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } TestLoadProvisioningForOutOfRangeSubstringOffsetAndLengths( [](size_t response_message_length, ProvisioningRoundTrip* provisioning_messages) { @@ -1470,6 +1507,12 @@ TEST_F( TEST_F( OEMCryptoLoadsCertificate, OEMCryptoMemoryLoadProvisioningForOutOfRangeCoreMessageEncPrivateKeyOffset) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } TestLoadProvisioningForOutOfRangeSubstringOffsetAndLengths( [](size_t response_message_length, ProvisioningRoundTrip* provisioning_messages) { @@ -1482,6 +1525,12 @@ TEST_F( TEST_F(OEMCryptoLoadsCertificate, OEMCryptoMemoryLoadProvisioningForHugeCoreMessageEncPrivateKeyIvLength) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } TestLoadProvisioningForHugeBufferLengths( [](size_t length, ProvisioningRoundTrip* provisioning_messages) { provisioning_messages->core_response().enc_private_key_iv.length = @@ -1492,6 +1541,12 @@ TEST_F(OEMCryptoLoadsCertificate, TEST_F(OEMCryptoLoadsCertificate, OEMCryptoMemoryLoadProvisioningForHugeCoreMessageEncPrivateKeyIvOffset) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } TestLoadProvisioningForHugeBufferLengths( [](size_t offset, ProvisioningRoundTrip* provisioning_messages) { provisioning_messages->core_response().enc_private_key_iv.offset = @@ -1503,6 +1558,12 @@ TEST_F(OEMCryptoLoadsCertificate, TEST_F( OEMCryptoLoadsCertificate, OEMCryptoMemoryLoadProvisioningForOutOfRangeCoreMessageEncPrivateKeyIvLengthAPI16) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } TestLoadProvisioningForOutOfRangeSubstringOffsetAndLengths( [](size_t response_message_length, ProvisioningRoundTrip* provisioning_messages) { @@ -1516,6 +1577,12 @@ TEST_F( TEST_F( OEMCryptoLoadsCertificate, OEMCryptoMemoryLoadProvisioningForOutOfRangeCoreMessageEncPrivateKeyIvOffset) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } TestLoadProvisioningForOutOfRangeSubstringOffsetAndLengths( [](size_t response_message_length, ProvisioningRoundTrip* provisioning_messages) { @@ -1528,6 +1595,12 @@ TEST_F( TEST_F(OEMCryptoLoadsCertificate, OEMCryptoMemoryLoadProvisioningForHugeCoreMessageEncMessageKeyLength) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } TestLoadProvisioningForHugeBufferLengths( [](size_t length, ProvisioningRoundTrip* provisioning_messages) { provisioning_messages->core_response().encrypted_message_key.length = @@ -1538,6 +1611,12 @@ TEST_F(OEMCryptoLoadsCertificate, TEST_F(OEMCryptoLoadsCertificate, OEMCryptoMemoryLoadProvisioningForHugeCoreMessageEncMessageKeyOffset) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } TestLoadProvisioningForHugeBufferLengths( [](size_t offset, ProvisioningRoundTrip* provisioning_messages) { provisioning_messages->core_response().encrypted_message_key.offset = @@ -1549,6 +1628,12 @@ TEST_F(OEMCryptoLoadsCertificate, TEST_F( OEMCryptoLoadsCertificate, OEMCryptoMemoryLoadProvisioningForOutOfRangeCoreMessageEncMessageKeyLengthProv30) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } if (global_features.provisioning_method != OEMCrypto_OEMCertificate) { GTEST_SKIP() << "Test for Prov 3.0 devices only."; } @@ -1565,6 +1650,12 @@ TEST_F( TEST_F( OEMCryptoLoadsCertificate, OEMCryptoMemoryLoadProvisioningForOutOfRangeCoreMessageEncMessageKeyOffsetProv30) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } if (global_features.provisioning_method != OEMCrypto_OEMCertificate) { GTEST_SKIP() << "Test for Prov 3.0 devices only."; } @@ -1585,6 +1676,12 @@ TEST_F( TEST_F(OEMCryptoLoadsCertificate, OEMCryptoMemoryPrepareProvisioningRequestForHugeRequestMessageLength) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } TestPrepareProvisioningRequestForHugeBufferLengths( [](size_t message_size, ProvisioningRoundTrip* provisioning_messages) { provisioning_messages->set_message_size(message_size); @@ -1594,6 +1691,12 @@ TEST_F(OEMCryptoLoadsCertificate, TEST_F(OEMCryptoLoadsCertificate, OEMCryptoMemoryPrepareProvisioningRequestForHugeSignatureLength) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } TestPrepareProvisioningRequestForHugeBufferLengths( [](size_t message_size, ProvisioningRoundTrip* provisioning_messages) { provisioning_messages->set_request_signature_size(message_size); @@ -1603,6 +1706,12 @@ TEST_F(OEMCryptoLoadsCertificate, TEST_F(OEMCryptoLoadsCertificate, OEMCryptoMemoryPrepareProvisioningRequestForHugeCoreMessageLength) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } TestPrepareProvisioningRequestForHugeBufferLengths( [](size_t message_size, ProvisioningRoundTrip* provisioning_messages) { provisioning_messages->set_core_message_size(message_size); @@ -1616,4 +1725,90 @@ TEST_F(OEMCryptoLoadsCertificate, /// @{ /// @} + +#ifdef CAS_TEST + +# include "tuner_hal.h" + +class OEMCryptoCasDemoTest : public OEMCryptoEntitlementLicenseTest {}; + +TEST_P(OEMCryptoCasDemoTest, BasicFlow) { + // License contains entitlement keys, function reused from + // OEMCryptoEntitlementLicenseTest + LoadEntitlementLicense(); + uint32_t key_session_id = 0; + ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_CreateEntitledKeySession( + session_.session_id(), &key_session_id)); + + EntitledMessage entitled_message(&license_messages_); + + // Randomly generate entitled content keys + entitled_message.FillKeyArray(); + if (session_.session_id() == key_session_id) { + GTEST_SKIP() + << "Skipping test because entitled and entitlement sessions are both " + << key_session_id << "."; + } + entitled_message.SetEntitledKeySession(key_session_id); + + // Encrypt and load 0th key (even key) into OEMCrypto + ASSERT_NO_FATAL_FAILURE(entitled_message.LoadCasKeys( + /*load_even=*/true, /*load_odd=*/false, OEMCrypto_SUCCESS)); + + // + // Perform DecryptCTR() but for CAS + // + vector unencrypted_data(256, 0); + vector encrypted_data(256, 0); + vector output_buffer(256, 0); + unencrypted_data.resize(encrypted_data.size()); + output_buffer.resize(encrypted_data.size()); + + OEMCrypto_SampleDescription sample_description; + OEMCrypto_SubSampleDescription subsample_description; + GenerateSimpleSampleDescription(encrypted_data, output_buffer, + &sample_description, &subsample_description); + + // Use 0th entitled content key and IV to encrypt test data + EncryptCTR(unencrypted_data, + entitled_message.entitled_key_data()->content_key_data, + entitled_message.entitled_key_data()->content_iv, &encrypted_data); + + // Assume 0,0 pattern for CTR example + OEMCrypto_CENCEncryptPatternDesc pattern = {0, 0}; + + // Demo only -- copy IV into sample description so we can use + // WTPI_DecryptSample() in the Tuner decrypt impl. A real implementation would + // use the IV from the entitled content key, but the demo relies on the + // existing decrypt which uses SampleDescription IV. + memcpy(sample_description.iv, + entitled_message.entitled_key_data()->content_iv, 16); + + // Get key token to send to Tuner for decrypt + std::vector key_token; + size_t key_token_length = key_token.size(); + OEMCryptoResult res = OEMCrypto_GetOEMKeyToken( + key_session_id, key_token.data(), &key_token_length); + if (res == OEMCrypto_ERROR_SHORT_BUFFER) { + key_token.resize(key_token_length); + res = OEMCrypto_GetOEMKeyToken(key_session_id, key_token.data(), + &key_token_length); + } + ASSERT_EQ(OEMCrypto_SUCCESS, res); + + // Decrypt the data + ASSERT_EQ(TUNER_HAL_SUCCESS, + TunerHal_Decrypt(key_token.data(), key_token_length, + TunerHal_KeyParityType_EvenKey, + &sample_description, // an array of samples. + 1, // the number of samples. + &pattern)); + + ASSERT_EQ(unencrypted_data, output_buffer); +} + +INSTANTIATE_TEST_SUITE_P(TestAll, OEMCryptoCasDemoTest, + Range(kCoreMessagesAPI, kCurrentAPI + 1)); + +#endif } // namespace wvoec diff --git a/oemcrypto/test/oemcrypto_test_android.cpp b/oemcrypto/test/oemcrypto_test_android.cpp index 074eb4b..4337f75 100644 --- a/oemcrypto/test/oemcrypto_test_android.cpp +++ b/oemcrypto/test/oemcrypto_test_android.cpp @@ -25,11 +25,11 @@ namespace wvoec { class OEMCryptoAndroidLMPTest : public ::testing::Test { protected: void SetUp() override { + OEMCrypto_SetSandbox(kTestSandbox, sizeof(kTestSandbox)); + ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Initialize()); if (OEMCrypto_GetProvisioningMethod() == OEMCrypto_BootCertificateChain) { GTEST_SKIP() << "Test for non Prov 4.0 devices only."; } - OEMCrypto_SetSandbox(kTestSandbox, sizeof(kTestSandbox)); - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Initialize()); OEMCrypto_SetMaxAPIVersion(kCurrentAPI); OEMCrypto_EnterTestMode(); } @@ -39,6 +39,10 @@ class OEMCryptoAndroidLMPTest : public ::testing::Test { // Android devices must have a keybox, or use provisioning 3.0. TEST_F(OEMCryptoAndroidLMPTest, GetKeyDataImplemented) { + if (global_features.provisioning_method != OEMCrypto_Keybox && + global_features.provisioning_method != OEMCrypto_OEMCertificate) { + GTEST_SKIP() << "Test for Prov 2.0 and 3.0 devices only."; + } uint8_t key_data[256]; size_t key_data_len = sizeof(key_data); if (OEMCrypto_Keybox == OEMCrypto_GetProvisioningMethod()) { @@ -62,6 +66,9 @@ TEST_F(OEMCryptoAndroidLMPTest, MinVersionNumber9) { } TEST_F(OEMCryptoAndroidLMPTest, ValidKeyboxTest) { + if (global_features.provisioning_method != OEMCrypto_Keybox) { + GTEST_SKIP() << "Test for Prov 2.0 devices only."; + } ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_IsKeyboxValid()); } @@ -115,6 +122,9 @@ TEST_F(OEMCryptoAndroidMNCTest, MinVersionNumber10) { // Android devices using Provisioning 2.0 must be able to load a test keybox. // If they are not using Provisioning 2.0, then they must use Provisioning 3.0. TEST_F(OEMCryptoAndroidMNCTest, LoadsTestKeyboxImplemented) { + if (global_features.provisioning_method != OEMCrypto_Keybox) { + GTEST_SKIP() << "Test for Prov 2.0 devices only."; + } if (OEMCrypto_Keybox == OEMCrypto_GetProvisioningMethod()) { ASSERT_EQ( OEMCrypto_SUCCESS, diff --git a/oemcrypto/test/oemcrypto_usage_table_test.cpp b/oemcrypto/test/oemcrypto_usage_table_test.cpp index 77c6240..ff15b24 100644 --- a/oemcrypto/test/oemcrypto_usage_table_test.cpp +++ b/oemcrypto/test/oemcrypto_usage_table_test.cpp @@ -440,6 +440,9 @@ TEST_P(OEMCryptoUsageTableTest, LoadEntryInMultipleSessions) { // Test generic encrypt when the license uses a PST. TEST_P(OEMCryptoUsageTableTest, GenericCryptoEncrypt) { + if (!wvoec::global_features.generic_crypto) { + GTEST_SKIP() << "Test for devices with generic crypto API only"; + } LicenseWithUsageEntry entry; entry.license_messages().set_api_version(license_api_version_); entry.set_generic_crypto(true); @@ -479,6 +482,9 @@ TEST_P(OEMCryptoUsageTableTest, GenericCryptoEncrypt) { // Test generic decrypt when the license uses a PST. TEST_P(OEMCryptoUsageTableTest, GenericCryptoDecrypt) { + if (!wvoec::global_features.generic_crypto) { + GTEST_SKIP() << "Test for devices with generic crypto API only"; + } LicenseWithUsageEntry entry; entry.license_messages().set_api_version(license_api_version_); entry.set_generic_crypto(true); @@ -516,6 +522,9 @@ TEST_P(OEMCryptoUsageTableTest, GenericCryptoDecrypt) { // Test generic sign when the license uses a PST. TEST_P(OEMCryptoUsageTableTest, GenericCryptoSign) { + if (!wvoec::global_features.generic_crypto) { + GTEST_SKIP() << "Test for devices with generic crypto API only"; + } LicenseWithUsageEntry entry; entry.license_messages().set_api_version(license_api_version_); entry.set_generic_crypto(true); @@ -565,6 +574,9 @@ TEST_P(OEMCryptoUsageTableTest, GenericCryptoSign) { // Test generic verify when the license uses a PST. TEST_P(OEMCryptoUsageTableTest, GenericCryptoVerify) { + if (!wvoec::global_features.generic_crypto) { + GTEST_SKIP() << "Test for devices with generic crypto API only"; + } LicenseWithUsageEntry entry; entry.license_messages().set_api_version(license_api_version_); entry.set_generic_crypto(true); diff --git a/oemcrypto/test/oemcrypto_usage_table_test.h b/oemcrypto/test/oemcrypto_usage_table_test.h index ce5d7ab..b36404a 100644 --- a/oemcrypto/test/oemcrypto_usage_table_test.h +++ b/oemcrypto/test/oemcrypto_usage_table_test.h @@ -28,6 +28,9 @@ class OEMCryptoGenericCryptoTest : public OEMCryptoRefreshTest { void SetUp() override { OEMCryptoRefreshTest::SetUp(); + if (!wvoec::global_features.generic_crypto) { + GTEST_SKIP() << "Test for devices with generic crypto API only"; + } ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE( license_messages_.CreateResponseWithGenericCryptoKeys());