From 4080a1a1de5f2a37b346447b57faca4fbb916c76 Mon Sep 17 00:00:00 2001 From: Alex Dale Date: Thu, 27 Jun 2024 16:18:42 -0700 Subject: [PATCH] OPK v17.4 --- CHANGELOG.md | 19 +++ oemcrypto/include/OEMCryptoCENC.h | 48 ++++++- oemcrypto/odk/include/core_message_features.h | 4 +- oemcrypto/odk/include/odk_structs.h | 4 +- oemcrypto/odk/src/core_message_features.cpp | 2 +- oemcrypto/odk/src/odk_timer.c | 2 +- oemcrypto/odk/test/odk_test.cpp | 1 + oemcrypto/opk/oemcrypto_ta/oemcrypto.c | 66 +++++++++- .../opk/oemcrypto_ta/oemcrypto_api_macros.h | 2 +- .../ta/interface_impls/wtpi_clock_layer2.c | 6 +- .../common/GEN_common_serializer.c | 15 +++ .../common/GEN_common_serializer.h | 2 + .../opk/serialization/ree/GEN_oemcrypto_api.c | 35 ++++++ .../serialization/ree/GEN_ree_serializer.c | 45 +++++++ .../serialization/ree/GEN_ree_serializer.h | 7 ++ .../opk/serialization/tee/GEN_dispatcher.c | 26 ++++ .../serialization/tee/GEN_tee_serializer.c | 36 ++++++ .../serialization/tee/GEN_tee_serializer.h | 7 ++ oemcrypto/test/GEN_api_lock_file.c | 7 ++ .../test/fuzz_tests/oemcrypto_fuzz_helper.h | 2 + .../oemcrypto_renewal_request_fuzz.cc | 1 + oemcrypto/test/oemcrypto_test.cpp | 117 +----------------- util/src/string_conversions.cpp | 16 +-- 23 files changed, 339 insertions(+), 131 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 04db2b8..68086de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,24 @@ [TOC] +## [Version 17.4][v17.4] + +This is a minor release that includes a few security fixes. + +General + +- New API function OEMCrypto_WrapClearPrivateKey() for ATSC factory builds. +- Clarified HDCP level enforcement for downstream devices (see OEMCrypto + documentation). +- Small test improvement + - Skip CAS tests for non CAS devices + - Removed tests for deprecated features + +OPK TA + +- Improved backwards compatibility for DRM key wrapping. +- Added secure clearing of buffers contain sensitive data. + ## [Version 17.3][v17.3] This is a minor release that includes a few security fixes. @@ -270,3 +288,4 @@ Public release for OEMCrypto API and ODK library version 16.4. [v17.2]: https://widevine-partner.googlesource.com/oemcrypto/+/refs/tags/v17.2 [v17.2.1]: https://widevine-partner.googlesource.com/oemcrypto/+/refs/tags/v17.2.1 [v17.3]: https://widevine-partner.googlesource.com/oemcrypto/+/refs/tags/v17.3 +[v17.4]: https://widevine-partner.googlesource.com/oemcrypto/+/refs/tags/v17.4 diff --git a/oemcrypto/include/OEMCryptoCENC.h b/oemcrypto/include/OEMCryptoCENC.h index 656debe..5b395fc 100644 --- a/oemcrypto/include/OEMCryptoCENC.h +++ b/oemcrypto/include/OEMCryptoCENC.h @@ -3,7 +3,7 @@ // License Agreement. /** - * @mainpage OEMCrypto API v17.3 + * @mainpage OEMCrypto API v17.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 @@ -722,6 +722,7 @@ typedef enum OEMCrypto_WatermarkingSupport { #define OEMCrypto_GetDTCP2Capability _oecc128 #define OEMCrypto_GetWatermarkingSupport _oecc129 #define OEMCrypto_GetOEMKeyToken _oecc130 +#define OEMCrypto_WrapClearPrivateKey _oecc154 // clang-format on /// @addtogroup initcontrol @@ -3290,6 +3291,51 @@ OEMCryptoResult OEMCrypto_IsKeyboxOrOEMCertValid(void); OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* device_id, size_t* device_id_length); +/** + * Encrypts a clear device RSA/ECC key with an internal key (such as the OEM + * key or Widevine Keybox key) and a generated IV using AES-128-CBC with PKCS#5 + * padding. + * + * Copies the wrapped key to the buffer specified by |wrapped_private_key| and + * sets the size of the wrapped key to |wrapped_private_key_length|. + * + * The clear private key is encoded in PKCS#8 binary DER format. The OEMCrypto + * library shall verify that this RSA key is valid. + * + * The clear key should be encrypted using the same device specific key used in + * OEMCrypto_LoadProvisioning. The wrapped private key will be unwrapped in the + * function OEMCrypto_LoadDRMPrivateKey. + * + * This function should only be implemented for factory builds. + * + * @param[in] clear_private_key_bytes: pointer to memory containing the + * unencrypted private key data. + * @param[in] clear_private_key_length: the length of the private key data. + * @param[out] wrapped_private_key: pointer to buffer in which the encrypted + * private key should be stored. May be null on the first call in order to + * find required buffer size. + * @param[in,out] wrapped_private_key_length: (in) length of the encrypted + * private key, in bytes. (out) actual length of the encrypted private key, + * or required length if provided length is too small. + * + * @retval OEMCrypto_SUCCESS on success + * @retval OEMCrypto_ERROR_INVALID_CONTEXT clear_private_key_bytes is NULL, or + * clear private key fails to parse as PKCS#8 + * @retval OEMCrypto_ERROR_SHORT_BUFFER wrapped_private_key_length is too small, + * or wrapped_private_key is NULL + * + * @threading + * This is an "Initialization and Termination Function" and will not be + * called simultaneously with any other function, as if the CDM holds a write + * lock on the OEMCrypto system. + * + * @version + * This method is new in API version 17.2. + */ +OEMCryptoResult OEMCrypto_WrapClearPrivateKey( + const uint8_t* clear_private_key_bytes, size_t clear_private_key_length, + uint8_t* wrapped_private_key, size_t* wrapped_private_key_length); + /// @} /// @addtogroup keybox diff --git a/oemcrypto/odk/include/core_message_features.h b/oemcrypto/odk/include/core_message_features.h index 951918b..7c3406b 100644 --- a/oemcrypto/odk/include/core_message_features.h +++ b/oemcrypto/odk/include/core_message_features.h @@ -25,9 +25,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 17.3. + // number. The default is 17.4. uint32_t maximum_major_version = 17; - 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 4ba7be6..ff8958e 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 17 -#define ODK_MINOR_VERSION 3 +#define ODK_MINOR_VERSION 4 /* ODK Version string. Date changed automatically on each release. */ -#define ODK_RELEASE_DATE "ODK v17.3 2024-03-21" +#define ODK_RELEASE_DATE "ODK v17.4 2024-06-04" /* 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 94f6b37..fa5b75b 100644 --- a/oemcrypto/odk/src/core_message_features.cpp +++ b/oemcrypto/odk/src/core_message_features.cpp @@ -23,7 +23,7 @@ CoreMessageFeatures CoreMessageFeatures::DefaultFeatures( features.maximum_minor_version = 5; // 16.5 break; case 17: - features.maximum_minor_version = 3; // 17.3 + features.maximum_minor_version = 4; // 17.4 break; default: features.maximum_minor_version = 0; diff --git a/oemcrypto/odk/src/odk_timer.c b/oemcrypto/odk/src/odk_timer.c index d05d14b..a1b37fd 100644 --- a/oemcrypto/odk/src/odk_timer.c +++ b/oemcrypto/odk/src/odk_timer.c @@ -274,7 +274,7 @@ OEMCryptoResult ODK_InitializeSessionValues(ODK_TimerLimits* timer_limits, nonce_values->api_minor_version = 5; break; case 17: - nonce_values->api_minor_version = 3; + nonce_values->api_minor_version = 4; break; case 18: nonce_values->api_minor_version = 3; diff --git a/oemcrypto/odk/test/odk_test.cpp b/oemcrypto/odk/test/odk_test.cpp index 854b7aa..0116a9b 100644 --- a/oemcrypto/odk/test/odk_test.cpp +++ b/oemcrypto/odk/test/odk_test.cpp @@ -874,6 +874,7 @@ std::vector TestCases() { {17, 17, 1, 17, 1}, {17, 17, 2, 17, 2}, {17, 17, 3, 17, 3}, + {17, 17, 4, 17, 4}, }; return test_cases; } diff --git a/oemcrypto/opk/oemcrypto_ta/oemcrypto.c b/oemcrypto/opk/oemcrypto_ta/oemcrypto.c index c946d90..14e4910 100644 --- a/oemcrypto/opk/oemcrypto_ta/oemcrypto.c +++ b/oemcrypto/opk/oemcrypto_ta/oemcrypto.c @@ -592,8 +592,9 @@ OEMCryptoResult OEMCrypto_CloseSession(OEMCrypto_SESSION session) { result = OPKI_CheckStatePreCall(session_context, API_CLOSESESSION); if (result != OEMCrypto_SUCCESS) return result; result = OPKI_FreeEntitledKeySessions(session); - if (result != OEMCrypto_SUCCESS) return result; - return OPKI_FreeSession(session); + OEMCryptoResult free_session_result = OPKI_FreeSession(session); + if (result == OEMCrypto_SUCCESS) result = free_session_result; + return result; } OEMCryptoResult OEMCrypto_GenerateDerivedKeys(OEMCrypto_SESSION session, @@ -3869,3 +3870,64 @@ OEMCryptoResult OEMCrypto_GetOEMKeyToken(OEMCrypto_SESSION key_session UNUSED, return OEMCrypto_ERROR_NOT_IMPLEMENTED; } #endif + +OEMCryptoResult OEMCrypto_WrapClearPrivateKey( + const uint8_t* clear_private_key_bytes, size_t clear_private_key_length, + uint8_t* wrapped_private_key, size_t* wrapped_private_key_length) { +#ifdef FACTORY_BUILD_ONLY + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + OEMCryptoResult res = OEMCrypto_ERROR_UNKNOWN_FAILURE; + + RETURN_INVALID_CONTEXT_IF_NULL(clear_private_key_bytes); + RETURN_INVALID_CONTEXT_IF_NULL(wrapped_private_key_length); + + size_t required_size = 0; + AsymmetricKeyType key_type = DRM_RSA_PRIVATE_KEY; + + // verify clear private key format by trying to convert it into a key handle + WTPI_AsymmetricKey_Handle out_handle; + res = WTPI_CreateAsymmetricKeyHandle( + clear_private_key_bytes, clear_private_key_length, key_type, &out_handle); + WTPI_FreeAsymmetricKeyHandle(out_handle); + if (res != OEMCrypto_SUCCESS) { + // try again with ECC type + key_type = DRM_ECC_PRIVATE_KEY; + res = WTPI_CreateAsymmetricKeyHandle(clear_private_key_bytes, + clear_private_key_length, key_type, + &out_handle); + WTPI_FreeAsymmetricKeyHandle(out_handle); + if (res != OEMCrypto_SUCCESS) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + } + + res = WTPI_GetWrappedAsymmetricKeySize(clear_private_key_length, key_type, + &required_size); + if (res != OEMCrypto_SUCCESS) { + return res; + } + + if (wrapped_private_key == NULL || + *wrapped_private_key_length < required_size) { + *wrapped_private_key_length = required_size; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + + res = WTPI_WrapAsymmetricKey(wrapped_private_key, *wrapped_private_key_length, + key_type, clear_private_key_bytes, + clear_private_key_length); + + return res; +#else + (void)clear_private_key_bytes; + (void)clear_private_key_length; + (void)wrapped_private_key; + (void)wrapped_private_key_length; + 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 d9a9bfb..17b99b2 100644 --- a/oemcrypto/opk/oemcrypto_ta/oemcrypto_api_macros.h +++ b/oemcrypto/opk/oemcrypto_ta/oemcrypto_api_macros.h @@ -31,7 +31,7 @@ // version bumps to v17.1, the first released OPK implementation would be // v17.1.0 #define API_MAJOR_VERSION 17 -#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/ports/trusty/ta/interface_impls/wtpi_clock_layer2.c b/oemcrypto/opk/ports/trusty/ta/interface_impls/wtpi_clock_layer2.c index a9a7ac4..7ea83fe 100644 --- a/oemcrypto/opk/ports/trusty/ta/interface_impls/wtpi_clock_layer2.c +++ b/oemcrypto/opk/ports/trusty/ta/interface_impls/wtpi_clock_layer2.c @@ -19,12 +19,16 @@ #include #include +#include #include OEMCryptoResult WTPI_GetSecureTimer(uint64_t* time_in_s) { int64_t now; - trusty_gettime(0, &now); + int rc = trusty_gettime(0, &now); + if (rc != NO_ERROR) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } *time_in_s = now / 1000000000ll; return OEMCrypto_SUCCESS; } diff --git a/oemcrypto/opk/serialization/common/GEN_common_serializer.c b/oemcrypto/opk/serialization/common/GEN_common_serializer.c index 023f3eb..4220c88 100644 --- a/oemcrypto/opk/serialization/common/GEN_common_serializer.c +++ b/oemcrypto/opk/serialization/common/GEN_common_serializer.c @@ -735,6 +735,21 @@ void OPK_UnpackNullable_OEMCrypto_DestBufferDesc( OPK_Unpack_OEMCrypto_DestBufferDesc(msg, NULL); } } +void OPK_PackNullable_uint8_t(ODK_Message* msg, const uint8_t* value) { + OPK_PackBoolValue(msg, value == NULL); + if (value) { + OPK_Pack_uint8_t(msg, value); + } +} +void OPK_UnpackNullable_uint8_t(ODK_Message* msg, uint8_t** value) { + if (OPK_UnpackIsNull(msg)) { + if (value) *value = NULL; + } else if (value) { + OPK_Unpack_uint8_t(msg, *value); + } else { + OPK_Unpack_uint8_t(msg, NULL); + } +} void OPK_PackNullable_uint16_t(ODK_Message* msg, const uint16_t* value) { OPK_PackBoolValue(msg, value == NULL); if (value) { diff --git a/oemcrypto/opk/serialization/common/GEN_common_serializer.h b/oemcrypto/opk/serialization/common/GEN_common_serializer.h index a29a1d1..fe33a2a 100644 --- a/oemcrypto/opk/serialization/common/GEN_common_serializer.h +++ b/oemcrypto/opk/serialization/common/GEN_common_serializer.h @@ -109,6 +109,8 @@ void OPK_PackNullable_OEMCrypto_DestBufferDesc( ODK_Message* msg, const OEMCrypto_DestBufferDesc* value); void OPK_UnpackNullable_OEMCrypto_DestBufferDesc( ODK_Message* msg, OEMCrypto_DestBufferDesc** value); +void OPK_PackNullable_uint8_t(ODK_Message* msg, const uint8_t* value); +void OPK_UnpackNullable_uint8_t(ODK_Message* msg, uint8_t** value); void OPK_PackNullable_uint16_t(ODK_Message* msg, const uint16_t* value); void OPK_UnpackNullable_uint16_t(ODK_Message* msg, uint16_t** value); void OPK_UnpackAlloc_uint16_t(ODK_Message* msg, uint16_t** value); diff --git a/oemcrypto/opk/serialization/ree/GEN_oemcrypto_api.c b/oemcrypto/opk/serialization/ree/GEN_oemcrypto_api.c index fd26aca..469b7c4 100644 --- a/oemcrypto/opk/serialization/ree/GEN_oemcrypto_api.c +++ b/oemcrypto/opk/serialization/ree/GEN_oemcrypto_api.c @@ -1167,6 +1167,41 @@ cleanup_and_return: return result; } +OEMCRYPTO_API OEMCryptoResult OEMCrypto_WrapClearPrivateKey( + const uint8_t* clear_private_key_bytes, size_t clear_private_key_length, + uint8_t* wrapped_private_key, size_t* wrapped_private_key_length) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_WrapClearPrivateKey_Request( + clear_private_key_bytes, clear_private_key_length, wrapped_private_key, + wrapped_private_key_length); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + if (ODK_Message_GetStatus(&request) == MESSAGE_STATUS_BUFFER_TOO_LARGE) { + api_result = OEMCrypto_ERROR_BUFFER_TOO_LARGE; + } else { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_WrapClearPrivateKey_Response( + &response, &result, &wrapped_private_key, &wrapped_private_key_length); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_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; +} + OEMCRYPTO_API OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* key_data, size_t* key_data_length) { pthread_mutex_lock(&api_lock); diff --git a/oemcrypto/opk/serialization/ree/GEN_ree_serializer.c b/oemcrypto/opk/serialization/ree/GEN_ree_serializer.c index db59df9..7ff7bae 100644 --- a/oemcrypto/opk/serialization/ree/GEN_ree_serializer.c +++ b/oemcrypto/opk/serialization/ree/GEN_ree_serializer.c @@ -1364,6 +1364,51 @@ void OPK_Unpack_GetDeviceID_Response(ODK_Message* msg, OEMCryptoResult* result, } } +ODK_Message OPK_Pack_WrapClearPrivateKey_Request( + const uint8_t* clear_private_key_bytes, size_t clear_private_key_length, + const uint8_t* wrapped_private_key, + const size_t* wrapped_private_key_length) { + uint32_t api_value = 154; /* from _oecc154 */ + 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_PackNullable_size_t(&msg, wrapped_private_key_length); + OPK_PackNullable_uint8_t(&msg, clear_private_key_bytes); + OPK_Pack_size_t(&msg, &clear_private_key_length); + OPK_PackAlloc(&msg, wrapped_private_key); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_WrapClearPrivateKey_Response( + ODK_Message* msg, OEMCryptoResult* result, uint8_t** wrapped_private_key, + size_t** wrapped_private_key_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 154) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_UnpackNullable_size_t(msg, wrapped_private_key_length); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + if (SuccessResult(*result)) { + uint8_t* p; + OPK_UnpackInPlace(msg, &p, OPK_FromSizeTPtrPtr(wrapped_private_key_length)); + if (p && *wrapped_private_key) { + memcpy(*wrapped_private_key, p, + OPK_SafeDerefSizeTPtrPtr(wrapped_private_key_length)); + } + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + ODK_Message OPK_Pack_GetKeyData_Request(const uint8_t* key_data, const size_t* key_data_length) { uint32_t api_value = 4; /* from _oecc4 */ diff --git a/oemcrypto/opk/serialization/ree/GEN_ree_serializer.h b/oemcrypto/opk/serialization/ree/GEN_ree_serializer.h index b56e27a..0c119c6 100644 --- a/oemcrypto/opk/serialization/ree/GEN_ree_serializer.h +++ b/oemcrypto/opk/serialization/ree/GEN_ree_serializer.h @@ -198,6 +198,13 @@ ODK_Message OPK_Pack_GetDeviceID_Request(const uint8_t* device_id, void OPK_Unpack_GetDeviceID_Response(ODK_Message* msg, OEMCryptoResult* result, uint8_t** device_id, size_t** device_id_length); +ODK_Message OPK_Pack_WrapClearPrivateKey_Request( + const uint8_t* clear_private_key_bytes, size_t clear_private_key_length, + const uint8_t* wrapped_private_key, + const size_t* wrapped_private_key_length); +void OPK_Unpack_WrapClearPrivateKey_Response( + ODK_Message* msg, OEMCryptoResult* result, uint8_t** wrapped_private_key, + size_t** wrapped_private_key_length); ODK_Message OPK_Pack_GetKeyData_Request(const uint8_t* key_data, const size_t* key_data_length); void OPK_Unpack_GetKeyData_Response(ODK_Message* msg, OEMCryptoResult* result, diff --git a/oemcrypto/opk/serialization/tee/GEN_dispatcher.c b/oemcrypto/opk/serialization/tee/GEN_dispatcher.c index 7b67a28..617b623 100644 --- a/oemcrypto/opk/serialization/tee/GEN_dispatcher.c +++ b/oemcrypto/opk/serialization/tee/GEN_dispatcher.c @@ -894,6 +894,32 @@ ODK_MessageStatus OPK_DispatchMessage(ODK_Message* request, OPK_Pack_GetDeviceID_Response(result, device_id, device_id_length); break; } + case 154: /* OEMCrypto_WrapClearPrivateKey */ + { + size_t* wrapped_private_key_length = + (size_t*)OPK_VarAlloc(sizeof(size_t)); + OPK_Init_size_t(wrapped_private_key_length); + uint8_t* clear_private_key_bytes = + (uint8_t*)OPK_VarAlloc(sizeof(uint8_t)); + OPK_Init_uint8_t((uint8_t*)clear_private_key_bytes); + size_t clear_private_key_length; + OPK_Init_size_t((size_t*)&clear_private_key_length); + uint8_t* wrapped_private_key; + OPK_InitPointer((uint8_t**)&wrapped_private_key); + OPK_Unpack_WrapClearPrivateKey_Request( + request, &clear_private_key_bytes, &clear_private_key_length, + &wrapped_private_key, &wrapped_private_key_length); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("WrapClearPrivateKey"); + result = OEMCrypto_WrapClearPrivateKey( + clear_private_key_bytes, clear_private_key_length, + wrapped_private_key, wrapped_private_key_length); + *response = OPK_Pack_WrapClearPrivateKey_Response( + result, wrapped_private_key, wrapped_private_key_length); + break; + } case 4: /* OEMCrypto_GetKeyData */ { size_t* key_data_length = (size_t*)OPK_VarAlloc(sizeof(size_t)); diff --git a/oemcrypto/opk/serialization/tee/GEN_tee_serializer.c b/oemcrypto/opk/serialization/tee/GEN_tee_serializer.c index 967f8ff..49d6fd6 100644 --- a/oemcrypto/opk/serialization/tee/GEN_tee_serializer.c +++ b/oemcrypto/opk/serialization/tee/GEN_tee_serializer.c @@ -1182,6 +1182,42 @@ ODK_Message OPK_Pack_GetDeviceID_Response(OEMCryptoResult result, return msg; } +void OPK_Unpack_WrapClearPrivateKey_Request( + ODK_Message* msg, uint8_t** clear_private_key_bytes, + size_t* clear_private_key_length, uint8_t** wrapped_private_key, + size_t** wrapped_private_key_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 154) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_UnpackNullable_size_t(msg, wrapped_private_key_length); + OPK_UnpackNullable_uint8_t(msg, clear_private_key_bytes); + OPK_Unpack_size_t(msg, clear_private_key_length); + *wrapped_private_key = (uint8_t*)OPK_UnpackAllocBuffer( + msg, OPK_FromSizeTPtrPtr(wrapped_private_key_length), sizeof(uint8_t)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_WrapClearPrivateKey_Response( + OEMCryptoResult result, const uint8_t* wrapped_private_key, + const size_t* wrapped_private_key_length) { + uint32_t api_value = 154; /* from _oecc154 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_PackNullable_size_t(&msg, wrapped_private_key_length); + OPK_Pack_uint32_t(&msg, &result); + if (SuccessResult(result)) { + OPK_PackMemory(&msg, (const uint8_t*)wrapped_private_key, + OPK_FromSizeTPtr(wrapped_private_key_length)); + } + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + void OPK_Unpack_GetKeyData_Request(ODK_Message* msg, uint8_t** key_data, size_t** key_data_length) { uint32_t api_value = UINT32_MAX; diff --git a/oemcrypto/opk/serialization/tee/GEN_tee_serializer.h b/oemcrypto/opk/serialization/tee/GEN_tee_serializer.h index 5737a2a..20ec103 100644 --- a/oemcrypto/opk/serialization/tee/GEN_tee_serializer.h +++ b/oemcrypto/opk/serialization/tee/GEN_tee_serializer.h @@ -204,6 +204,13 @@ void OPK_Unpack_GetDeviceID_Request(ODK_Message* msg, uint8_t** device_id, ODK_Message OPK_Pack_GetDeviceID_Response(OEMCryptoResult result, const uint8_t* device_id, const size_t* device_id_length); +void OPK_Unpack_WrapClearPrivateKey_Request( + ODK_Message* msg, uint8_t** clear_private_key_bytes, + size_t* clear_private_key_length, uint8_t** wrapped_private_key, + size_t** wrapped_private_key_length); +ODK_Message OPK_Pack_WrapClearPrivateKey_Response( + OEMCryptoResult result, const uint8_t* wrapped_private_key, + const size_t* wrapped_private_key_length); void OPK_Unpack_GetKeyData_Request(ODK_Message* msg, uint8_t** key_data, size_t** key_data_length); ODK_Message OPK_Pack_GetKeyData_Response(OEMCryptoResult result, diff --git a/oemcrypto/test/GEN_api_lock_file.c b/oemcrypto/test/GEN_api_lock_file.c index 37e577e..bece894 100644 --- a/oemcrypto/test/GEN_api_lock_file.c +++ b/oemcrypto/test/GEN_api_lock_file.c @@ -304,3 +304,10 @@ OEMCryptoResult _oecc120(OEMCrypto_SESSION session, const uint8_t* message, // OEMCrypto_GetOEMKeyToken defined in v17.2 OEMCryptoResult _oecc130(OEMCrypto_SESSION key_session, uint8_t* key_token, size_t* key_token_length); + +// OEMCrypto_WrapClearPrivateKey defined in v17.2 +OEMCryptoResult _oecc154(const uint8_t* clear_private_key_bytes, + size_t clear_private_key_length, + uint8_t* wrapped_private_key, + size_t* wrapped_private_key_length); + diff --git a/oemcrypto/test/fuzz_tests/oemcrypto_fuzz_helper.h b/oemcrypto/test/fuzz_tests/oemcrypto_fuzz_helper.h index a60a9cf..057fcf3 100644 --- a/oemcrypto/test/fuzz_tests/oemcrypto_fuzz_helper.h +++ b/oemcrypto/test/fuzz_tests/oemcrypto_fuzz_helper.h @@ -122,6 +122,8 @@ class OEMCryptoRenewalAPIFuzz { void Initialize() { license_api_fuzz_.Initialize(); } + void LoadLicense() { license_api_fuzz_.LoadLicense(); } + void Terminate() { license_api_fuzz_.Terminate(); } LicenseRoundTrip& license_messages() { diff --git a/oemcrypto/test/fuzz_tests/oemcrypto_renewal_request_fuzz.cc b/oemcrypto/test/fuzz_tests/oemcrypto_renewal_request_fuzz.cc index 658bdc1..03cf860 100644 --- a/oemcrypto/test/fuzz_tests/oemcrypto_renewal_request_fuzz.cc +++ b/oemcrypto/test/fuzz_tests/oemcrypto_renewal_request_fuzz.cc @@ -20,6 +20,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { std::vector input(data, data + size); wvoec::OEMCryptoRenewalAPIFuzz renewal_api_fuzz; renewal_api_fuzz.Initialize(); + renewal_api_fuzz.LoadLicense(); renewal_api_fuzz.renewal_messages().InjectFuzzedRequestData(input.data(), input.size()); renewal_api_fuzz.Terminate(); diff --git a/oemcrypto/test/oemcrypto_test.cpp b/oemcrypto/test/oemcrypto_test.cpp index fc75680..8b9cb0b 100644 --- a/oemcrypto/test/oemcrypto_test.cpp +++ b/oemcrypto/test/oemcrypto_test.cpp @@ -292,7 +292,7 @@ TEST_F(OEMCryptoClientTest, FreeUnallocatedSecureBufferNoFailure) { */ TEST_F(OEMCryptoClientTest, VersionNumber) { const std::string log_message = - "OEMCrypto unit tests for API 17.3. Tests last updated 2024-03-21"; + "OEMCrypto unit tests for API 17.4. Tests last updated 2024-06-04"; cout << " " << log_message << "\n"; cout << " " << "These tests are part of Android T." @@ -301,7 +301,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, 17); - EXPECT_EQ(ODK_MINOR_VERSION, 3); + EXPECT_EQ(ODK_MINOR_VERSION, 4); EXPECT_EQ(kCurrentAPI, 17u); OEMCrypto_Security_Level level = OEMCrypto_SecurityLevel(); EXPECT_GT(level, OEMCrypto_Level_Unknown); @@ -3217,6 +3217,9 @@ TEST_P(OEMCryptoLicenseTest, SelectKeyEntitlementKeyAPI17) { // This verifies that entitled key sessions can be created and removed. TEST_P(OEMCryptoLicenseTest, EntitledKeySessionsAPI17) { + 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()); @@ -6270,116 +6273,6 @@ class OEMCryptoUsesCertificate : public OEMCryptoLoadsCertificate { Session session_; }; -// 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) { - const std::chrono::milliseconds kTestDuration(5000); - OEMCryptoResult sts; - std::chrono::steady_clock clock; - wvutil::TestSleep::Sleep(kShortSleep); // Make sure we are not nonce limited. - - auto start_time = clock.now(); - int count = 15; - for (int i = 0; i < count; i++) { // Only 20 nonce available. - ASSERT_NO_FATAL_FAILURE(CreateWrappedRSAKey()); - } - auto delta_time = clock.now() - start_time; - const double provision_time = - delta_time / std::chrono::milliseconds(1) / count; - - Session session; - ASSERT_NO_FATAL_FAILURE(CreateWrappedRSAKey()); - start_time = clock.now(); - count = 0; - while (clock.now() - start_time < kTestDuration) { - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(s.LoadWrappedRsaDrmKey(wrapped_rsa_key_)); - const size_t size = 50; - vector licenseRequest(size); - GetRandBytes(licenseRequest.data(), licenseRequest.size()); - size_t signature_length = 0; - sts = OEMCrypto_GenerateRSASignature(s.session_id(), licenseRequest.data(), - licenseRequest.size(), nullptr, - &signature_length, kSign_RSASSA_PSS); - ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); - ASSERT_NE(static_cast(0), signature_length); - - if (ShouldGenerateCorpus()) { - const std::string file_name = - GetFileName("oemcrypto_generate_rsa_signature_fuzz_seed_corpus"); - OEMCrypto_Generate_RSA_Signature_Fuzz fuzzed_structure; - fuzzed_structure.padding_scheme = kSign_RSASSA_PSS; - fuzzed_structure.signature_length = signature_length; - // Cipher mode and algorithm. - AppendToFile(file_name, reinterpret_cast(&fuzzed_structure), - sizeof(fuzzed_structure)); - AppendToFile(file_name, - reinterpret_cast(licenseRequest.data()), - licenseRequest.size()); - } - - std::vector signature(signature_length, 0); - sts = OEMCrypto_GenerateRSASignature( - s.session_id(), licenseRequest.data(), licenseRequest.size(), - signature.data(), &signature_length, kSign_RSASSA_PSS); - ASSERT_EQ(OEMCrypto_SUCCESS, sts); - count++; - } - delta_time = clock.now() - start_time; - const double license_request_time = - delta_time / std::chrono::milliseconds(1) / count; - - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(s.LoadWrappedRsaDrmKey(wrapped_rsa_key_)); - vector session_key; - vector enc_session_key; - ASSERT_NO_FATAL_FAILURE(s.SetRsaPublicKeyFromPrivateKeyInfo( - encoded_rsa_key_.data(), encoded_rsa_key_.size())); - ASSERT_TRUE(s.GenerateRsaSessionKey(&session_key, &enc_session_key)); - vector mac_context; - vector enc_context; - s.FillDefaultContext(&mac_context, &enc_context); - - enc_session_key = wvutil::a2b_hex( - "7789c619aa3b9fa3c0a53f57a4abc6" - "02157c8aa57e3c6fb450b0bea22667fb" - "0c3200f9d9d618e397837c720dc2dadf" - "486f33590744b2a4e54ca134ae7dbf74" - "434c2fcf6b525f3e132262f05ea3b3c1" - "198595c0e52b573335b2e8a3debd0d0d" - "d0306f8fcdde4e76476be71342957251" - "e1688c9ca6c1c34ed056d3b989394160" - "cf6937e5ce4d39cc73d11a2e93da21a2" - "fa019d246c852fe960095b32f120c3c2" - "7085f7b64aac344a68d607c0768676ce" - "d4c5b2d057f7601921b453a451e1dea0" - "843ebfef628d9af2784d68e86b730476" - "e136dfe19989de4be30a4e7878efcde5" - "ad2b1254f80c0c5dd3cf111b56572217" - "b9f58fc1dacbf74b59d354a1e62cfa0e" - "bf"); - start_time = clock.now(); - while (clock.now() - start_time < kTestDuration) { - ASSERT_EQ(OEMCrypto_SUCCESS, - OEMCrypto_DeriveKeysFromSessionKey( - s.session_id(), enc_session_key.data(), - enc_session_key.size(), mac_context.data(), - mac_context.size(), enc_context.data(), enc_context.size())); - count++; - } - delta_time = clock.now() - start_time; - const double derive_keys_time = - 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:stat, %u, %8.3f, %8.3f, %8.3f\n", - static_cast(level), provision_time, license_request_time, - derive_keys_time); -} - // Test DeriveKeysFromSessionKey using the maximum size for the HMAC context. TEST_F(OEMCryptoUsesCertificate, GenerateDerivedKeysLargeBuffer) { vector session_key; diff --git a/util/src/string_conversions.cpp b/util/src/string_conversions.cpp index 37ac2ef..0470d3a 100644 --- a/util/src/string_conversions.cpp +++ b/util/src/string_conversions.cpp @@ -32,12 +32,12 @@ const char kBase64SafeCodes[] = // Decodes a single Base64 encoded character into its 6-bit value. // The provided |codes| must be a Base64 character map. -int DecodeBase64Char(char c, const char* codes) { +int32_t DecodeBase64Char(char c, const char* codes) { const char* c_in_codes = strchr(codes, c); if (c_in_codes == nullptr) return -1; const uintptr_t c_in_codes_int = reinterpret_cast(c_in_codes); const uintptr_t codes_int = reinterpret_cast(codes); - return static_cast(c_in_codes_int - codes_int); + return static_cast(c_in_codes_int - codes_int); } bool DecodeHexChar(char ch, uint8_t* digit) { @@ -124,7 +124,7 @@ std::vector Base64DecodeInternal(const char* encoded, size_t length, break; } - const int decoded = DecodeBase64Char(encoded[i], codes); + const int32_t decoded = DecodeBase64Char(encoded[i], codes); if (decoded < 0) { LOGE("base64Decode failed"); return std::vector(); @@ -167,8 +167,8 @@ std::vector a2b_hex(const std::string& byte) { } for (size_t i = 0; i < count / 2; ++i) { - unsigned char msb = 0; // most significant 4 bits - unsigned char lsb = 0; // least significant 4 bits + uint8_t msb = 0; // most significant 4 bits + uint8_t lsb = 0; // least significant 4 bits if (!DecodeHexChar(byte[i * 2], &msb) || !DecodeHexChar(byte[i * 2 + 1], &lsb)) { LOGE("Invalid hex value %c%c at index %zu", byte[i * 2], byte[i * 2 + 1], @@ -219,7 +219,7 @@ std::string unlimited_b2a_hex(const std::string& byte) { } std::string HexEncode(const uint8_t* in_buffer, size_t size) { - constexpr unsigned int kMaxSafeSize = 2048; + constexpr size_t kMaxSafeSize = 2048; if (size > kMaxSafeSize) size = kMaxSafeSize; return UnlimitedHexEncode(in_buffer, size); } @@ -229,7 +229,7 @@ std::string UnlimitedHexEncode(const uint8_t* in_buffer, size_t size) { if (size == 0) return ""; // Each input byte creates two output hex characters. std::string out_buffer(size * 2, '\0'); - for (unsigned int i = 0; i < size; ++i) { + for (size_t i = 0; i < size; ++i) { char byte = in_buffer[i]; out_buffer[(i << 1)] = kHexChars[(byte >> 4) & 0xf]; out_buffer[(i << 1) + 1] = kHexChars[byte & 0xf]; @@ -331,7 +331,7 @@ int64_t htonll64(int64_t x) { } // Encode unsigned integer into a big endian formatted string -std::string EncodeUint32(unsigned int u) { +std::string EncodeUint32(uint32_t u) { std::string s; s.push_back((u >> 24) & 0xFF); s.push_back((u >> 16) & 0xFF);