diff --git a/core/libs/prx/libSceLibreSsl/CMakeLists.txt b/core/libs/prx/libSceLibreSsl/CMakeLists.txt index ebaa00d309..e9200c508e 100644 --- a/core/libs/prx/libSceLibreSsl/CMakeLists.txt +++ b/core/libs/prx/libSceLibreSsl/CMakeLists.txt @@ -1,4 +1,4 @@ -add_library(libSceLibreSsl SHARED EXCLUDE_FROM_ALL ${CMAKE_CURRENT_SOURCE_DIR}/Export.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Bignum.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Digest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Runtime.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Aes.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Error.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Ocsp.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Stack.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Ui.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rsa.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/X509StoreContext.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Pkey.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/EvpCipherCtx.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Evp.cpp) +add_library(libSceLibreSsl SHARED EXCLUDE_FROM_ALL ${CMAKE_CURRENT_SOURCE_DIR}/Export.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Bignum.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Digest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Runtime.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Aes.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Error.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Ocsp.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Stack.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Ui.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rsa.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/X509StoreContext.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Pkey.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/EvpCipherCtx.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Evp.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Ec.cpp) target_include_directories(libSceLibreSsl PRIVATE ${LIBS_INCLUDE_DIR}) target_link_libraries(libSceLibreSsl PRIVATE mbedcrypto) configure_windows_unwind(libSceLibreSsl) @@ -181,6 +181,17 @@ if(BUILD_TESTING) set_tests_properties(guest_libressl_evp_cipher_ctx PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$") endif() + + add_test_executable(guest_libressl_ec_tests ${LIBS_INCLUDE_DIR}/tests/GuestLibreSslEc.cpp) + target_include_directories(guest_libressl_ec_tests PRIVATE ${LIBS_INCLUDE_DIR}) + target_link_libraries(guest_libressl_ec_tests PRIVATE libSceLibreSsl libc) + configure_windows_unwind(guest_libressl_ec_tests) + add_test(NAME guest_libressl_ec COMMAND guest_libressl_ec_tests) + set_tests_properties(guest_libressl_ec PROPERTIES TIMEOUT 30) + if(WIN32) + set_tests_properties(guest_libressl_ec PROPERTIES ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:$") + endif() endif() target_sources(libSceLibreSsl PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src/Cipher.cpp) diff --git a/core/libs/prx/libSceLibreSsl/Export.cpp b/core/libs/prx/libSceLibreSsl/Export.cpp index 0f80ff0573..96e18bb632 100644 --- a/core/libs/prx/libSceLibreSsl/Export.cpp +++ b/core/libs/prx/libSceLibreSsl/Export.cpp @@ -160,51 +160,21 @@ int APS5_VABI ECDSA_sign_nid_postfix(void) { return 0; } -int APS5_VABI ECDSA_size_nid_postfix(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - int APS5_VABI ECDSA_verify_nid_postfix(void) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI EC_GROUP_free_nid_postfix(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI EC_GROUP_get_degree_nid_postfix(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI EC_GROUP_new_by_curve_name_nid_postfix(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - int APS5_VABI EC_KEY_check_key_nid_postfix(void) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI EC_KEY_free_nid_postfix(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - int APS5_VABI EC_KEY_generate_key_nid_postfix(void) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI EC_KEY_get0_group_nid_postfix(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - int APS5_VABI EC_KEY_get0_private_key_nid_postfix(void) { NotImplemented_nid_no_patch(__func__); return 0; @@ -215,21 +185,6 @@ int APS5_VABI EC_KEY_get0_public_key_nid_postfix(void) { return 0; } -int APS5_VABI EC_KEY_new_nid_postfix(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI EC_KEY_new_by_curve_name_nid_postfix(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI EC_KEY_set_group_nid_postfix(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - int APS5_VABI EC_KEY_set_private_key_nid_postfix(void) { NotImplemented_nid_no_patch(__func__); return 0; diff --git a/core/libs/prx/libSceLibreSsl/include/Api.hpp b/core/libs/prx/libSceLibreSsl/include/Api.hpp index 78ebb73e80..34566845e8 100644 --- a/core/libs/prx/libSceLibreSsl/include/Api.hpp +++ b/core/libs/prx/libSceLibreSsl/include/Api.hpp @@ -323,6 +323,9 @@ struct LibreSslEvpCipherCtx { unsigned char final[32]; }; +struct EcGroup; +struct EcKey; + extern "C" { std::int64_t APS5_VABI ASN1_INTEGER_get_nid_postfix(const Asn1String* value); @@ -380,20 +383,20 @@ int APS5_VABI DTLSv1_client_method_nid_postfix(void); int APS5_VABI DTLSv1_server_method_nid_postfix(void); int APS5_VABI ECDH_compute_key_nid_postfix(void); int APS5_VABI ECDSA_sign_nid_postfix(void); -int APS5_VABI ECDSA_size_nid_postfix(void); +int APS5_VABI ECDSA_size_nid_postfix(const EcKey* key); int APS5_VABI ECDSA_verify_nid_postfix(void); -int APS5_VABI EC_GROUP_free_nid_postfix(void); -int APS5_VABI EC_GROUP_get_degree_nid_postfix(void); -int APS5_VABI EC_GROUP_new_by_curve_name_nid_postfix(void); +void APS5_VABI EC_GROUP_free_nid_postfix(EcGroup* group); +int APS5_VABI EC_GROUP_get_degree_nid_postfix(const EcGroup* group); +EcGroup* APS5_VABI EC_GROUP_new_by_curve_name_nid_postfix(int nid); int APS5_VABI EC_KEY_check_key_nid_postfix(void); -int APS5_VABI EC_KEY_free_nid_postfix(void); +void APS5_VABI EC_KEY_free_nid_postfix(EcKey* key); int APS5_VABI EC_KEY_generate_key_nid_postfix(void); -int APS5_VABI EC_KEY_get0_group_nid_postfix(void); +const EcGroup* APS5_VABI EC_KEY_get0_group_nid_postfix(const EcKey* key); int APS5_VABI EC_KEY_get0_private_key_nid_postfix(void); int APS5_VABI EC_KEY_get0_public_key_nid_postfix(void); -int APS5_VABI EC_KEY_new_nid_postfix(void); -int APS5_VABI EC_KEY_new_by_curve_name_nid_postfix(void); -int APS5_VABI EC_KEY_set_group_nid_postfix(void); +EcKey* APS5_VABI EC_KEY_new_nid_postfix(void); +EcKey* APS5_VABI EC_KEY_new_by_curve_name_nid_postfix(int nid); +int APS5_VABI EC_KEY_set_group_nid_postfix(EcKey* key, const EcGroup* group); int APS5_VABI EC_KEY_set_private_key_nid_postfix(void); int APS5_VABI EC_KEY_set_public_key_nid_postfix(void); int APS5_VABI EC_POINT_free_nid_postfix(void); diff --git a/core/libs/prx/libSceLibreSsl/include/Ec.hpp b/core/libs/prx/libSceLibreSsl/include/Ec.hpp new file mode 100644 index 0000000000..5789a3d8b8 --- /dev/null +++ b/core/libs/prx/libSceLibreSsl/include/Ec.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include +#include "mbedtls/ecp.h" + +struct NamedCurve { + int nid; + mbedtls_ecp_group_id id; +}; + +struct EcGroup { + explicit EcGroup(const NamedCurve& named); + ~EcGroup(); + + EcGroup(const EcGroup&) = delete; + EcGroup& operator=(const EcGroup&) = delete; + + const NamedCurve curve; + mbedtls_ecp_group group; +}; + +struct EcKey { + std::unique_ptr group; +}; diff --git a/core/libs/prx/libSceLibreSsl/src/Ec.cpp b/core/libs/prx/libSceLibreSsl/src/Ec.cpp new file mode 100644 index 0000000000..c2b3f06b9d --- /dev/null +++ b/core/libs/prx/libSceLibreSsl/src/Ec.cpp @@ -0,0 +1,110 @@ +#include "prx/libSceLibreSsl/include/Api.hpp" +#include +#include +#include +#include +#include "prx/libSceLibreSsl/include/Ec.hpp" +#include "prx/libc/include/General.hpp" +#include "mbedtls/bignum.h" +#include "mbedtls/ecp.h" + +EcGroup::EcGroup(const NamedCurve& named) : curve(named) { + mbedtls_ecp_group_init(&group); + if (mbedtls_ecp_group_load(&group, named.id) != 0) { + mbedtls_ecp_group_free(&group); + throw std::runtime_error("EC_GROUP: mbedtls failed to load curve " + std::to_string(named.nid)); + } +} + +EcGroup::~EcGroup() { + mbedtls_ecp_group_free(&group); +} + +namespace { +constexpr NamedCurve NamedCurves[] = { + {409, MBEDTLS_ECP_DP_SECP192R1}, + {415, MBEDTLS_ECP_DP_SECP256R1}, + {711, MBEDTLS_ECP_DP_SECP192K1}, + {712, MBEDTLS_ECP_DP_SECP224K1}, + {713, MBEDTLS_ECP_DP_SECP224R1}, + {714, MBEDTLS_ECP_DP_SECP256K1}, + {715, MBEDTLS_ECP_DP_SECP384R1}, + {716, MBEDTLS_ECP_DP_SECP521R1}, + {928, MBEDTLS_ECP_DP_BP256R1}, + {932, MBEDTLS_ECP_DP_BP384R1}, + {934, MBEDTLS_ECP_DP_BP512R1}, +}; + +constexpr std::pair CurvesMissingFromMbedtls[] = { + {410, 414}, {684, 690}, {693, 696}, {699, 710}, {717, 745}, {749, 750}, + {839, 844}, {922, 927}, {929, 931}, {933, 933}, {935, 936}, {943, 944}, +}; + +std::unique_ptr NewGroup(int nid) { + for (const NamedCurve& curve : NamedCurves) { + if (curve.nid == nid) return std::make_unique(curve); + } + for (const auto& [first, last] : CurvesMissingFromMbedtls) { + if (nid >= first && nid <= last) NotImplemented_nid_no_patch("EC_GROUP_new_by_curve_name: a LibreSSL curve that mbedtls does not provide"); + } + return nullptr; +} + +int DerLengthSize(int length) { + int size = 1; + if (length > 127) { + for (; length > 0; length >>= 8) ++size; + } + return size; +} + +} + +extern "C" { + +EcGroup* APS5_VABI EC_GROUP_new_by_curve_name_nid_postfix(int nid) { + return NewGroup(nid).release(); +} + +void APS5_VABI EC_GROUP_free_nid_postfix(EcGroup* group) { + delete group; +} + +int APS5_VABI EC_GROUP_get_degree_nid_postfix(const EcGroup* group) { + if (group == nullptr) APS5_INVALID_ARG_EX; + return static_cast(group->group.pbits); +} + +EcKey* APS5_VABI EC_KEY_new_nid_postfix(void) { + return new EcKey; +} + +EcKey* APS5_VABI EC_KEY_new_by_curve_name_nid_postfix(int nid) { + std::unique_ptr group = NewGroup(nid); + if (!group) return nullptr; + return new EcKey{std::move(group)}; +} + +void APS5_VABI EC_KEY_free_nid_postfix(EcKey* key) { + delete key; +} + +const EcGroup* APS5_VABI EC_KEY_get0_group_nid_postfix(const EcKey* key) { + if (key == nullptr) APS5_INVALID_ARG_EX; + return key->group.get(); +} + +int APS5_VABI EC_KEY_set_group_nid_postfix(EcKey* key, const EcGroup* group) { + if (key == nullptr) APS5_INVALID_ARG_EX; + key->group = group ? std::make_unique(group->curve) : nullptr; + return key->group ? 1 : 0; +} + +int APS5_VABI ECDSA_size_nid_postfix(const EcKey* key) { + if (key == nullptr || !key->group) return 0; + const int orderBytes = static_cast((mbedtls_mpi_bitlen(&key->group->group.N) + 7) / 8); + const int integerSize = 1 + DerLengthSize(orderBytes + 1) + orderBytes + 1; + return 1 + DerLengthSize(2 * integerSize) + 2 * integerSize; +} + +} diff --git a/core/libs/tests/GuestLibreSslEc.cpp b/core/libs/tests/GuestLibreSslEc.cpp new file mode 100644 index 0000000000..8241d895ca --- /dev/null +++ b/core/libs/tests/GuestLibreSslEc.cpp @@ -0,0 +1,108 @@ +#include "prx/libSceLibreSsl/include/Api.hpp" +#include "prx/libc/include/general/VabiMacros.hpp" +#include +#include +#include + +struct Curve { + int nid; + int degree; + int signatureSize; +}; + +static constexpr Curve Curves[] = { + {409, 192, 56}, + {415, 256, 72}, + {711, 192, 56}, + {712, 224, 66}, + {713, 224, 64}, + {714, 256, 72}, + {715, 384, 104}, + {716, 521, 141}, + {928, 256, 72}, + {932, 384, 104}, + {934, 512, 137}, +}; + +static constexpr int UnknownNids[] = {0, -1, 1, 406, 691, 746, 921, 937, 948, 100000}; + +static void Require(bool value, const char* what) { + if (value) return; + std::fprintf(stderr, "failed: %s\n", what); + std::abort(); +} + +template static void RequireThrows(TAction action, const char* what) { + try { action(); } catch (const std::exception&) { return; } + Require(false, what); +} + +static void CheckNamedCurves() { + for (const Curve& curve : Curves) { + EcGroup* group = EC_GROUP_new_by_curve_name_nid_postfix(curve.nid); + Require(group != nullptr, "EC_GROUP_new_by_curve_name for a named curve"); + Require(EC_GROUP_get_degree_nid_postfix(group) == curve.degree, "EC_GROUP_get_degree"); + EcKey* key = EC_KEY_new_by_curve_name_nid_postfix(curve.nid); + Require(key != nullptr, "EC_KEY_new_by_curve_name for a named curve"); + const EcGroup* keyGroup = EC_KEY_get0_group_nid_postfix(key); + Require(keyGroup != nullptr && keyGroup != group, "EC_KEY_get0_group of a key made from a curve name"); + Require(EC_GROUP_get_degree_nid_postfix(keyGroup) == curve.degree, "degree of the key's group"); + Require(ECDSA_size_nid_postfix(key) == curve.signatureSize, "ECDSA_size"); + EC_KEY_free_nid_postfix(key); + EC_GROUP_free_nid_postfix(group); + } +} + +static void CheckOtherNids() { + for (const int nid : UnknownNids) { + Require(EC_GROUP_new_by_curve_name_nid_postfix(nid) == nullptr, "EC_GROUP_new_by_curve_name for an unknown curve"); + Require(EC_KEY_new_by_curve_name_nid_postfix(nid) == nullptr, "EC_KEY_new_by_curve_name for an unknown curve"); + } +} + +static void CheckSetGroup() { + EcKey* key = EC_KEY_new_nid_postfix(); + Require(key != nullptr, "EC_KEY_new"); + Require(EC_KEY_get0_group_nid_postfix(key) == nullptr, "EC_KEY_new has no group"); + Require(ECDSA_size_nid_postfix(key) == 0, "ECDSA_size of a key without a group"); + + EcGroup* group = EC_GROUP_new_by_curve_name_nid_postfix(715); + Require(EC_KEY_set_group_nid_postfix(key, group) == 1, "EC_KEY_set_group"); + const EcGroup* copy = EC_KEY_get0_group_nid_postfix(key); + Require(copy != nullptr && copy != group, "EC_KEY_set_group stores a copy"); + EC_GROUP_free_nid_postfix(group); + Require(EC_GROUP_get_degree_nid_postfix(EC_KEY_get0_group_nid_postfix(key)) == 384, "the copy outlives the original group"); + Require(ECDSA_size_nid_postfix(key) == 104, "ECDSA_size after EC_KEY_set_group"); + + Require(EC_KEY_set_group_nid_postfix(key, EC_KEY_get0_group_nid_postfix(key)) == 1, "EC_KEY_set_group with the key's own group"); + Require(EC_GROUP_get_degree_nid_postfix(EC_KEY_get0_group_nid_postfix(key)) == 384, "the key's own group set again"); + + EcGroup* other = EC_GROUP_new_by_curve_name_nid_postfix(409); + Require(EC_KEY_set_group_nid_postfix(key, other) == 1, "EC_KEY_set_group replacing a group"); + Require(EC_GROUP_get_degree_nid_postfix(EC_KEY_get0_group_nid_postfix(key)) == 192, "the replaced group"); + EC_GROUP_free_nid_postfix(other); + + Require(EC_KEY_set_group_nid_postfix(key, nullptr) == 0, "EC_KEY_set_group with a null group"); + Require(EC_KEY_get0_group_nid_postfix(key) == nullptr, "a null group drops the previous one"); + Require(ECDSA_size_nid_postfix(key) == 0, "ECDSA_size after a null group"); + EC_KEY_free_nid_postfix(key); +} + +static void CheckNullArguments() { + EC_GROUP_free_nid_postfix(nullptr); + EC_KEY_free_nid_postfix(nullptr); + Require(ECDSA_size_nid_postfix(nullptr) == 0, "ECDSA_size of a null key"); + RequireThrows([] { EC_GROUP_get_degree_nid_postfix(nullptr); }, "EC_GROUP_get_degree of a null group"); + RequireThrows([] { EC_KEY_get0_group_nid_postfix(nullptr); }, "EC_KEY_get0_group of a null key"); + EcGroup* group = EC_GROUP_new_by_curve_name_nid_postfix(415); + RequireThrows([&] { EC_KEY_set_group_nid_postfix(nullptr, group); }, "EC_KEY_set_group of a null key"); + EC_GROUP_free_nid_postfix(group); +} + +int main() { + CheckNamedCurves(); + CheckOtherNids(); + CheckSetGroup(); + CheckNullArguments(); + return 0; +} diff --git a/docs/dev/debt/libSceLibreSsl.md b/docs/dev/debt/libSceLibreSsl.md index 296026380b..1755d37e93 100644 --- a/docs/dev/debt/libSceLibreSsl.md +++ b/docs/dev/debt/libSceLibreSsl.md @@ -7,7 +7,7 @@ ### Unknown function info -- [libSceLibreSsl pending exports](../../../core/libs/prx/libSceLibreSsl/Export.cpp) - 231 named callable exports are listed in the [PS5 3.20 export table](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceLibreSsl.c) and were cross-checked against the same module in PS5 firmware 13.60. Their signatures and behaviour are unverified; the declarations use the no-parameter unknown-signature convention and throw through `NotImplemented_nid_no_patch`. +- [libSceLibreSsl pending exports](../../../core/libs/prx/libSceLibreSsl/Export.cpp) - 222 named callable exports are listed in the [PS5 3.20 export table](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceLibreSsl.c) and were cross-checked against the same module in PS5 firmware 13.60. Their signatures and behaviour are unverified; the declarations use the no-parameter unknown-signature convention and throw through `NotImplemented_nid_no_patch`. - [MD4, MD5, SHA1, SHA224, SHA256, SHA384 and SHA512 Init/Update/Final and SHA256](../../../core/libs/prx/libSceLibreSsl/src/Digest.cpp) (libSceLibreSsl) - signatures and context layouts follow the [LibreSSL headers](https://github.com/libressl/openbsd/blob/bc9483725c5815efb64b6d9acc52fc5fba139212/src/lib/libcrypto/sha/sha.h), and buffering and `Final` follow its [sources](https://github.com/libressl/openbsd/tree/bc9483725c5815efb64b6d9acc52fc5fba139212/src/lib/libcrypto/sha); nothing is checked on the console. Null contexts, null input with a nonzero length, null digest buffers in the 32-bit `Final` functions and a buffered byte count that is not below the block size, which LibreSSL does not check, throw. SHA-384 and SHA-512 initialization retains the block buffer, following [LibreSSL 2.5.4 `sha512.c`](https://github.com/libressl/openbsd/blob/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto/sha/sha512.c) - [DES_set_key, DES_ecb_encrypt, DES_set_odd_parity](../../../core/libs/prx/libSceLibreSsl/Export.cpp) (libSceLibreSsl) - the 128-byte `DES_key_schedule` holds the 8-byte key followed by zeros instead of LibreSSL's precomputed subkeys, so a title reading its contents sees different bytes; `DES_ecb_encrypt` derives the subkeys through mbedtls on each call. Not compared with the console. Results follow LibreSSL [des_key.c](https://github.com/libressl/openbsd/blob/bc9483725c5815efb64b6d9acc52fc5fba139212/src/lib/libcrypto/des/des_key.c) and [des.c](https://github.com/libressl/openbsd/blob/bc9483725c5815efb64b6d9acc52fc5fba139212/src/lib/libcrypto/des/des.c): `DES_set_key` skips the parity and weak-key checks and returns 0, as it does while `DES_check_key` is 0 (that variable is not in the PS5 export inventory), a nonzero `enc` encrypts, and `DES_set_odd_parity` sets each byte's low bit for odd parity. Null pointers throw where LibreSSL faults - [X509_verify_cert_error_string](../../../core/libs/prx/libSceLibreSsl/Export.cpp) - no console call is verified. The 64-bit argument, low-32-bit lookup, messages and shared numeric fallback follow [LibreSSL 2.5.4](https://github.com/libressl/openbsd/blob/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto/x509/x509_txt.c), the version identified in [published firmware 13.40 measurements](https://github.com/boykopovar/AnyPS5/discussions/8981#discussioncomment-19111214). Console modifications and other firmware versions remain unverified. Unknown-code results share an overwritten static buffer and are not safe for concurrent use, as in that release; code 1 and codes above 54 use the numeric fallback. @@ -18,6 +18,7 @@ - [OCSP_response_status_str, OCSP_cert_status_str and OCSP_crl_reason_str](../../../core/libs/prx/libSceLibreSsl/src/Ocsp.cpp) (libSceLibreSsl) - the module's strings are not measured on the console. The tables follow LibreSSL [ocsp_prn.c](https://github.com/libressl/openbsd/blob/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto/ocsp/ocsp_prn.c), unchanged from revision 1.8 (2015) to 1.12 (2025), with codes from `ocsp.h`; the argument is a 64-bit `long` and every value not in a table returns `(UNKNOWN)` - [SSL_get_ex_new_index, SSL_CTX_get_ex_new_index, SSL_get_ex_data_X509_STORE_CTX_idx and CRYPTO_cleanup_all_ex_data](../../../core/libs/prx/libSceLibreSsl/src/ExData.cpp) (libSceLibreSsl) - unverified on a PS5. The stored `argl`, `argp` and callbacks are never called, because no export creates an `SSL` or `SSL_CTX` and `SSL_get_ex_data`/`SSL_set_ex_data` still throw. Index numbering follows LibreSSL 2.5.4's [ex_data.c](https://github.com/libressl/openbsd/blob/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto/ex_data.c#L310-L406), [ssl_lib.c](https://github.com/libressl/openbsd/blob/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libssl/ssl_lib.c#L2828-L2854) and [ssl_cert.c](https://github.com/libressl/openbsd/blob/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libssl/ssl_cert.c#L133-L160): per class from 0, the verify-callback index allocated once and kept across `CRYPTO_cleanup_all_ex_data`, which restarts every class at 0. - [EVP digest and HMAC functions](../../../core/libs/prx/libSceLibreSsl/src/Evp.cpp) (libSceLibreSsl) - follow LibreSSL 2.5.4's [digest.c](https://github.com/libressl/openbsd/blob/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto/evp/digest.c) and [hmac.c](https://github.com/libressl/openbsd/blob/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto/hmac/hmac.c). They and the `EVP_MD`, `EVP_MD_CTX` and `HMAC_CTX` layouts behave the same up to 3.4.1, so this holds for 2.5.4 (the firmware 13.40 measurements) and for 2.7 or later (its `SSL_get0_param` export). Nothing is checked on the console. `sign` and `verify` in the `EVP_MD` tables are null as `RSA_sign` and `RSA_verify` still throw, failures add nothing to the error queue, and a failed allocation leaves the context without a digest, where LibreSSL keeps it. An `ENGINE`, an `EVP_PKEY_CTX`, a null context, `EVP_DigestInit_ex` without a type on a context that has a digest, and an update or final without a digest throw +- [EC_GROUP_new_by_curve_name, EC_GROUP_free, EC_GROUP_get_degree, EC_KEY_new, EC_KEY_new_by_curve_name, EC_KEY_free, EC_KEY_set_group, EC_KEY_get0_group, ECDSA_size](../../../core/libs/prx/libSceLibreSsl/src/Ec.cpp) (libSceLibreSsl) - not checked on the console. They follow LibreSSL 2.5.4's [ec](https://github.com/libressl/openbsd/tree/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto/ec) and [ecs_lib.c](https://github.com/libressl/openbsd/blob/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto/ecdsa/ecs_lib.c) over mbedtls, which has 11 of LibreSSL's 90 curves (NIDs 409, 415, 711 to 716, 928, 932, 934). The other 79 throw; other NIDs return null without queuing `EC_R_UNKNOWN_GROUP`. A key holds only its group: no reference count, keys, conversion form or flags. Setting a key's own group keeps it, where LibreSSL frees it first. Null groups and keys throw where LibreSSL dereferences them - [SSL_CIPHER_get_bits, SSL_CIPHER_get_name and SSL_CIPHER_get_version](../../../core/libs/prx/libSceLibreSsl/src/Cipher.cpp) - the 88-byte guest cipher layout and these calls are not console-verified. They follow [LibreSSL 2.5.4 ssl.h](https://github.com/libressl/openbsd/blob/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libssl/ssl.h) and [ssl_ciph.c](https://github.com/libressl/openbsd/blob/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libssl/ssl_ciph.c), matching the version in [published firmware 13.40 measurements](https://github.com/boykopovar/AnyPS5/discussions/8981#discussioncomment-19111214). Console modifications and other firmware layouts remain unverified. Protocol labels use the full 64-bit cipher ID, not the protocol flags; TLS 1.2 ciphers still report `TLSv1/SSLv3`, as in that release. - [sk_num, sk_value, sk_pop, sk_pop_free](../../../core/libs/prx/libSceLibreSsl/src/Stack.cpp) (libSceLibreSsl) - the 32-byte `_STACK` layout (`num`, `data`, `sorted`, `num_alloc`, `comp`) and the results follow [LibreSSL 2.5.4](https://github.com/libressl/openbsd/tree/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto/stack) `stack.h` and `stack.c`, the version identified in [published firmware 13.40 measurements](https://github.com/boykopovar/AnyPS5/discussions/8981#discussioncomment-19111214); nothing is checked on the console. `sk_pop_free` releases the element array and the stack with the guest `free`, as the module would through libc. No export of this module creates a stack yet, so only stacks the caller builds reach them. A null element array that LibreSSL would read through, and a null free function with a non-null element, throw where LibreSSL faults