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
10 changes: 10 additions & 0 deletions core/libs/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,16 @@ if(BUILD_TESTING)
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceNetCtlApDialog>")
endif()

add_test_executable(guest_np_auth_tests tests/GuestNpAuth.cpp)
target_include_directories(guest_np_auth_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_np_auth_tests PRIVATE libSceNpAuth libc)
configure_windows_unwind(guest_np_auth_tests)
add_test(NAME guest_np_auth COMMAND guest_np_auth_tests)
if(WIN32)
set_tests_properties(guest_np_auth PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceNpAuth>")
endif()

add_test_executable(guest_rudp_status_tests tests/GuestRudpStatus.cpp)
target_include_directories(guest_rudp_status_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_rudp_status_tests PRIVATE libSceRudp libc)
Expand Down
100 changes: 78 additions & 22 deletions core/libs/prx/libSceNpAuth/Export.cpp
Original file line number Diff line number Diff line change
@@ -1,34 +1,62 @@
#include <cstdint>
#include <cstddef>
#include <atomic>
#include <cstddef>
#include <cstdint>
#include <mutex>
#include <unordered_map>
#include "SceTypes.hpp"
#include "prx/libc/include/General.hpp"

static constexpr int SCE_NP_ERROR_INVALID_ARGUMENT = static_cast<int>(0x80550003);
static constexpr int ORBIS_NP_AUTH_ERROR_INVALID_ARGUMENT = static_cast<int>(0x80550301);
static constexpr int SCE_NP_ERROR_SIGNED_OUT = static_cast<int>(0x80550006);
static constexpr int ORBIS_NP_AUTH_ERROR_ABORTED = static_cast<int>(0x80550304);
static constexpr int ORBIS_NP_AUTH_ERROR_REQUEST_NOT_FOUND = static_cast<int>(0x80550306);
static constexpr int ORBIS_NP_AUTH_ERROR_INVALID_ID = static_cast<int>(0x80550307);
static constexpr int NP_POLL_ASYNC_FINISHED = 0;

static std::atomic<int> g_nextRequest{1};
namespace {

struct RequestInfo {
bool isAsync = false;
bool processed = false;
bool aborted = false;
int result = SCE_NP_ERROR_SIGNED_OUT;
};

std::mutex g_requestMutex;
std::unordered_map<int, RequestInfo> g_requests;
std::atomic<int> g_nextRequest{1};

int CreateRequest(bool isAsync) {
const int reqId = g_nextRequest.fetch_add(1, std::memory_order_relaxed);
std::lock_guard lock(g_requestMutex);
g_requests[reqId] = {isAsync, false, false, SCE_NP_ERROR_SIGNED_OUT};
return reqId;
}

}

extern "C" {

int APS5_VABI sceNpAuthAbortRequest(int req_id) {
(void)req_id;
std::lock_guard lock(g_requestMutex);
auto it = g_requests.find(req_id);
if (it == g_requests.end()) return ORBIS_NP_AUTH_ERROR_REQUEST_NOT_FOUND;
if (!it->second.processed) it->second.aborted = true;
return 0;
}

int APS5_VABI sceNpAuthCreateAsyncRequest(const void* param) {
(void)param;
return g_nextRequest.fetch_add(1, std::memory_order_relaxed);
return CreateRequest(true);
}

int APS5_VABI sceNpAuthCreateRequest(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
return CreateRequest(false);
}

int APS5_VABI sceNpAuthDeleteRequest(int req_id) {
(void)req_id;
std::lock_guard lock(g_requestMutex);
g_requests.erase(req_id);
return 0;
}

Expand All @@ -38,31 +66,59 @@ int APS5_VABI sceNpAuthGetAuthorizedAppCode() {
}

int APS5_VABI sceNpAuthGetAuthorizationCodeV3(int req_id, const void* param, void* auth_code, int* issuer_id) {
(void)req_id;
(void)issuer_id;
if (!param || !auth_code) return SCE_NP_ERROR_INVALID_ARGUMENT;
if (!param || !auth_code) return ORBIS_NP_AUTH_ERROR_INVALID_ARGUMENT;
std::lock_guard lock(g_requestMutex);
auto it = g_requests.find(req_id);
if (it == g_requests.end()) return ORBIS_NP_AUTH_ERROR_REQUEST_NOT_FOUND;
if (it->second.processed) return ORBIS_NP_AUTH_ERROR_INVALID_ARGUMENT;
if (it->second.aborted) {
it->second.result = ORBIS_NP_AUTH_ERROR_ABORTED;
return ORBIS_NP_AUTH_ERROR_ABORTED;
}
it->second.processed = true;
if (it->second.isAsync) {
it->second.result = SCE_NP_ERROR_SIGNED_OUT;
return 0;
}
return SCE_NP_ERROR_SIGNED_OUT;
}

int APS5_VABI sceNpAuthGetIdTokenV3(int req_id, const void* param, void* id_token) {
(void)req_id;
(void)param;
(void)id_token;
NotImplemented_nid_no_patch(__func__);
return 0;
if (!param || !id_token) return ORBIS_NP_AUTH_ERROR_INVALID_ARGUMENT;
std::lock_guard lock(g_requestMutex);
auto it = g_requests.find(req_id);
if (it == g_requests.end()) return ORBIS_NP_AUTH_ERROR_REQUEST_NOT_FOUND;
if (it->second.processed) return ORBIS_NP_AUTH_ERROR_INVALID_ARGUMENT;
if (it->second.aborted) {
it->second.result = ORBIS_NP_AUTH_ERROR_ABORTED;
return ORBIS_NP_AUTH_ERROR_ABORTED;
}
it->second.processed = true;
if (it->second.isAsync) {
it->second.result = SCE_NP_ERROR_SIGNED_OUT;
return 0;
}
return SCE_NP_ERROR_SIGNED_OUT;
}

int APS5_VABI sceNpAuthPollAsync(int req_id, int* result) {
(void)req_id;
if (result) *result = SCE_NP_ERROR_SIGNED_OUT;
std::lock_guard lock(g_requestMutex);
auto it = g_requests.find(req_id);
if (it == g_requests.end()) return ORBIS_NP_AUTH_ERROR_REQUEST_NOT_FOUND;
if (!it->second.isAsync || (!it->second.processed && !it->second.aborted)) return ORBIS_NP_AUTH_ERROR_INVALID_ID;
if (result) *result = it->second.result;
return NP_POLL_ASYNC_FINISHED;
}

int APS5_VABI sceNpAuthWaitAsync(int req_id, int* result) {
(void)req_id;
(void)result;
NotImplemented_nid_no_patch(__func__);
return 0;
if (!result) return ORBIS_NP_AUTH_ERROR_INVALID_ARGUMENT;
std::lock_guard lock(g_requestMutex);
auto it = g_requests.find(req_id);
if (it == g_requests.end()) return ORBIS_NP_AUTH_ERROR_REQUEST_NOT_FOUND;
if (!it->second.isAsync || (!it->second.processed && !it->second.aborted)) return ORBIS_NP_AUTH_ERROR_INVALID_ID;
*result = it->second.result;
return NP_POLL_ASYNC_FINISHED;
}

}
104 changes: 104 additions & 0 deletions core/libs/tests/GuestNpAuth.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
#include "SceTypes.hpp"

#include <cstdint>
#include <cstdlib>

extern "C" {
int APS5_VABI sceNpAuthAbortRequest(int req_id);
int APS5_VABI sceNpAuthCreateAsyncRequest(const void* param);
int APS5_VABI sceNpAuthCreateRequest(void);
int APS5_VABI sceNpAuthDeleteRequest(int req_id);
int APS5_VABI sceNpAuthGetAuthorizationCodeV3(int req_id, const void* param, void* auth_code, int* issuer_id);
int APS5_VABI sceNpAuthGetIdTokenV3(int req_id, const void* param, void* id_token);
int APS5_VABI sceNpAuthPollAsync(int req_id, int* result);
int APS5_VABI sceNpAuthWaitAsync(int req_id, int* result);
}

static void Require(bool condition) {
if (!condition) {
std::abort();
}
}

int main() {
constexpr int invalidArgument = static_cast<int>(0x80550301);
constexpr int signedOut = static_cast<int>(0x80550006);
constexpr int aborted = static_cast<int>(0x80550304);
constexpr int requestNotFound = static_cast<int>(0x80550306);
constexpr int invalidId = static_cast<int>(0x80550307);

const int syncReq = sceNpAuthCreateRequest();
const int syncReq2 = sceNpAuthCreateRequest();
Require(syncReq > 0);
Require(syncReq2 > syncReq);

const int asyncReq = sceNpAuthCreateAsyncRequest(nullptr);
const int asyncReq2 = sceNpAuthCreateAsyncRequest(nullptr);
Require(asyncReq > syncReq2);
Require(asyncReq2 > asyncReq);

alignas(8) char param[16] = {};
alignas(8) char token[16] = {};
alignas(8) char authCode[16] = {};
int issuerId = 0;

int pollUnprocessed = 0;
Require(sceNpAuthPollAsync(asyncReq, &pollUnprocessed) == invalidId);
int waitUnprocessed = 0;
Require(sceNpAuthWaitAsync(asyncReq, &waitUnprocessed) == invalidId);

Require(sceNpAuthGetIdTokenV3(syncReq, nullptr, token) == invalidArgument);
Require(sceNpAuthGetIdTokenV3(syncReq, param, nullptr) == invalidArgument);
Require(sceNpAuthGetIdTokenV3(syncReq, nullptr, nullptr) == invalidArgument);
Require(sceNpAuthGetIdTokenV3(syncReq, param, token) == signedOut);
Require(sceNpAuthGetIdTokenV3(syncReq, param, token) == invalidArgument);
Require(sceNpAuthGetAuthorizationCodeV3(syncReq, nullptr, authCode, &issuerId) == invalidArgument);
Require(sceNpAuthGetAuthorizationCodeV3(syncReq, param, nullptr, &issuerId) == invalidArgument);
Require(sceNpAuthGetAuthorizationCodeV3(syncReq2, param, authCode, &issuerId) == signedOut);
Require(sceNpAuthGetAuthorizationCodeV3(syncReq2, param, authCode, &issuerId) == invalidArgument);

Require(sceNpAuthGetIdTokenV3(asyncReq, param, token) == 0);
Require(sceNpAuthGetIdTokenV3(asyncReq, param, token) == invalidArgument);
int asyncPoll = 0;
Require(sceNpAuthPollAsync(asyncReq, &asyncPoll) == 0);
Require(asyncPoll == signedOut);
int asyncWait = 0;
Require(sceNpAuthWaitAsync(asyncReq, &asyncWait) == 0);
Require(asyncWait == signedOut);

Require(sceNpAuthGetAuthorizationCodeV3(asyncReq2, param, authCode, &issuerId) == 0);
Require(sceNpAuthGetAuthorizationCodeV3(asyncReq2, param, authCode, &issuerId) == invalidArgument);
int asyncWait2 = 0;
Require(sceNpAuthWaitAsync(asyncReq2, &asyncWait2) == 0);
Require(asyncWait2 == signedOut);

constexpr int unknownReq = 999999;
Require(sceNpAuthGetIdTokenV3(unknownReq, param, token) == requestNotFound);
Require(sceNpAuthGetAuthorizationCodeV3(unknownReq, param, authCode, &issuerId) == requestNotFound);
int pollResult = 0;
Require(sceNpAuthPollAsync(unknownReq, &pollResult) == requestNotFound);
int waitResult = 0;
Require(sceNpAuthWaitAsync(unknownReq, &waitResult) == requestNotFound);

Require(sceNpAuthWaitAsync(syncReq, nullptr) == invalidArgument);
Require(sceNpAuthWaitAsync(syncReq, &waitResult) == invalidId);
Require(sceNpAuthPollAsync(syncReq, &pollResult) == invalidId);

Require(sceNpAuthAbortRequest(asyncReq) == 0);
Require(sceNpAuthGetIdTokenV3(asyncReq, param, token) == invalidArgument);
Require(sceNpAuthAbortRequest(unknownReq) == requestNotFound);

const int abortedReq = sceNpAuthCreateAsyncRequest(nullptr);
Require(sceNpAuthAbortRequest(abortedReq) == 0);
Require(sceNpAuthGetIdTokenV3(abortedReq, param, token) == aborted);
Require(sceNpAuthGetAuthorizationCodeV3(abortedReq, param, authCode, &issuerId) == aborted);
int abortedWait = 0;
Require(sceNpAuthWaitAsync(abortedReq, &abortedWait) == 0);
Require(abortedWait == aborted);

Require(sceNpAuthDeleteRequest(syncReq) == 0);
Require(sceNpAuthGetIdTokenV3(syncReq, param, token) == requestNotFound);
Require(sceNpAuthWaitAsync(syncReq, &waitResult) == requestNotFound);

return 0;
}
1 change: 1 addition & 0 deletions docs/dev/debt/libSceNpAuth.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
### Unknown function info

- [sceNpAuthGetAuthorizedAppCode](../../../core/libs/prx/libSceNpAuth/Export.cpp) (libSceNpAuth) - declared without parameters as a throwing stub; Minecraft (PPSA17221) imports it and fails to load without it. The request parameter and code output are not known, and there is no PSN sign-in for it to succeed against.
- [sceNpAuthAbortRequest, sceNpAuthGetAuthorizationCodeV3, sceNpAuthGetIdTokenV3, sceNpAuthPollAsync, sceNpAuthWaitAsync](../../../core/libs/prx/libSceNpAuth/Export.cpp) (libSceNpAuth) - request tracking, `ORBIS_NP_AUTH_ERROR_REQUEST_NOT_FOUND` (0x80550306) on unknown request IDs, `ORBIS_NP_AUTH_ERROR_INVALID_ID` (0x80550307) on non-async or unprocessed requests in Poll/Wait, `ORBIS_NP_AUTH_ERROR_INVALID_ARGUMENT` (0x80550301) on null arguments and repeated calls on completed requests, and `ORBIS_NP_AUTH_ERROR_ABORTED` (0x80550304) after an abort follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/np/np_auth.cpp) (PS4); async requests return OK and defer `SCE_NP_ERROR_SIGNED_OUT` to `sceNpAuthPollAsync`/`sceNpAuthWaitAsync` while sync requests fail immediately; PlayStation Network is not contacted. The parameter `size` fields are not checked (`ORBIS_NP_AUTH_ERROR_INVALID_SIZE`, structure layouts unknown), the request table has no limit (`ORBIS_NP_AUTH_ERROR_REQUEST_MAX`), and console behavior on PS5 is unverified
Loading