diff --git a/CHANGELOG.md b/CHANGELOG.md index 68086de..0eb8bc1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,37 @@ [TOC] +## [Version 17.5][v17.5] + +This release is mostly testing-focused, including new tests that provide +stricter enforcement of the existing OEMCrypto specification. + +This release of OPK fixes a rare but possible null dereference bug. + +### Tests + +- Added new tests to better validate the behavior of + `OEMCrypto_BuildInformation()` + - Verifies output length is set correctly + - Verifies content is ASCII text without trailing null bytes +- Removed OEMCryptoLicenseTest.RejectCbc1API16 +- Fixed erroneous failures on devices with low TEE memory caused by sending an + output buffer to decrypt that was much larger than necessary + +### API + +- Clarified the expected handling of the pattern (0,0) in cbcs mode. For more + information, please check the [OEMCrypto v17 Delta Document][delta-17]. + +### OPK + +- Fixed a potential null dereference in `OPK_DispatchMessage()` if the allocator + runs out of memory +- Fixed incorrect behavior of `OEMCrypto_RemoveEntitledKeySession()` when the + session is already closed + +[delta-17]: https://developers.google.com/widevine/drm/client/oemcrypto/v17/delta + ## [Version 17.4][v17.4] This is a minor release that includes a few security fixes. @@ -289,3 +320,4 @@ Public release for OEMCrypto API and ODK library version 16.4. [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 +[v17.5]: https://widevine-partner.googlesource.com/oemcrypto/+/refs/tags/v17.5 diff --git a/docs/Widevine_Open_Source_License_Terms.pdf b/docs/Widevine_Open_Source_License_Terms.pdf new file mode 100644 index 0000000..eee3844 Binary files /dev/null and b/docs/Widevine_Open_Source_License_Terms.pdf differ diff --git a/oemcrypto/include/OEMCryptoCENC.h b/oemcrypto/include/OEMCryptoCENC.h index 5b395fc..067948d 100644 --- a/oemcrypto/include/OEMCryptoCENC.h +++ b/oemcrypto/include/OEMCryptoCENC.h @@ -3,7 +3,7 @@ // License Agreement. /** - * @mainpage OEMCrypto API v17.4 + * @mainpage OEMCrypto API v17.5 * * 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 @@ -2495,10 +2495,20 @@ OEMCryptoResult OEMCrypto_SelectKey(OEMCrypto_SESSION session, * usually be non-zero. This mode allows devices to decrypt FMP4 HLS content, * SAMPLE-AES HLS content, as well as content using the DASH 'cbcs' scheme. * - * The skip field of OEMCrypto_CENCEncryptPatternDesc may also be zero. If - * the skip field is zero, then patterns are not in use and all crypto blocks - * in the encrypted part of the subsample are encrypted. It is not valid for - * the encrypt field to be zero. + * The skip field of OEMCrypto_CENCEncryptPatternDesc may be zero. If the skip + * field is zero, then patterns are not in use and all crypto blocks in the + * encrypted part of the subsample are encrypted, except for any partial crypto + * blocks at the end. The most common pattern with a skip field of zero is + * (10,0), but all patterns with a skip field of zero are functionally the same. + * + * If the skip field of OEMCrypto_CENCEncryptPatternDesc is zero, the encrypt + * field may also be zero. This pattern sometimes appears in content, + * particularly in audio tracks. This (0,0) pattern should be treated as + * equivalent to the pattern (10,0). e.g. All complete crypto blocks should be + * decrypted. + * + * It is not valid for the encrypt field of OEMCrypto_CENCEncryptPatternDesc to + * be zero if the skip field is non-zero. * * The length of a crypto block in AES-128 is 16 bytes. In the 'cbcs' scheme, * if the encrypted part of a subsample has a length that is not a multiple diff --git a/oemcrypto/odk/include/core_message_features.h b/oemcrypto/odk/include/core_message_features.h index 7c3406b..8c97b12 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.4. + // number. The default is 17.5. uint32_t maximum_major_version = 17; - uint32_t maximum_minor_version = 4; + uint32_t maximum_minor_version = 5; 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 ff8958e..0a77f1c 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 4 +#define ODK_MINOR_VERSION 5 /* ODK Version string. Date changed automatically on each release. */ -#define ODK_RELEASE_DATE "ODK v17.4 2024-06-04" +#define ODK_RELEASE_DATE "ODK v17.5 2024-09-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 fa5b75b..4f14f6b 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 = 4; // 17.4 + features.maximum_minor_version = 5; // 17.5 break; default: features.maximum_minor_version = 0; diff --git a/oemcrypto/odk/src/odk_timer.c b/oemcrypto/odk/src/odk_timer.c index a1b37fd..6b2cac8 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 = 4; + nonce_values->api_minor_version = 5; 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 0116a9b..e104903 100644 --- a/oemcrypto/odk/test/odk_test.cpp +++ b/oemcrypto/odk/test/odk_test.cpp @@ -875,6 +875,7 @@ std::vector TestCases() { {17, 17, 2, 17, 2}, {17, 17, 3, 17, 3}, {17, 17, 4, 17, 4}, + {17, 17, 5, 17, 5}, }; return test_cases; } diff --git a/oemcrypto/opk/oemcrypto_ta/oemcrypto.c b/oemcrypto/opk/oemcrypto_ta/oemcrypto.c index 14e4910..d3d1820 100644 --- a/oemcrypto/opk/oemcrypto_ta/oemcrypto.c +++ b/oemcrypto/opk/oemcrypto_ta/oemcrypto.c @@ -3243,7 +3243,13 @@ OEMCryptoResult OEMCrypto_RemoveEntitledKeySession( OEMCryptoEntitledKeySession* key_session_context = NULL; OEMCryptoResult result = GetSessionContext(key_session, &session_context, &key_session_context); - if (result != OEMCrypto_SUCCESS) return result; + if (result != OEMCrypto_SUCCESS) { + // In case that the entitlement session is closed prior to the entitled key + // session, the result of OPKI_GetSession() will not be OEMCrypto_SUCCESS, + // and that's ok. This entitled key session should already be released when + // its entitlement session was closed. Just return success here. + return OEMCrypto_SUCCESS; + } ABORT_IF(session_context == NULL, "Failed to get the entitlement session context."); ABORT_IF(key_session_context == NULL, diff --git a/oemcrypto/opk/oemcrypto_ta/oemcrypto_api_macros.h b/oemcrypto/opk/oemcrypto_ta/oemcrypto_api_macros.h index 17b99b2..463a9c5 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 4 +#define API_MINOR_VERSION 5 #define OPK_PATCH_VERSION 0 #endif /* OEMCRYPTO_TA_OEMCRYPTO_API_MACROS_H_ */ diff --git a/oemcrypto/opk/ports/linux/cas/tee/tuner_tee_serialization.c b/oemcrypto/opk/ports/linux/cas/tee/tuner_tee_serialization.c index e75233b..1c681df 100644 --- a/oemcrypto/opk/ports/linux/cas/tee/tuner_tee_serialization.c +++ b/oemcrypto/opk/ports/linux/cas/tee/tuner_tee_serialization.c @@ -67,10 +67,9 @@ ODK_MessageStatus Tuner_DispatchMessage(ODK_Message* request, OEMCrypto_CENCEncryptPatternDesc* pattern = (OEMCrypto_CENCEncryptPatternDesc*)OPK_VarAlloc( sizeof(OEMCrypto_CENCEncryptPatternDesc)); + if (!pattern) goto handle_out_of_memory; 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); @@ -90,6 +89,12 @@ handle_invalid_request: LOGE("invalid request"); *response = CreateEmptyMessage(); return MESSAGE_STATUS_OK; + +handle_out_of_memory: + LOGE("out of memory"); + ODK_Message_SetStatus(request, MESSAGE_STATUS_OUT_OF_MEMORY); + *response = CreateEmptyMessage(); + return MESSAGE_STATUS_OK; } void OPK_Unpack_TunerHal_Decrypt_Request( diff --git a/oemcrypto/opk/serialization/tee/GEN_dispatcher.c b/oemcrypto/opk/serialization/tee/GEN_dispatcher.c index 617b623..d3961e2 100644 --- a/oemcrypto/opk/serialization/tee/GEN_dispatcher.c +++ b/oemcrypto/opk/serialization/tee/GEN_dispatcher.c @@ -331,12 +331,14 @@ ODK_MessageStatus OPK_DispatchMessage(ODK_Message* request, size_t message_length; OPK_Init_size_t((size_t*)&message_length); size_t* signature_length = (size_t*)OPK_VarAlloc(sizeof(size_t)); + if (signature_length == NULL) goto handle_out_of_memory; OPK_Init_size_t(signature_length); OEMCrypto_SESSION session; OPK_Init_uint32_t((uint32_t*)&session); uint8_t* message; OPK_InitPointer((uint8_t**)&message); size_t* core_message_size = (size_t*)OPK_VarAlloc(sizeof(size_t)); + if (core_message_size == NULL) goto handle_out_of_memory; OPK_Init_size_t(core_message_size); uint8_t* signature; OPK_InitPointer((uint8_t**)&signature); @@ -360,12 +362,14 @@ ODK_MessageStatus OPK_DispatchMessage(ODK_Message* request, size_t message_length; OPK_Init_size_t((size_t*)&message_length); size_t* signature_length = (size_t*)OPK_VarAlloc(sizeof(size_t)); + if (signature_length == NULL) goto handle_out_of_memory; OPK_Init_size_t(signature_length); OEMCrypto_SESSION session; OPK_Init_uint32_t((uint32_t*)&session); uint8_t* message; OPK_InitPointer((uint8_t**)&message); size_t* core_message_size = (size_t*)OPK_VarAlloc(sizeof(size_t)); + if (core_message_size == NULL) goto handle_out_of_memory; OPK_Init_size_t(core_message_size); uint8_t* signature; OPK_InitPointer((uint8_t**)&signature); @@ -469,6 +473,7 @@ ODK_MessageStatus OPK_DispatchMessage(ODK_Message* request, OEMCrypto_KeyRefreshObject* key_array = (OEMCrypto_KeyRefreshObject*)OPK_VarAlloc( sizeof(OEMCrypto_KeyRefreshObject)); + if (key_array == NULL) goto handle_out_of_memory; OPK_Init_OEMCrypto_KeyRefreshObject( (OEMCrypto_KeyRefreshObject*)key_array); OPK_Unpack_RefreshKeys_Request(request, &session, &message, @@ -516,6 +521,7 @@ ODK_MessageStatus OPK_DispatchMessage(ODK_Message* request, size_t content_key_id_length; OPK_Init_size_t((size_t*)&content_key_id_length); size_t* key_control_block_length = (size_t*)OPK_VarAlloc(sizeof(size_t)); + if (key_control_block_length == NULL) goto handle_out_of_memory; OPK_Init_size_t(key_control_block_length); OEMCrypto_SESSION session; OPK_Init_uint32_t((uint32_t*)&session); @@ -610,13 +616,20 @@ ODK_MessageStatus OPK_DispatchMessage(ODK_Message* request, } case 120: /* OEMCrypto_LoadCasECMKeys */ { - if (!Handle_OEMCrypto_LoadCasECMKeys(request, response)) - goto handle_invalid_request; + ODK_MessageStatus status = MESSAGE_STATUS_OK; + if (!Handle_OEMCrypto_LoadCasECMKeys(request, response, &status)) { + if (status == MESSAGE_STATUS_OUT_OF_MEMORY) { + goto handle_out_of_memory; + } else { + goto handle_invalid_request; + } + } break; } case 130: /* OEMCrypto_GetOEMKeyToken */ { size_t* key_token_length = (size_t*)OPK_VarAlloc(sizeof(size_t)); + if (key_token_length == NULL) goto handle_out_of_memory; OPK_Init_size_t(key_token_length); OEMCrypto_SESSION key_session; OPK_Init_uint32_t((uint32_t*)&key_session); @@ -665,6 +678,7 @@ ODK_MessageStatus OPK_DispatchMessage(ODK_Message* request, OEMCrypto_CENCEncryptPatternDesc* pattern = (OEMCrypto_CENCEncryptPatternDesc*)OPK_VarAlloc( sizeof(OEMCrypto_CENCEncryptPatternDesc)); + if (pattern == NULL) goto handle_out_of_memory; OPK_Init_OEMCrypto_CENCEncryptPatternDesc( (OEMCrypto_CENCEncryptPatternDesc*)pattern); OPK_Unpack_DecryptCENC_Request(request, &session, &samples, @@ -687,6 +701,7 @@ ODK_MessageStatus OPK_DispatchMessage(ODK_Message* request, OEMCrypto_DestBufferDesc* out_buffer_descriptor = (OEMCrypto_DestBufferDesc*)OPK_VarAlloc( sizeof(OEMCrypto_DestBufferDesc)); + if (out_buffer_descriptor == NULL) goto handle_out_of_memory; OPK_Init_OEMCrypto_DestBufferDesc( (OEMCrypto_DestBufferDesc*)out_buffer_descriptor); uint8_t subsample_flags; @@ -761,6 +776,7 @@ ODK_MessageStatus OPK_DispatchMessage(ODK_Message* request, size_t buffer_length; OPK_Init_size_t((size_t*)&buffer_length); size_t* signature_length = (size_t*)OPK_VarAlloc(sizeof(size_t)); + if (signature_length == NULL) goto handle_out_of_memory; OPK_Init_size_t(signature_length); OEMCrypto_SESSION session; OPK_Init_uint32_t((uint32_t*)&session); @@ -815,6 +831,7 @@ ODK_MessageStatus OPK_DispatchMessage(ODK_Message* request, OPK_Init_size_t((size_t*)&keybox_or_cert_length); size_t* wrapped_keybox_or_cert_length = (size_t*)OPK_VarAlloc(sizeof(size_t)); + if (wrapped_keybox_or_cert_length == NULL) goto handle_out_of_memory; OPK_Init_size_t(wrapped_keybox_or_cert_length); size_t transport_key_length; OPK_Init_size_t((size_t*)&transport_key_length); @@ -881,6 +898,7 @@ ODK_MessageStatus OPK_DispatchMessage(ODK_Message* request, case 7: /* OEMCrypto_GetDeviceID */ { size_t* device_id_length = (size_t*)OPK_VarAlloc(sizeof(size_t)); + if (device_id_length == NULL) goto handle_out_of_memory; OPK_Init_size_t(device_id_length); uint8_t* device_id; OPK_InitPointer((uint8_t**)&device_id); @@ -898,9 +916,11 @@ ODK_MessageStatus OPK_DispatchMessage(ODK_Message* request, { size_t* wrapped_private_key_length = (size_t*)OPK_VarAlloc(sizeof(size_t)); + if (wrapped_private_key_length == NULL) goto handle_out_of_memory; OPK_Init_size_t(wrapped_private_key_length); uint8_t* clear_private_key_bytes = (uint8_t*)OPK_VarAlloc(sizeof(uint8_t)); + if (clear_private_key_bytes == NULL) goto handle_out_of_memory; 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); @@ -923,6 +943,7 @@ ODK_MessageStatus OPK_DispatchMessage(ODK_Message* request, case 4: /* OEMCrypto_GetKeyData */ { size_t* key_data_length = (size_t*)OPK_VarAlloc(sizeof(size_t)); + if (key_data_length == NULL) goto handle_out_of_memory; OPK_Init_size_t(key_data_length); uint8_t* key_data; OPK_InitPointer((uint8_t**)&key_data); @@ -967,6 +988,7 @@ ODK_MessageStatus OPK_DispatchMessage(ODK_Message* request, case 104: /* OEMCrypto_GetOEMPublicCertificate */ { size_t* public_cert_length = (size_t*)OPK_VarAlloc(sizeof(size_t)); + if (public_cert_length == NULL) goto handle_out_of_memory; OPK_Init_size_t(public_cert_length); uint8_t* public_cert; OPK_InitPointer((uint8_t**)&public_cert); @@ -1023,6 +1045,7 @@ ODK_MessageStatus OPK_DispatchMessage(ODK_Message* request, case 125: /* OEMCrypto_BuildInformation */ { size_t* buffer_length = (size_t*)OPK_VarAlloc(sizeof(size_t)); + if (buffer_length == NULL) goto handle_out_of_memory; OPK_Init_size_t(buffer_length); char* buffer; OPK_InitPointer((uint8_t**)&buffer); @@ -1221,6 +1244,7 @@ ODK_MessageStatus OPK_DispatchMessage(ODK_Message* request, OPK_Init_size_t((size_t*)&signature_length); size_t* wrapped_private_key_length = (size_t*)OPK_VarAlloc(sizeof(size_t)); + if (wrapped_private_key_length == NULL) goto handle_out_of_memory; OPK_Init_size_t(wrapped_private_key_length); OEMCrypto_SESSION session; OPK_Init_uint32_t((uint32_t*)&session); @@ -1285,6 +1309,7 @@ ODK_MessageStatus OPK_DispatchMessage(ODK_Message* request, size_t message_length; OPK_Init_size_t((size_t*)&message_length); size_t* signature_length = (size_t*)OPK_VarAlloc(sizeof(size_t)); + if (signature_length == NULL) goto handle_out_of_memory; OPK_Init_size_t(signature_length); OEMCrypto_SESSION session; OPK_Init_uint32_t((uint32_t*)&session); @@ -1313,12 +1338,14 @@ ODK_MessageStatus OPK_DispatchMessage(ODK_Message* request, size_t message_length; OPK_Init_size_t((size_t*)&message_length); size_t* signature_length = (size_t*)OPK_VarAlloc(sizeof(size_t)); + if (signature_length == NULL) goto handle_out_of_memory; OPK_Init_size_t(signature_length); OEMCrypto_SESSION session; OPK_Init_uint32_t((uint32_t*)&session); uint8_t* message; OPK_InitPointer((uint8_t**)&message); size_t* core_message_size = (size_t*)OPK_VarAlloc(sizeof(size_t)); + if (core_message_size == NULL) goto handle_out_of_memory; OPK_Init_size_t(core_message_size); uint8_t* signature; OPK_InitPointer((uint8_t**)&signature); @@ -1340,6 +1367,7 @@ ODK_MessageStatus OPK_DispatchMessage(ODK_Message* request, case 61: /* OEMCrypto_CreateUsageTableHeader */ { size_t* header_buffer_length = (size_t*)OPK_VarAlloc(sizeof(size_t)); + if (header_buffer_length == NULL) goto handle_out_of_memory; OPK_Init_size_t(header_buffer_length); uint8_t* header_buffer; OPK_InitPointer((uint8_t**)&header_buffer); @@ -1427,8 +1455,10 @@ ODK_MessageStatus OPK_DispatchMessage(ODK_Message* request, case 65: /* OEMCrypto_UpdateUsageEntry */ { size_t* header_buffer_length = (size_t*)OPK_VarAlloc(sizeof(size_t)); + if (header_buffer_length == NULL) goto handle_out_of_memory; OPK_Init_size_t(header_buffer_length); size_t* entry_buffer_length = (size_t*)OPK_VarAlloc(sizeof(size_t)); + if (entry_buffer_length == NULL) goto handle_out_of_memory; OPK_Init_size_t(entry_buffer_length); OEMCrypto_SESSION session; OPK_Init_uint32_t((uint32_t*)&session); @@ -1474,6 +1504,7 @@ ODK_MessageStatus OPK_DispatchMessage(ODK_Message* request, size_t pst_length; OPK_Init_size_t((size_t*)&pst_length); size_t* buffer_length = (size_t*)OPK_VarAlloc(sizeof(size_t)); + if (buffer_length == NULL) goto handle_out_of_memory; OPK_Init_size_t(buffer_length); OEMCrypto_SESSION session; OPK_Init_uint32_t((uint32_t*)&session); @@ -1510,6 +1541,7 @@ ODK_MessageStatus OPK_DispatchMessage(ODK_Message* request, case 67: /* OEMCrypto_ShrinkUsageTableHeader */ { size_t* header_buffer_length = (size_t*)OPK_VarAlloc(sizeof(size_t)); + if (header_buffer_length == NULL) goto handle_out_of_memory; OPK_Init_size_t(header_buffer_length); uint32_t new_entry_count; OPK_Init_uint32_t((uint32_t*)&new_entry_count); @@ -1530,9 +1562,11 @@ ODK_MessageStatus OPK_DispatchMessage(ODK_Message* request, case 116: /* OEMCrypto_GetBootCertificateChain */ { size_t* bcc_length = (size_t*)OPK_VarAlloc(sizeof(size_t)); + if (bcc_length == NULL) goto handle_out_of_memory; OPK_Init_size_t(bcc_length); size_t* additional_signature_length = (size_t*)OPK_VarAlloc(sizeof(size_t)); + if (additional_signature_length == NULL) goto handle_out_of_memory; OPK_Init_size_t(additional_signature_length); uint8_t* bcc; OPK_InitPointer((uint8_t**)&bcc); @@ -1555,12 +1589,15 @@ ODK_MessageStatus OPK_DispatchMessage(ODK_Message* request, case 117: /* OEMCrypto_GenerateCertificateKeyPair */ { size_t* public_key_length = (size_t*)OPK_VarAlloc(sizeof(size_t)); + if (public_key_length == NULL) goto handle_out_of_memory; OPK_Init_size_t(public_key_length); size_t* public_key_signature_length = (size_t*)OPK_VarAlloc(sizeof(size_t)); + if (public_key_signature_length == NULL) goto handle_out_of_memory; OPK_Init_size_t(public_key_signature_length); size_t* wrapped_private_key_length = (size_t*)OPK_VarAlloc(sizeof(size_t)); + if (wrapped_private_key_length == NULL) goto handle_out_of_memory; OPK_Init_size_t(wrapped_private_key_length); OEMCrypto_SESSION session; OPK_Init_uint32_t((uint32_t*)&session); @@ -1690,6 +1727,7 @@ ODK_MessageStatus OPK_DispatchMessage(ODK_Message* request, OEMCrypto_DestBufferDesc* output_descriptor = (OEMCrypto_DestBufferDesc*)OPK_VarAlloc( sizeof(OEMCrypto_DestBufferDesc)); + if (output_descriptor == NULL) goto handle_out_of_memory; OPK_Init_OEMCrypto_DestBufferDesc(output_descriptor); int secure_fd; OPK_Init_int((int*)&secure_fd); @@ -1707,8 +1745,10 @@ ODK_MessageStatus OPK_DispatchMessage(ODK_Message* request, case 115: /* OEMCrypto_OPK_SerializationVersion */ { uint32_t* ree_major = (uint32_t*)OPK_VarAlloc(sizeof(uint32_t)); + if (ree_major == NULL) goto handle_out_of_memory; OPK_Init_uint32_t(ree_major); uint32_t* ree_minor = (uint32_t*)OPK_VarAlloc(sizeof(uint32_t)); + if (ree_minor == NULL) goto handle_out_of_memory; OPK_Init_uint32_t(ree_minor); uint32_t* tee_major; OPK_InitPointer((uint8_t**)&tee_major); @@ -1729,6 +1769,7 @@ ODK_MessageStatus OPK_DispatchMessage(ODK_Message* request, case 113: /* OEMCrypto_GenerateOTARequest */ { size_t* buffer_length = (size_t*)OPK_VarAlloc(sizeof(size_t)); + if (buffer_length == NULL) goto handle_out_of_memory; OPK_Init_size_t(buffer_length); OEMCrypto_SESSION session; OPK_Init_uint32_t((uint32_t*)&session); @@ -1778,4 +1819,10 @@ handle_invalid_request: LOGE("invalid request"); *response = CreateEmptyMessage(); return MESSAGE_STATUS_OK; + +handle_out_of_memory: + LOGE("out of memory"); + ODK_Message_SetStatus(request, MESSAGE_STATUS_OUT_OF_MEMORY); + *response = CreateEmptyMessage(); + return MESSAGE_STATUS_OK; } diff --git a/oemcrypto/opk/serialization/tee/special_case_request_handlers.c b/oemcrypto/opk/serialization/tee/special_case_request_handlers.c index ebbfd05..97339a7 100644 --- a/oemcrypto/opk/serialization/tee/special_case_request_handlers.c +++ b/oemcrypto/opk/serialization/tee/special_case_request_handlers.c @@ -18,7 +18,8 @@ void OPK_Init_OEMCrypto_EntitledContentKeyObject( OEMCrypto_EntitledContentKeyObject* obj); bool Handle_OEMCrypto_LoadCasECMKeys(ODK_Message* request, - ODK_Message* response) { + ODK_Message* response, + ODK_MessageStatus* status) { size_t message_length; OPK_Init_size_t((size_t*)&message_length); OEMCrypto_SESSION session; @@ -28,11 +29,19 @@ bool Handle_OEMCrypto_LoadCasECMKeys(ODK_Message* request, OEMCrypto_EntitledContentKeyObject* even_key = (OEMCrypto_EntitledContentKeyObject*)OPK_VarAlloc( sizeof(OEMCrypto_EntitledContentKeyObject)); + if (!even_key) { + *status = MESSAGE_STATUS_OUT_OF_MEMORY; + return false; + } OPK_Init_OEMCrypto_EntitledContentKeyObject( (OEMCrypto_EntitledContentKeyObject*)even_key); OEMCrypto_EntitledContentKeyObject* odd_key = (OEMCrypto_EntitledContentKeyObject*)OPK_VarAlloc( sizeof(OEMCrypto_EntitledContentKeyObject)); + if (!odd_key) { + *status = MESSAGE_STATUS_OUT_OF_MEMORY; + return false; + } OPK_Init_OEMCrypto_EntitledContentKeyObject( (OEMCrypto_EntitledContentKeyObject*)odd_key); OPK_Unpack_LoadCasECMKeys_Request(request, &session, &message, @@ -44,5 +53,6 @@ bool Handle_OEMCrypto_LoadCasECMKeys(ODK_Message* request, result = OEMCrypto_LoadCasECMKeys(session, message, message_length, even_key, odd_key); *response = OPK_Pack_LoadCasECMKeys_Response(result); + *status = MESSAGE_STATUS_OK; return true; } diff --git a/oemcrypto/opk/serialization/tee/special_case_request_handlers.h b/oemcrypto/opk/serialization/tee/special_case_request_handlers.h index 4c7273e..e48c49e 100644 --- a/oemcrypto/opk/serialization/tee/special_case_request_handlers.h +++ b/oemcrypto/opk/serialization/tee/special_case_request_handlers.h @@ -16,7 +16,8 @@ extern "C" { #include "odk_message.h" bool Handle_OEMCrypto_LoadCasECMKeys(ODK_Message* request, - ODK_Message* response); + ODK_Message* response, + ODK_MessageStatus* status); #ifdef __cplusplus } // extern "C" diff --git a/oemcrypto/test/oec_decrypt_fallback_chain.cpp b/oemcrypto/test/oec_decrypt_fallback_chain.cpp index cbf552b..0846b53 100644 --- a/oemcrypto/test/oec_decrypt_fallback_chain.cpp +++ b/oemcrypto/test/oec_decrypt_fallback_chain.cpp @@ -17,7 +17,6 @@ void advance_dest_buffer(OEMCrypto_DestBufferDesc* dest_buffer, size_t bytes) { switch (dest_buffer->type) { case OEMCrypto_BufferType_Clear: dest_buffer->buffer.clear.clear_buffer += bytes; - dest_buffer->buffer.clear.clear_buffer_length -= bytes; break; case OEMCrypto_BufferType_Secure: @@ -96,6 +95,11 @@ OEMCryptoResult DecryptFallbackChain::DecryptSample( const size_t length = subsample.num_bytes_clear + subsample.num_bytes_encrypted; fake_sample.buffers.input_data_length = length; + if (fake_sample.buffers.output_descriptor.type == + OEMCrypto_BufferType_Clear) { + fake_sample.buffers.output_descriptor.buffer.clear.clear_buffer_length = + length; + } fake_sample.subsamples = &subsample; fake_sample.subsamples_length = 1; @@ -138,6 +142,11 @@ OEMCryptoResult DecryptFallbackChain::DecryptSubsample( if (subsample.num_bytes_clear > 0) { fake_sample.buffers.input_data_length = subsample.num_bytes_clear; + if (fake_sample.buffers.output_descriptor.type == + OEMCrypto_BufferType_Clear) { + fake_sample.buffers.output_descriptor.buffer.clear.clear_buffer_length = + subsample.num_bytes_clear; + } fake_subsample.num_bytes_clear = subsample.num_bytes_clear; fake_subsample.num_bytes_encrypted = 0; fake_subsample.block_offset = 0; @@ -160,6 +169,11 @@ OEMCryptoResult DecryptFallbackChain::DecryptSubsample( if (subsample.num_bytes_encrypted > 0) { fake_sample.buffers.input_data_length = subsample.num_bytes_encrypted; + if (fake_sample.buffers.output_descriptor.type == + OEMCrypto_BufferType_Clear) { + fake_sample.buffers.output_descriptor.buffer.clear.clear_buffer_length = + subsample.num_bytes_encrypted; + } fake_subsample.num_bytes_clear = 0; fake_subsample.num_bytes_encrypted = subsample.num_bytes_encrypted; fake_subsample.block_offset = subsample.block_offset; diff --git a/oemcrypto/test/oec_device_features.cpp b/oemcrypto/test/oec_device_features.cpp index 9db9c00..88990f4 100644 --- a/oemcrypto/test/oec_device_features.cpp +++ b/oemcrypto/test/oec_device_features.cpp @@ -10,7 +10,9 @@ #include +#include "log.h" #include "oec_test_data.h" +#include "string_conversions.h" #include "test_sleep.h" namespace wvoec { @@ -58,6 +60,12 @@ void DeviceFeatures::Initialize() { loads_certificate = false; } printf("loads_certificate = %s.\n", loads_certificate ? "true" : "false"); + if (rsa_test_key().empty()) { + set_rsa_test_key( + std::vector(kTestRSAPKCS8PrivateKeyInfo2_2048, + kTestRSAPKCS8PrivateKeyInfo2_2048 + + sizeof(kTestRSAPKCS8PrivateKeyInfo2_2048))); + } generic_crypto = (OEMCrypto_ERROR_NOT_IMPLEMENTED != OEMCrypto_Generic_Encrypt(session, buffer, 0, iv, @@ -113,6 +121,9 @@ void DeviceFeatures::Initialize() { case LOAD_TEST_RSA_KEY: printf("LOAD_TEST_RSA_KEY: Call LoadTestRSAKey before deriving keys.\n"); break; + case PRELOADED_RSA_KEY: + printf("PRELOADED_RSA_KEY: Device has test RSA key baked in.\n"); + break; case TEST_PROVISION_30: printf("TEST_PROVISION_30: Device provisioned with OEM Cert.\n"); break; @@ -181,9 +192,10 @@ void DeviceFeatures::PickDerivedKey() { derive_key_method = TEST_PROVISION_30; return; case OEMCrypto_DrmCertificate: - if (OEMCrypto_ERROR_NOT_IMPLEMENTED != OEMCrypto_LoadTestRSAKey()) { - derive_key_method = LOAD_TEST_RSA_KEY; - } + derive_key_method = + (OEMCrypto_ERROR_NOT_IMPLEMENTED == OEMCrypto_LoadTestRSAKey()) + ? PRELOADED_RSA_KEY + : LOAD_TEST_RSA_KEY; return; case OEMCrypto_Keybox: // Fall through to api_version < 12 case. diff --git a/oemcrypto/test/oec_device_features.h b/oemcrypto/test/oec_device_features.h index a65eef1..9034b03 100644 --- a/oemcrypto/test/oec_device_features.h +++ b/oemcrypto/test/oec_device_features.h @@ -38,6 +38,7 @@ class DeviceFeatures { LOAD_TEST_RSA_KEY, // Call LoadTestRSAKey before deriving keys. TEST_PROVISION_30, // Device has OEM Certificate installed. TEST_PROVISION_40, // Device has Boot Certificate Chain installed. + PRELOADED_RSA_KEY, // Device has test RSA key baked in. }; enum DeriveMethod derive_key_method; @@ -69,6 +70,16 @@ class DeviceFeatures { // Get a list of output types that should be tested. const std::vector& GetOutputTypes(); + // If the device has a baked in cert, then this is the public key that should + // be used for testing. + const std::vector& rsa_test_key() const { return rsa_test_key_; }; + void set_rsa_test_key(const std::vector& rsa_test_key) { + rsa_test_key_ = rsa_test_key; + } + void set_rsa_test_key(std::vector&& rsa_test_key) { + rsa_test_key_ = std::move(rsa_test_key); + } + private: // Decide which method should be used to derive session keys, based on // supported featuers. @@ -81,6 +92,7 @@ class DeviceFeatures { // A list of possible output types. std::vector output_types_; bool initialized_ = false; + std::vector rsa_test_key_; }; // There is one global set of features for the version of OEMCrypto being diff --git a/oemcrypto/test/oec_session_util.cpp b/oemcrypto/test/oec_session_util.cpp index 8b9b557..a6b1c08 100644 --- a/oemcrypto/test/oec_session_util.cpp +++ b/oemcrypto/test/oec_session_util.cpp @@ -1771,10 +1771,9 @@ void Session::LoadOEMCert(bool verify_cert) { void Session::SetTestRsaPublicKey() { public_ec_.reset(); - public_rsa_ = util::RsaPublicKey::LoadPrivateKeyInfo( - kTestRSAPKCS8PrivateKeyInfo2_2048, - sizeof(kTestRSAPKCS8PrivateKeyInfo2_2048)); - ASSERT_TRUE(public_rsa_) << "Could not parse test RSA public key #2"; + public_rsa_ = + util::RsaPublicKey::LoadPrivateKeyInfo(global_features.rsa_test_key()); + ASSERT_TRUE(public_rsa_) << "Could not parse test RSA public key"; } void Session::SetPublicKeyFromPrivateKeyInfo(OEMCrypto_PrivateKeyType key_type, diff --git a/oemcrypto/test/oemcrypto_session_tests_helper.cpp b/oemcrypto/test/oemcrypto_session_tests_helper.cpp index f84e37c..9d94351 100644 --- a/oemcrypto/test/oemcrypto_session_tests_helper.cpp +++ b/oemcrypto/test/oemcrypto_session_tests_helper.cpp @@ -63,6 +63,9 @@ void SessionUtil::EnsureTestKeys() { case DeviceFeatures::TEST_PROVISION_30: // Can use oem certificate to install test rsa key. break; + case DeviceFeatures::PRELOADED_RSA_KEY: + // There is already a key. + break; case wvoec::DeviceFeatures::TEST_PROVISION_40: // OEM certificate is retrieved from the server. break; diff --git a/oemcrypto/test/oemcrypto_test.cpp b/oemcrypto/test/oemcrypto_test.cpp index 8b9cb0b..daf801c 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.4. Tests last updated 2024-06-04"; + "OEMCrypto unit tests for API 17.5. Tests last updated 2024-09-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, 4); + EXPECT_EQ(ODK_MINOR_VERSION, 5); EXPECT_EQ(kCurrentAPI, 17u); OEMCrypto_Security_Level level = OEMCrypto_SecurityLevel(); EXPECT_GT(level, OEMCrypto_Level_Unknown); @@ -548,6 +548,118 @@ TEST_F(OEMCryptoClientTest, CheckNullBuildInformationAPI17) { } } +// Verifies that OEMCrypto_BuildInformation() is behaving as expected +// by assigning appropriate values to the build info size. +TEST_F(OEMCryptoClientTest, CheckBuildInformation_OutputLengthAPI17) { + constexpr size_t kZero = 0; + constexpr char kNullChar = '\0'; + + // Allocating single byte to avoid potential null dereference. + std::string build_info(1, kNullChar); + size_t build_info_length = 0; + + OEMCryptoResult result = + OEMCrypto_BuildInformation(&build_info[0], &build_info_length); + + ASSERT_EQ(result, OEMCrypto_ERROR_SHORT_BUFFER); + ASSERT_GT(build_info_length, kZero) + << "Signaling ERROR_SHORT_BUFFER should have assigned a length"; + + // Force a ERROR_SHORT_BUFFER using a non-zero value. + // Note: It is assumed that vendors will provide more than a single + // character of info. + const size_t second_attempt_length = + (build_info_length >= 2) ? build_info_length / 2 : 1; + build_info.assign(second_attempt_length, kNullChar); + build_info_length = build_info.size(); + + result = OEMCrypto_BuildInformation(&build_info[0], &build_info_length); + ASSERT_EQ(result, OEMCrypto_ERROR_SHORT_BUFFER) + << "second_attempt_length = " << second_attempt_length + << ", build_info_length" << build_info_length; + // OEM specified build info length should be larger than the + // original length if returning ERROR_SHORT_BUFFER. + ASSERT_GT(build_info_length, second_attempt_length); + + // Final attempt with a buffer large enough buffer, padding to + // ensure the caller truncates. + constexpr size_t kBufferPadSize = 42; + const size_t expected_length = build_info_length; + const size_t final_attempt_length = expected_length + kBufferPadSize; + build_info.assign(final_attempt_length, kNullChar); + build_info_length = build_info.size(); + + result = OEMCrypto_BuildInformation(&build_info[0], &build_info_length); + + ASSERT_EQ(result, OEMCrypto_SUCCESS) + << "final_attempt_length = " << final_attempt_length + << ", expected_length = " << expected_length + << ", build_info_length = " << build_info_length; + // Ensure not empty. + ASSERT_GT(build_info_length, kZero) << "Build info cannot be empty"; + // Ensure it was truncated down from the padded length. + ASSERT_LT(build_info_length, final_attempt_length) + << "Should have truncated from oversized buffer: expected_length = " + << expected_length; + // Ensure the real length is within the size originally specified. + // OK if final length is smaller than estimated length. + ASSERT_LE(build_info_length, expected_length); +} + +// Verifies that OEMCrypto_BuildInformation() is behaving as expected +// by checking the resulting contents. +TEST_F(OEMCryptoClientTest, CheckBuildInformation_OutputContentAPI17) { + constexpr size_t kZero = 0; + constexpr char kNullChar = '\0'; + + // Allocating single byte to avoid potential null dereference. + std::string build_info(1, kNullChar); + size_t build_info_length = 0; + OEMCryptoResult result = + OEMCrypto_BuildInformation(&build_info[0], &build_info_length); + ASSERT_EQ(result, OEMCrypto_ERROR_SHORT_BUFFER); + ASSERT_GT(build_info_length, kZero) + << "Signaling ERROR_SHORT_BUFFER should have assigned a length"; + + // Expect successful acquisition of build information. + const size_t expected_length = build_info_length; + build_info.assign(expected_length, kNullChar); + result = OEMCrypto_BuildInformation(&build_info[0], &build_info_length); + ASSERT_EQ(result, OEMCrypto_SUCCESS) + << "expected_length = " << expected_length + << ", build_info_length = " << build_info_length; + // Ensure not empty. + ASSERT_GT(build_info_length, kZero) << "Build info cannot be empty"; + // Ensure the real length is within the size originally specified. + ASSERT_LE(build_info_length, expected_length) + << "Cannot specify success if buffer was too small"; + build_info.resize(build_info_length); + + // Ensure there isn't a trailing null byte. + ASSERT_NE(build_info.back(), kNullChar) + << "Build info must not contain trailing null byte"; + + // Ensure all build info characters are printable, or a limited + // set of white space characters (case of JSON build info). + const auto is_valid_build_info_white_space = [](const char& ch) -> bool { + constexpr char kSpace = ' '; + constexpr char kLineFeed = '\n'; + constexpr char kTab = '\t'; + return ch == kLineFeed || ch == kTab || ch == kSpace; + }; + const auto is_valid_build_info_char = [&](const char& ch) -> bool { + return ::isprint(ch) || is_valid_build_info_white_space(ch); + }; + ASSERT_TRUE(std::all_of(build_info.begin(), build_info.end(), + is_valid_build_info_char)) + << "Build info is not printable: " << wvutil::b2a_hex(build_info); + + // Ensure build info isn't just white space. + ASSERT_FALSE(std::all_of(build_info.begin(), build_info.end(), + is_valid_build_info_white_space)) + << "Build info is just white space: " << wvutil::b2a_hex(build_info); +} + TEST_F(OEMCryptoClientTest, CheckMaxNumberOfSessionsAPI10) { size_t sessions_count; ASSERT_EQ(OEMCrypto_SUCCESS, @@ -3258,10 +3370,15 @@ TEST_P(OEMCryptoLicenseTest, // Close the OEMCrypto session. session_.close(); // All entitled key sessions associated with the OEMCrypto session should - // already be been destroyed, + // already be destroyed. OEMCryptoResult sts = OEMCrypto_RemoveEntitledKeySession(key_session_id_1); + // For v17, there is a discrepancy in the L3 and OPK implementation for when + // the entitlement session is closed prior to the entitled key session. This + // is because it is difficult to update L3 for Android T. To accommodate this, + // we accept both error codes and OEMCrypto_SUCCESS. EXPECT_TRUE(sts == OEMCrypto_ERROR_INVALID_ENTITLED_KEY_SESSION || - sts == OEMCrypto_ERROR_INVALID_SESSION); + sts == OEMCrypto_ERROR_INVALID_SESSION || + sts == OEMCrypto_SUCCESS); // Open a new session just for OEMCryptoLicenseTest TearDown. session_.open(); } @@ -3496,36 +3613,6 @@ TEST_P(OEMCryptoLicenseTest, RejectCensAPI16) { EXPECT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, sts); } -// 'cbc1' mode is no longer supported in v16 -TEST_P(OEMCryptoLicenseTest, RejectCbc1API16) { - ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); - ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); - ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); - ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); - - OEMCryptoResult sts; - sts = OEMCrypto_SelectKey( - session_.session_id(), session_.license().keys[0].key_id, - session_.license().keys[0].key_id_length, OEMCrypto_CipherMode_CBCS); - ASSERT_EQ(OEMCrypto_SUCCESS, sts); - - vector in_buffer(256); - vector out_buffer(in_buffer.size()); - OEMCrypto_SampleDescription sample_description; - OEMCrypto_SubSampleDescription subsample_description; - - GenerateSimpleSampleDescription(in_buffer, out_buffer, &sample_description, - &subsample_description); - - // Create a zero pattern to indicate this is 'cbc1' - OEMCrypto_CENCEncryptPatternDesc pattern = {0, 0}; - - // Try to decrypt the data - sts = OEMCrypto_DecryptCENC(session_.session_id(), &sample_description, 1, - &pattern); - EXPECT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, sts); -} - TEST_P(OEMCryptoLicenseTest, RejectCbcsWithBlockOffset) { ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); @@ -9637,7 +9724,7 @@ TEST_P(OEMCryptoUsageTableDefragTest, MoveUsageEntries) { ASSERT_NO_FATAL_FAILURE( FailReloadLicense(&entries[3], OEMCrypto_ERROR_UNKNOWN_FAILURE)); } - + TEST_P(OEMCryptoUsageTableDefragTest, MakeAndMoveEntry) { // 1. Make an entry then close. LicenseWithUsageEntry entry;