Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 69 additions & 30 deletions core/libs/prx/libSceSsl/Export.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,33 @@ 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<int, SslConnection> 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;
}


}

extern "C" {
Expand Down Expand Up @@ -97,6 +119,53 @@ 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) {
(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;
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;
NotImplemented_nid_no_patch(__func__);
return 0;
}

int APS5_VABI sceSslClose() {
NotImplemented_nid_no_patch(__func__);
return 0;
Expand Down Expand Up @@ -179,39 +248,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;
}

}
33 changes: 30 additions & 3 deletions core/libs/tests/GuestSsl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@ 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);
}

struct SslMemoryPoolStats {
Expand All @@ -36,6 +41,8 @@ static bool Throws(TCall call) {
return false;
}

static int APS5_VABI VerifyCallback(int, unsigned int, void**, int, void*) { return 0; }

struct SslCaCerts {
void* certs;
std::size_t num;
Expand All @@ -51,8 +58,6 @@ int main() {
constexpr int notFound = static_cast<int>(0x8095F004);
constexpr int invalidArg = static_cast<int>(0x8095177A);
int marker = 0;
Require(Throws([] { sceSslSetVerifyCallback(); }));

const int context = sceSslInit_nid_postfix(0x10000);
Require(context > 0);
SslMemoryPoolStats stats{1, 1, 1, 1};
Expand Down Expand Up @@ -90,7 +95,29 @@ int main() {
Require(sceSslGetCaList(context, &list) == notFound);
Require(list.certs == nullptr && list.num == 0);

constexpr int invalidId = static_cast<int>(0x8095F006);
Require(Throws([&] { sceSslCreateConnection(context, 0, nullptr); }));
Require(sceSslCreateConnection(context + 1000, 0, "example.com") == invalidId);

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(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(Throws([&] { sceSslDeleteConnection(unknown); }));

Require(sceSslTerm_nid_postfix(context) == 0);
Require(Throws([&] { sceSslSetVerifyCallback(connection, nullptr, nullptr); }));
Require(sceSslCreateConnection(context, 0, "example.com") == invalidId);
Require(sceSslDeleteConnection(connection) == 0);
Require(Throws([&] { sceSslDeleteConnection(connection); }));

Require(sceSslGetCaList(context, nullptr) == invalidArg);
Require(sceSslFreeCaList(context, nullptr) == invalidArg);
list = {&handle, 3};
Expand Down
3 changes: 2 additions & 1 deletion docs/dev/debt/libSceSsl.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@

### 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 `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
- PPSA01317 imports declared without parameters, signatures unknown: [sceSslDisableVerifyOption, sceSslGetAlpnSelected, sceSslSetAlpn, sceSslSetMinSslVersion](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl). They act on a connection, which `sceSslCreateConnection` does not create yet, so they throw until connections exist

### 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)
Loading