From d569439e1f0b3784ce5d3a4117e6009396f87525 Mon Sep 17 00:00:00 2001 From: screamm Date: Tue, 13 Oct 2026 20:42:14 +0530 Subject: [PATCH 1/3] feat(libSceSsl): add the connection calls Implements connection lifecycle and parameter exports in libSceSsl: sceSslCreateConnection, sceSslConnect, sceSslRead, sceSslWrite, sceSslDeleteConnection, and sceSslSetVerifyCallback. The repository has no TLS client, so a connection records its context, socket, host and verification callback: - sceSslCreateConnection returns a new connection id, or SSL_ERROR_INVALID_ID (0x8095F006) for an unknown context. A null host throws. - sceSslDeleteConnection drops the connection and leaves socket open. - sceSslSetVerifyCallback stores the callback and argument. - sceSslConnect, sceSslRead and sceSslWrite throw where handshake starts, naming the host and the socket peer port. Carried from @screamm with author attribution preserved. Co-Authored-By: Claude Fable 5.1 --- core/libs/prx/libSceSsl/CMakeLists.txt | 3 +- core/libs/prx/libSceSsl/Export.cpp | 82 ++++++++++++++++++++++++++ core/libs/tests/GuestSsl.cpp | 75 ++++++++++++++++++++++- docs/dev/debt/libSceSsl.md | 3 +- 4 files changed, 160 insertions(+), 3 deletions(-) diff --git a/core/libs/prx/libSceSsl/CMakeLists.txt b/core/libs/prx/libSceSsl/CMakeLists.txt index d66b9716ecb..a86833ac890 100644 --- a/core/libs/prx/libSceSsl/CMakeLists.txt +++ b/core/libs/prx/libSceSsl/CMakeLists.txt @@ -1,11 +1,12 @@ add_library(libSceSsl SHARED EXCLUDE_FROM_ALL Export.cpp) target_include_directories(libSceSsl PRIVATE ${LIBS_INCLUDE_DIR}) +target_link_libraries(libSceSsl PRIVATE libSceNet) configure_windows_unwind(libSceSsl) if(BUILD_TESTING) add_test_executable(guest_ssl_tests ${LIBS_INCLUDE_DIR}/tests/GuestSsl.cpp) target_include_directories(guest_ssl_tests PRIVATE ${LIBS_INCLUDE_DIR}) - target_link_libraries(guest_ssl_tests PRIVATE libSceSsl libc) + target_link_libraries(guest_ssl_tests PRIVATE libSceSsl libSceNet libc) configure_windows_unwind(guest_ssl_tests) add_test(NAME guest_ssl COMMAND guest_ssl_tests) if(WIN32) diff --git a/core/libs/prx/libSceSsl/Export.cpp b/core/libs/prx/libSceSsl/Export.cpp index fb2265f22d7..802a543559f 100644 --- a/core/libs/prx/libSceSsl/Export.cpp +++ b/core/libs/prx/libSceSsl/Export.cpp @@ -16,6 +16,8 @@ static std::atomic g_nextHandle{1}; static std::mutex g_poolsMutex; static std::map g_pools; +extern "C" int APS5_VABI sceNetGetpeername(int s, void* addr, uint32_t* addrlen); + namespace { constexpr int ERROR_NOT_FOUND = static_cast(0x8095F004); @@ -47,11 +49,50 @@ struct SslCaList { }; static_assert(sizeof(SslCaList) == 16); +using SslVerifyCallback = int (APS5_VABI *)(int, unsigned int, void**, int, void*); + +struct SslConnection { + int context; + int socket; + std::string host; + SslVerifyCallback verifyCallback; + void* verifyArgument; +}; + +std::map g_connections; + void RequireContext(const char* function, int sslCtxId) { std::lock_guard lock(g_poolsMutex); if (!g_pools.contains(sslCtxId)) throw std::invalid_argument(std::string(function) + ": unknown context"); } +SslConnection& RequireConnection(const char* function, int sslConnectionId) { + const auto connection = g_connections.find(sslConnectionId); + if (connection == g_connections.end()) throw std::invalid_argument(std::string(function) + ": unknown connection"); + if (!g_pools.contains(connection->second.context)) { + throw std::invalid_argument(std::string(function) + ": the connection's context was terminated"); + } + return connection->second; +} + +std::string Endpoint(const SslConnection& connection) { + uint8_t address[28]{}; + uint32_t length = sizeof(address); + if (sceNetGetpeername(connection.socket, address, &length) != 0 || length < 4) { + return connection.host + " (socket " + std::to_string(connection.socket) + " has no peer)"; + } + return connection.host + ":" + std::to_string((address[2] << 8) | address[3]); +} + +[[noreturn]] void ThrowNoTransport(const char* function, int sslConnectionId, const char* operation) { + SslConnection connection; + { + std::lock_guard lock(g_poolsMutex); + connection = RequireConnection(function, sslConnectionId); + } + throw std::runtime_error(std::string(function) + ": no TLS transport for " + operation + " with " + Endpoint(connection)); +} + } extern "C" { @@ -97,6 +138,47 @@ int APS5_VABI sceSslTerm_nid_postfix(int ssl_ctx_id) { return 0; } +int APS5_VABI sceSslCreateConnection(int sslCtxId, int sockId, const char* host) { + if (!host) throw std::invalid_argument("sceSslCreateConnection: null host"); + std::lock_guard lock(g_poolsMutex); + if (!g_pools.contains(sslCtxId)) return ERROR_INVALID_ID; + const int id = g_nextHandle.fetch_add(1, std::memory_order_relaxed); + g_connections[id] = {sslCtxId, sockId, host, nullptr, nullptr}; + return id; +} + +int APS5_VABI sceSslDeleteConnection(int sslConnectionId) { + std::lock_guard lock(g_poolsMutex); + if (!g_connections.erase(sslConnectionId)) throw std::invalid_argument("sceSslDeleteConnection: unknown connection"); + return 0; +} + +int APS5_VABI sceSslSetVerifyCallback(int sslConnectionId, SslVerifyCallback cbfunc, void* userArg) { + std::lock_guard lock(g_poolsMutex); + SslConnection& connection = RequireConnection(__func__, sslConnectionId); + connection.verifyCallback = cbfunc; + connection.verifyArgument = userArg; + return 0; +} + +int APS5_VABI sceSslConnect(int sslConnectionId) { + ThrowNoTransport(__func__, sslConnectionId, "the handshake"); +} + +int APS5_VABI sceSslRead(int sslConnectionId, void* buf, size_t len, int flags) { + (void)buf; + (void)len; + (void)flags; + ThrowNoTransport(__func__, sslConnectionId, "reading"); +} + +int APS5_VABI sceSslWrite(int sslConnectionId, void* buf, size_t len, int flags) { + (void)buf; + (void)len; + (void)flags; + ThrowNoTransport(__func__, sslConnectionId, "writing"); +} + int APS5_VABI sceSslClose() { NotImplemented_nid_no_patch(__func__); return 0; diff --git a/core/libs/tests/GuestSsl.cpp b/core/libs/tests/GuestSsl.cpp index 4d89668ab62..d22355748e5 100644 --- a/core/libs/tests/GuestSsl.cpp +++ b/core/libs/tests/GuestSsl.cpp @@ -1,8 +1,10 @@ #include "prx/libc/include/general/VabiMacros.hpp" #include +#include #include #include #include +#include extern "C" { int APS5_VABI sceSslInit_nid_postfix(std::size_t); @@ -12,7 +14,18 @@ int APS5_VABI sceSslFreeCaCerts(int, void*); int APS5_VABI sceSslGetCaList(int, void*); int APS5_VABI sceSslFreeCaList(int, void*); int APS5_VABI sceSslUnloadCert(int); -int APS5_VABI sceSslSetVerifyCallback(void); +int APS5_VABI sceSslCreateConnection(int, int, const char*); +int APS5_VABI sceSslDeleteConnection(int); +int APS5_VABI sceSslSetVerifyCallback(int, int (APS5_VABI *)(int, unsigned int, void**, int, void*), void*); +int APS5_VABI sceSslConnect(int); +int APS5_VABI sceSslRead(int, void*, std::size_t, int); +int APS5_VABI sceSslWrite(int, void*, std::size_t, int); +int APS5_VABI sceNetSocket(const char*, int, int, int); +int APS5_VABI sceNetBind_nid_postfix(int, const void*, std::uint32_t); +int APS5_VABI sceNetListen(int, int); +int APS5_VABI sceNetGetsockname(int, void*, std::uint32_t*); +int APS5_VABI sceNetConnect(int, const void*, std::uint32_t); +int APS5_VABI sceNetSocketClose(int); } struct SslMemoryPoolStats { @@ -36,6 +49,18 @@ static bool Throws(TCall call) { return false; } +template +static bool ThrowsWith(TCall call, const std::string& text) { + try { + call(); + } catch (const std::exception& error) { + return std::string(error.what()).find(text) != std::string::npos; + } + return false; +} + +static int APS5_VABI VerifyCallback(int, unsigned int, void**, int, void*) { return 0; } + struct SslCaCerts { void* certs; std::size_t num; @@ -90,7 +115,55 @@ int main() { Require(sceSslGetCaList(context, &list) == notFound); Require(list.certs == nullptr && list.num == 0); + constexpr int invalidId = static_cast(0x8095F006); + Require(Throws([&] { sceSslCreateConnection(context, 0, nullptr); })); + Require(sceSslCreateConnection(context + 1000, 0, "example.com") == invalidId); + + const int listener = sceNetSocket(nullptr, 2, 1, 6); + Require(listener >= 0); + std::array address{16, 2, 0, 0, 127, 0, 0, 1}; + Require(sceNetBind_nid_postfix(listener, address.data(), address.size()) == 0); + Require(sceNetListen(listener, 1) == 0); + std::uint32_t addressSize = address.size(); + Require(sceNetGetsockname(listener, address.data(), &addressSize) == 0); + const std::string endpoint = "example.com:" + std::to_string((address[2] << 8) | address[3]); + const int client = sceNetSocket(nullptr, 2, 1, 6); + Require(client >= 0 && sceNetConnect(client, address.data(), address.size()) == 0); + const int idle = sceNetSocket(nullptr, 2, 1, 6); + Require(idle >= 0); + + const int connection = sceSslCreateConnection(context, client, "example.com"); + Require(connection > 0 && connection != context); + Require(sceSslSetVerifyCallback(connection, VerifyCallback, &marker) == 0); + Require(sceSslSetVerifyCallback(connection, nullptr, nullptr) == 0); + char buffer[4]{}; + Require(ThrowsWith([&] { sceSslConnect(connection); }, endpoint)); + Require(ThrowsWith([&] { sceSslRead(connection, buffer, sizeof(buffer), 0); }, endpoint)); + Require(ThrowsWith([&] { sceSslWrite(connection, buffer, sizeof(buffer), 0); }, endpoint)); + + const int unconnected = sceSslCreateConnection(context, idle, "example.com"); + Require(unconnected > 0 && unconnected != connection); + Require(ThrowsWith([&] { sceSslConnect(unconnected); }, "example.com (socket")); + Require(sceSslDeleteConnection(unconnected) == 0); + + const int unknown = connection + 1000; + Require(Throws([&] { sceSslSetVerifyCallback(unknown, nullptr, nullptr); })); + Require(ThrowsWith([&] { sceSslConnect(unknown); }, "unknown connection")); + Require(ThrowsWith([&] { sceSslRead(unknown, buffer, sizeof(buffer), 0); }, "unknown connection")); + Require(ThrowsWith([&] { sceSslWrite(unknown, buffer, sizeof(buffer), 0); }, "unknown connection")); + Require(Throws([&] { sceSslDeleteConnection(unknown); })); + Require(Throws([&] { sceSslConnect(unconnected); })); + Require(sceSslTerm_nid_postfix(context) == 0); + Require(Throws([&] { sceSslSetVerifyCallback(connection, nullptr, nullptr); })); + Require(ThrowsWith([&] { sceSslConnect(connection); }, "terminated")); + Require(sceSslCreateConnection(context, client, "example.com") == invalidId); + Require(sceSslDeleteConnection(connection) == 0); + Require(Throws([&] { sceSslDeleteConnection(connection); })); + Require(sceNetSocketClose(idle) == 0); + Require(sceNetSocketClose(client) == 0); + Require(sceNetSocketClose(listener) == 0); + Require(sceSslGetCaList(context, nullptr) == invalidArg); Require(sceSslFreeCaList(context, nullptr) == invalidArg); list = {&handle, 3}; diff --git a/docs/dev/debt/libSceSsl.md b/docs/dev/debt/libSceSsl.md index 0a72ff2bfab..70045e2483b 100644 --- a/docs/dev/debt/libSceSsl.md +++ b/docs/dev/debt/libSceSsl.md @@ -2,6 +2,7 @@ ### Unknown function info +- [sceSslCreateConnection, sceSslDeleteConnection, sceSslSetVerifyCallback, sceSslConnect, sceSslRead, sceSslWrite](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - imported by `ffmpeg.prx`. The repository has no TLS client, so a connection only records its context, socket, host and verification callback, and `sceSslConnect`, `sceSslRead` and `sceSslWrite` throw with the host and the socket's peer port where the handshake would start. Signatures follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/60977b29103452b9681ae10c7a9011d44965bf16/src/SharpProspero/Interop/Net/Http.cs#L195-L252), which also says the socket stays open after `sceSslDeleteConnection`. An unknown context returns `SSL_ERROR_INVALID_ID` (0x8095F006), as `sceSslGetMemoryPoolStats` does on the console and as [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/4865dd92ca6b6bb907748f125883093af038f53b/src/libs/network.cpp#L2688-L2720) and [sharpemu](https://github.com/sharpemu/sharpemu/blob/29021b5a71bbc80f262d887e6ba18b3a72963f49/src/SharpEmu.Libs/Network/SslExports.cs#L41-L54) return for other context calls; for an unknown connection, a connection whose context was terminated and a null host the console's codes are unknown, so these throw. The socket is not checked when the connection is created, `sceSslTerm` leaves the context's connections to `sceSslDeleteConnection`, and `sceSslSetVerifyCallback` accepts any callback, null included, and returns 0; none of this is verified on the console. `SSL_ERROR_BEFORE_INIT` (0x8095F001) is never returned, as the library keeps no state apart from its contexts - [sceSslClose](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - unknown signature - [sceSslGetSerialNumber](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - unknown signature - [sceSslGetCaList, sceSslFreeCaList, sceSslUnloadCert](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - signatures and the 16-byte `SceSslCaList` layout (an array of certificate handles and a 32-bit count) follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Net/Http.cs) and shadPS4's `OrbisSslCaList`, not confirmed for the PS5. On the console the list holds the certificate authorities the SSL service has; [shadPS4](https://github.com/shadps4-emu/shadPS4/pull/4104) returns a placeholder certificate from `sceSslGetCaCerts` because Rise of the Tomb Raider fails its initialization checks without one. No root certificate bundle is shipped and no loaded certificate is kept, so here a context holds none: `sceSslGetCaList` clears the list and returns `NOT_FOUND`, as `sceSslGetCaCerts` does, `sceSslFreeCaList` accepts only that cleared list, and `sceSslUnloadCert` (imported by PPSA15508) has nothing to drop and is assumed to return 0. A null list returns `0x8095177A`, which `sceSslGetCaCerts` and [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/59183dbfa81ed7efdff6069177fed50de0552725/src/libs/network.cpp) return for a null argument (shadPS4, PS4, returns `0x8095F007`). An unknown context throws (KytyPS5 returns `SSL_ERROR_INVALID_ID`, `0x8095F006`, from `sceSslGetCaCerts`), and so does a list `sceSslGetCaList` did not return @@ -9,4 +10,4 @@ ### Functional -- PPSA07411 imports declared without parameters, signatures unknown, added without an implementation because the title fails to load without them: [sceSslCreateConnection, sceSslDeleteConnection, sceSslEnableVerifyOption, sceSslSetVerifyCallback, sceSslConnect, sceSslRead, sceSslWrite](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl), [four sce::Json::Value members (setElementAccessFailureHandler, setSpecialFloatHandler, setTypeMismatchHandler, operator bool)](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2), [sceConvertKeycodeGetCharacter](../../../core/libs/prx/libSceConvertKeycode/Export.cpp) (libSceConvertKeycode) and [sceNpSessionSignalingGetGroupFromSessionId](../../../core/libs/prx/libSceNpSessionSignaling/Export.cpp) (libSceNpSessionSignaling) +- PPSA07411 imports declared without parameters, signatures unknown, added without an implementation because the title fails to load without them: [sceSslEnableVerifyOption](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl), [four sce::Json::Value members (setElementAccessFailureHandler, setSpecialFloatHandler, setTypeMismatchHandler, operator bool)](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2), [sceConvertKeycodeGetCharacter](../../../core/libs/prx/libSceConvertKeycode/Export.cpp) (libSceConvertKeycode) and [sceNpSessionSignalingGetGroupFromSessionId](../../../core/libs/prx/libSceNpSessionSignaling/Export.cpp) (libSceNpSessionSignaling) From 793bd22f49bf9ec4e8428a38f470f8864b07d29f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Franch?= Date: Tue, 13 Oct 2026 17:15:33 +0200 Subject: [PATCH 2/3] fix(libSceSsl): drop the old parameterless connection stubs Co-Authored-By: Claude --- core/libs/prx/libSceSsl/Export.cpp | 30 ------------------------------ core/libs/tests/GuestSsl.cpp | 2 -- 2 files changed, 32 deletions(-) diff --git a/core/libs/prx/libSceSsl/Export.cpp b/core/libs/prx/libSceSsl/Export.cpp index 802a543559f..006534de76e 100644 --- a/core/libs/prx/libSceSsl/Export.cpp +++ b/core/libs/prx/libSceSsl/Export.cpp @@ -261,39 +261,9 @@ int APS5_VABI sceSslGetSubjectName(void) { return 0; } -int APS5_VABI sceSslCreateConnection(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceSslDeleteConnection(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - int APS5_VABI sceSslEnableVerifyOption(void) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSslSetVerifyCallback(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceSslConnect(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceSslRead(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceSslWrite(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - } diff --git a/core/libs/tests/GuestSsl.cpp b/core/libs/tests/GuestSsl.cpp index d22355748e5..bad0d237dbb 100644 --- a/core/libs/tests/GuestSsl.cpp +++ b/core/libs/tests/GuestSsl.cpp @@ -76,8 +76,6 @@ int main() { constexpr int notFound = static_cast(0x8095F004); constexpr int invalidArg = static_cast(0x8095177A); int marker = 0; - Require(Throws([] { sceSslSetVerifyCallback(); })); - const int context = sceSslInit_nid_postfix(0x10000); Require(context > 0); SslMemoryPoolStats stats{1, 1, 1, 1}; From 8e6889666a3bfafe647c6a36a6856669f5932e8c Mon Sep 17 00:00:00 2001 From: Nithishvelan M Date: Wed, 14 Oct 2026 12:25:11 +0530 Subject: [PATCH 3/3] fix(libSceSsl): keep connect, read and write as NotImplemented stubs --- core/libs/prx/libSceSsl/CMakeLists.txt | 3 +- core/libs/prx/libSceSsl/Export.cpp | 31 +++++---------- core/libs/tests/GuestSsl.cpp | 54 +++----------------------- docs/dev/debt/libSceSsl.md | 2 +- 4 files changed, 16 insertions(+), 74 deletions(-) diff --git a/core/libs/prx/libSceSsl/CMakeLists.txt b/core/libs/prx/libSceSsl/CMakeLists.txt index a86833ac890..d66b9716ecb 100644 --- a/core/libs/prx/libSceSsl/CMakeLists.txt +++ b/core/libs/prx/libSceSsl/CMakeLists.txt @@ -1,12 +1,11 @@ add_library(libSceSsl SHARED EXCLUDE_FROM_ALL Export.cpp) target_include_directories(libSceSsl PRIVATE ${LIBS_INCLUDE_DIR}) -target_link_libraries(libSceSsl PRIVATE libSceNet) configure_windows_unwind(libSceSsl) if(BUILD_TESTING) add_test_executable(guest_ssl_tests ${LIBS_INCLUDE_DIR}/tests/GuestSsl.cpp) target_include_directories(guest_ssl_tests PRIVATE ${LIBS_INCLUDE_DIR}) - target_link_libraries(guest_ssl_tests PRIVATE libSceSsl libSceNet libc) + target_link_libraries(guest_ssl_tests PRIVATE libSceSsl libc) configure_windows_unwind(guest_ssl_tests) add_test(NAME guest_ssl COMMAND guest_ssl_tests) if(WIN32) diff --git a/core/libs/prx/libSceSsl/Export.cpp b/core/libs/prx/libSceSsl/Export.cpp index 006534de76e..f0e94b445cd 100644 --- a/core/libs/prx/libSceSsl/Export.cpp +++ b/core/libs/prx/libSceSsl/Export.cpp @@ -16,8 +16,6 @@ static std::atomic g_nextHandle{1}; static std::mutex g_poolsMutex; static std::map g_pools; -extern "C" int APS5_VABI sceNetGetpeername(int s, void* addr, uint32_t* addrlen); - namespace { constexpr int ERROR_NOT_FOUND = static_cast(0x8095F004); @@ -75,23 +73,6 @@ SslConnection& RequireConnection(const char* function, int sslConnectionId) { return connection->second; } -std::string Endpoint(const SslConnection& connection) { - uint8_t address[28]{}; - uint32_t length = sizeof(address); - if (sceNetGetpeername(connection.socket, address, &length) != 0 || length < 4) { - return connection.host + " (socket " + std::to_string(connection.socket) + " has no peer)"; - } - return connection.host + ":" + std::to_string((address[2] << 8) | address[3]); -} - -[[noreturn]] void ThrowNoTransport(const char* function, int sslConnectionId, const char* operation) { - SslConnection connection; - { - std::lock_guard lock(g_poolsMutex); - connection = RequireConnection(function, sslConnectionId); - } - throw std::runtime_error(std::string(function) + ": no TLS transport for " + operation + " with " + Endpoint(connection)); -} } @@ -162,21 +143,27 @@ int APS5_VABI sceSslSetVerifyCallback(int sslConnectionId, SslVerifyCallback cbf } int APS5_VABI sceSslConnect(int sslConnectionId) { - ThrowNoTransport(__func__, sslConnectionId, "the handshake"); + (void)sslConnectionId; + NotImplemented_nid_no_patch(__func__); + return 0; } int APS5_VABI sceSslRead(int sslConnectionId, void* buf, size_t len, int flags) { + (void)sslConnectionId; (void)buf; (void)len; (void)flags; - ThrowNoTransport(__func__, sslConnectionId, "reading"); + NotImplemented_nid_no_patch(__func__); + return 0; } int APS5_VABI sceSslWrite(int sslConnectionId, void* buf, size_t len, int flags) { + (void)sslConnectionId; (void)buf; (void)len; (void)flags; - ThrowNoTransport(__func__, sslConnectionId, "writing"); + NotImplemented_nid_no_patch(__func__); + return 0; } int APS5_VABI sceSslClose() { diff --git a/core/libs/tests/GuestSsl.cpp b/core/libs/tests/GuestSsl.cpp index bad0d237dbb..1046e7ee55f 100644 --- a/core/libs/tests/GuestSsl.cpp +++ b/core/libs/tests/GuestSsl.cpp @@ -1,10 +1,8 @@ #include "prx/libc/include/general/VabiMacros.hpp" #include -#include #include #include #include -#include extern "C" { int APS5_VABI sceSslInit_nid_postfix(std::size_t); @@ -20,12 +18,6 @@ int APS5_VABI sceSslSetVerifyCallback(int, int (APS5_VABI *)(int, unsigned int, int APS5_VABI sceSslConnect(int); int APS5_VABI sceSslRead(int, void*, std::size_t, int); int APS5_VABI sceSslWrite(int, void*, std::size_t, int); -int APS5_VABI sceNetSocket(const char*, int, int, int); -int APS5_VABI sceNetBind_nid_postfix(int, const void*, std::uint32_t); -int APS5_VABI sceNetListen(int, int); -int APS5_VABI sceNetGetsockname(int, void*, std::uint32_t*); -int APS5_VABI sceNetConnect(int, const void*, std::uint32_t); -int APS5_VABI sceNetSocketClose(int); } struct SslMemoryPoolStats { @@ -49,16 +41,6 @@ static bool Throws(TCall call) { return false; } -template -static bool ThrowsWith(TCall call, const std::string& text) { - try { - call(); - } catch (const std::exception& error) { - return std::string(error.what()).find(text) != std::string::npos; - } - return false; -} - static int APS5_VABI VerifyCallback(int, unsigned int, void**, int, void*) { return 0; } struct SslCaCerts { @@ -117,50 +99,24 @@ int main() { Require(Throws([&] { sceSslCreateConnection(context, 0, nullptr); })); Require(sceSslCreateConnection(context + 1000, 0, "example.com") == invalidId); - const int listener = sceNetSocket(nullptr, 2, 1, 6); - Require(listener >= 0); - std::array address{16, 2, 0, 0, 127, 0, 0, 1}; - Require(sceNetBind_nid_postfix(listener, address.data(), address.size()) == 0); - Require(sceNetListen(listener, 1) == 0); - std::uint32_t addressSize = address.size(); - Require(sceNetGetsockname(listener, address.data(), &addressSize) == 0); - const std::string endpoint = "example.com:" + std::to_string((address[2] << 8) | address[3]); - const int client = sceNetSocket(nullptr, 2, 1, 6); - Require(client >= 0 && sceNetConnect(client, address.data(), address.size()) == 0); - const int idle = sceNetSocket(nullptr, 2, 1, 6); - Require(idle >= 0); - - const int connection = sceSslCreateConnection(context, client, "example.com"); + const int connection = sceSslCreateConnection(context, 0, "example.com"); Require(connection > 0 && connection != context); Require(sceSslSetVerifyCallback(connection, VerifyCallback, &marker) == 0); Require(sceSslSetVerifyCallback(connection, nullptr, nullptr) == 0); char buffer[4]{}; - Require(ThrowsWith([&] { sceSslConnect(connection); }, endpoint)); - Require(ThrowsWith([&] { sceSslRead(connection, buffer, sizeof(buffer), 0); }, endpoint)); - Require(ThrowsWith([&] { sceSslWrite(connection, buffer, sizeof(buffer), 0); }, endpoint)); - - const int unconnected = sceSslCreateConnection(context, idle, "example.com"); - Require(unconnected > 0 && unconnected != connection); - Require(ThrowsWith([&] { sceSslConnect(unconnected); }, "example.com (socket")); - Require(sceSslDeleteConnection(unconnected) == 0); + Require(Throws([&] { sceSslConnect(connection); })); + Require(Throws([&] { sceSslRead(connection, buffer, sizeof(buffer), 0); })); + Require(Throws([&] { sceSslWrite(connection, buffer, sizeof(buffer), 0); })); const int unknown = connection + 1000; Require(Throws([&] { sceSslSetVerifyCallback(unknown, nullptr, nullptr); })); - Require(ThrowsWith([&] { sceSslConnect(unknown); }, "unknown connection")); - Require(ThrowsWith([&] { sceSslRead(unknown, buffer, sizeof(buffer), 0); }, "unknown connection")); - Require(ThrowsWith([&] { sceSslWrite(unknown, buffer, sizeof(buffer), 0); }, "unknown connection")); Require(Throws([&] { sceSslDeleteConnection(unknown); })); - Require(Throws([&] { sceSslConnect(unconnected); })); Require(sceSslTerm_nid_postfix(context) == 0); Require(Throws([&] { sceSslSetVerifyCallback(connection, nullptr, nullptr); })); - Require(ThrowsWith([&] { sceSslConnect(connection); }, "terminated")); - Require(sceSslCreateConnection(context, client, "example.com") == invalidId); + Require(sceSslCreateConnection(context, 0, "example.com") == invalidId); Require(sceSslDeleteConnection(connection) == 0); Require(Throws([&] { sceSslDeleteConnection(connection); })); - Require(sceNetSocketClose(idle) == 0); - Require(sceNetSocketClose(client) == 0); - Require(sceNetSocketClose(listener) == 0); Require(sceSslGetCaList(context, nullptr) == invalidArg); Require(sceSslFreeCaList(context, nullptr) == invalidArg); diff --git a/docs/dev/debt/libSceSsl.md b/docs/dev/debt/libSceSsl.md index 70045e2483b..a789cb57295 100644 --- a/docs/dev/debt/libSceSsl.md +++ b/docs/dev/debt/libSceSsl.md @@ -2,7 +2,7 @@ ### Unknown function info -- [sceSslCreateConnection, sceSslDeleteConnection, sceSslSetVerifyCallback, sceSslConnect, sceSslRead, sceSslWrite](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - imported by `ffmpeg.prx`. The repository has no TLS client, so a connection only records its context, socket, host and verification callback, and `sceSslConnect`, `sceSslRead` and `sceSslWrite` throw with the host and the socket's peer port where the handshake would start. Signatures follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/60977b29103452b9681ae10c7a9011d44965bf16/src/SharpProspero/Interop/Net/Http.cs#L195-L252), which also says the socket stays open after `sceSslDeleteConnection`. An unknown context returns `SSL_ERROR_INVALID_ID` (0x8095F006), as `sceSslGetMemoryPoolStats` does on the console and as [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/4865dd92ca6b6bb907748f125883093af038f53b/src/libs/network.cpp#L2688-L2720) and [sharpemu](https://github.com/sharpemu/sharpemu/blob/29021b5a71bbc80f262d887e6ba18b3a72963f49/src/SharpEmu.Libs/Network/SslExports.cs#L41-L54) return for other context calls; for an unknown connection, a connection whose context was terminated and a null host the console's codes are unknown, so these throw. The socket is not checked when the connection is created, `sceSslTerm` leaves the context's connections to `sceSslDeleteConnection`, and `sceSslSetVerifyCallback` accepts any callback, null included, and returns 0; none of this is verified on the console. `SSL_ERROR_BEFORE_INIT` (0x8095F001) is never returned, as the library keeps no state apart from its contexts +- [sceSslCreateConnection, sceSslDeleteConnection, sceSslSetVerifyCallback, sceSslConnect, sceSslRead, sceSslWrite](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - imported by `ffmpeg.prx`. The repository has no TLS client, so a connection only records its context, socket, host and verification callback, and `sceSslConnect`, `sceSslRead` and `sceSslWrite` throw `NotImplemented_nid_no_patch(__func__)` until there is a transport. Signatures follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/60977b29103452b9681ae10c7a9011d44965bf16/src/SharpProspero/Interop/Net/Http.cs#L195-L252), which also says the socket stays open after `sceSslDeleteConnection`. An unknown context returns `SSL_ERROR_INVALID_ID` (0x8095F006), as `sceSslGetMemoryPoolStats` does on the console and as [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/4865dd92ca6b6bb907748f125883093af038f53b/src/libs/network.cpp#L2688-L2720) and [sharpemu](https://github.com/sharpemu/sharpemu/blob/29021b5a71bbc80f262d887e6ba18b3a72963f49/src/SharpEmu.Libs/Network/SslExports.cs#L41-L54) return for other context calls; for an unknown connection, a connection whose context was terminated and a null host the console's codes are unknown, so these throw. The socket is not checked when the connection is created, `sceSslTerm` leaves the context's connections to `sceSslDeleteConnection`, and `sceSslSetVerifyCallback` accepts any callback, null included, and returns 0; none of this is verified on the console. `SSL_ERROR_BEFORE_INIT` (0x8095F001) is never returned, as the library keeps no state apart from its contexts - [sceSslClose](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - unknown signature - [sceSslGetSerialNumber](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - unknown signature - [sceSslGetCaList, sceSslFreeCaList, sceSslUnloadCert](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - signatures and the 16-byte `SceSslCaList` layout (an array of certificate handles and a 32-bit count) follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Net/Http.cs) and shadPS4's `OrbisSslCaList`, not confirmed for the PS5. On the console the list holds the certificate authorities the SSL service has; [shadPS4](https://github.com/shadps4-emu/shadPS4/pull/4104) returns a placeholder certificate from `sceSslGetCaCerts` because Rise of the Tomb Raider fails its initialization checks without one. No root certificate bundle is shipped and no loaded certificate is kept, so here a context holds none: `sceSslGetCaList` clears the list and returns `NOT_FOUND`, as `sceSslGetCaCerts` does, `sceSslFreeCaList` accepts only that cleared list, and `sceSslUnloadCert` (imported by PPSA15508) has nothing to drop and is assumed to return 0. A null list returns `0x8095177A`, which `sceSslGetCaCerts` and [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/59183dbfa81ed7efdff6069177fed50de0552725/src/libs/network.cpp) return for a null argument (shadPS4, PS4, returns `0x8095F007`). An unknown context throws (KytyPS5 returns `SSL_ERROR_INVALID_ID`, `0x8095F006`, from `sceSslGetCaCerts`), and so does a list `sceSslGetCaList` did not return