From 4479d99a9a4fac96ed1d8f46a2ba6ce0884bd934 Mon Sep 17 00:00:00 2001 From: RipleyTom Date: Tue, 10 May 2022 17:25:45 +0200 Subject: [PATCH] Implement sceNpManagerGetTicketParam --- rpcs3/Emu/Cell/Modules/sceNp.cpp | 19 ++- rpcs3/Emu/Cell/Modules/sceNp.h | 66 ++++--- rpcs3/Emu/NP/np_handler.cpp | 285 +++++++++++++++++++++++++++++++ rpcs3/Emu/NP/np_handler.h | 46 ++++- rpcs3/Emu/NP/np_requests.cpp | 11 +- 5 files changed, 394 insertions(+), 33 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/sceNp.cpp b/rpcs3/Emu/Cell/Modules/sceNp.cpp index a7ea9e11c4..c3ab3c7438 100644 --- a/rpcs3/Emu/Cell/Modules/sceNp.cpp +++ b/rpcs3/Emu/Cell/Modules/sceNp.cpp @@ -3351,7 +3351,7 @@ error_code sceNpManagerGetTicket(vm::ptr buffer, vm::ptr bufferSize) error_code sceNpManagerGetTicketParam(s32 paramId, vm::ptr param) { - sceNp.todo("sceNpManagerGetTicketParam(paramId=%d, param=*0x%x)", paramId, param); + sceNp.notice("sceNpManagerGetTicketParam(paramId=%d, param=*0x%x)", paramId, param); auto& nph = g_fxo->get>(); @@ -3360,12 +3360,23 @@ error_code sceNpManagerGetTicketParam(s32 paramId, vm::ptr par return SCE_NP_ERROR_NOT_INITIALIZED; } - if (!param) + if (!param || paramId < SCE_NP_TICKET_PARAM_SERIAL_ID || paramId > SCE_NP_TICKET_PARAM_SUBJECT_DOB) { - // TODO: check paramId return SCE_NP_ERROR_INVALID_ARGUMENT; } + const auto& ticket = nph.get_ticket(); + + if (ticket.empty()) + { + return SCE_NP_ERROR_INVALID_STATE; + } + + if (!ticket.get_value(paramId, param)) + { + return SCE_NP_ERROR_INVALID_STATE; + } + return CELL_OK; } @@ -3380,7 +3391,7 @@ error_code sceNpManagerGetEntitlementIdList(vm::ptr entIdLis return SCE_NP_ERROR_NOT_INITIALIZED; } - return CELL_OK; + return not_an_error(0); } error_code sceNpManagerGetEntitlementById(vm::cptr entId, vm::ptr ent) diff --git a/rpcs3/Emu/Cell/Modules/sceNp.h b/rpcs3/Emu/Cell/Modules/sceNp.h index 01fda93ccc..a41830f3c1 100644 --- a/rpcs3/Emu/Cell/Modules/sceNp.h +++ b/rpcs3/Emu/Cell/Modules/sceNp.h @@ -243,26 +243,26 @@ enum SceNpError : u32 SCE_NP_COMMUNITY_SERVER_ERROR_UNSPECIFIED = 0x8002a4ff, // DRM - SCE_NP_DRM_ERROR_OUT_OF_MEMORY = 0x80029501, - SCE_NP_DRM_ERROR_INVALID_PARAM = 0x80029502, - SCE_NP_DRM_ERROR_SERVER_RESPONSE = 0x80029509, - SCE_NP_DRM_ERROR_NO_ENTITLEMENT = 0x80029513, - SCE_NP_DRM_ERROR_BAD_ACT = 0x80029514, - SCE_NP_DRM_ERROR_BAD_FORMAT = 0x80029515, - SCE_NP_DRM_ERROR_NO_LOGIN = 0x80029516, - SCE_NP_DRM_ERROR_INTERNAL = 0x80029517, - SCE_NP_DRM_ERROR_BAD_PERM = 0x80029519, - SCE_NP_DRM_ERROR_UNKNOWN_VERSION = 0x8002951a, - SCE_NP_DRM_ERROR_TIME_LIMIT = 0x8002951b, - SCE_NP_DRM_ERROR_DIFFERENT_ACCOUNT_ID = 0x8002951c, - SCE_NP_DRM_ERROR_DIFFERENT_DRM_TYPE = 0x8002951d, - SCE_NP_DRM_ERROR_SERVICE_NOT_STARTED = 0x8002951e, - SCE_NP_DRM_ERROR_BUSY = 0x80029520, - SCE_NP_DRM_ERROR_LICENSE_NOT_FOUND = 0x80029521, - SCE_NP_DRM_ERROR_IO = 0x80029525, - SCE_NP_DRM_ERROR_FORMAT = 0x80029530, - SCE_NP_DRM_ERROR_FILENAME = 0x80029533, - SCE_NP_DRM_ERROR_K_LICENSEE = 0x80029534, + SCE_NP_DRM_ERROR_OUT_OF_MEMORY = 0x80029501, + SCE_NP_DRM_ERROR_INVALID_PARAM = 0x80029502, + SCE_NP_DRM_ERROR_SERVER_RESPONSE = 0x80029509, + SCE_NP_DRM_ERROR_NO_ENTITLEMENT = 0x80029513, + SCE_NP_DRM_ERROR_BAD_ACT = 0x80029514, + SCE_NP_DRM_ERROR_BAD_FORMAT = 0x80029515, + SCE_NP_DRM_ERROR_NO_LOGIN = 0x80029516, + SCE_NP_DRM_ERROR_INTERNAL = 0x80029517, + SCE_NP_DRM_ERROR_BAD_PERM = 0x80029519, + SCE_NP_DRM_ERROR_UNKNOWN_VERSION = 0x8002951a, + SCE_NP_DRM_ERROR_TIME_LIMIT = 0x8002951b, + SCE_NP_DRM_ERROR_DIFFERENT_ACCOUNT_ID = 0x8002951c, + SCE_NP_DRM_ERROR_DIFFERENT_DRM_TYPE = 0x8002951d, + SCE_NP_DRM_ERROR_SERVICE_NOT_STARTED = 0x8002951e, + SCE_NP_DRM_ERROR_BUSY = 0x80029520, + SCE_NP_DRM_ERROR_LICENSE_NOT_FOUND = 0x80029521, + SCE_NP_DRM_ERROR_IO = 0x80029525, + SCE_NP_DRM_ERROR_FORMAT = 0x80029530, + SCE_NP_DRM_ERROR_FILENAME = 0x80029533, + SCE_NP_DRM_ERROR_K_LICENSEE = 0x80029534, // DRM Server SCE_NP_DRM_SERVER_ERROR_SERVICE_IS_END = 0x80029700, @@ -292,7 +292,7 @@ enum SceNpError : u32 SCE_NP_AUTH_EBUSY = 0x8002a00a, SCE_NP_AUTH_EABORT = 0x8002a00c, SCE_NP_AUTH_EEXIST = 0x8002a014, - SCE_NP_AUTH_EINVALID_ARGUMENT = 0x8002a015, + SCE_NP_AUTH_EINVALID_ARGUMENT = 0x8002a015, // Auth extended SCE_NP_AUTH_ERROR_SERVICE_END = 0x8002a200, @@ -882,6 +882,30 @@ enum SCE_NP_MATCHING_GUI_EVENT_GET_ROOM_LIST_LIMIT = 0x000b }; +enum +{ + SCE_NP_TICKET_PARAM_SERIAL_ID = 0, + SCE_NP_TICKET_PARAM_ISSUER_ID = 1, + SCE_NP_TICKET_PARAM_ISSUED_DATE = 2, + SCE_NP_TICKET_PARAM_EXPIRE_DATE = 3, + SCE_NP_TICKET_PARAM_SUBJECT_ACCOUNT_ID = 4, + SCE_NP_TICKET_PARAM_SUBJECT_ONLINE_ID = 5, + SCE_NP_TICKET_PARAM_SUBJECT_REGION = 6, + SCE_NP_TICKET_PARAM_SUBJECT_DOMAIN = 7, + SCE_NP_TICKET_PARAM_SERVICE_ID = 8, + SCE_NP_TICKET_PARAM_SUBJECT_STATUS = 9, + SCE_NP_TICKET_PARAM_STATUS_DURATION = 10, + SCE_NP_TICKET_PARAM_SUBJECT_DOB = 11, +}; + +enum +{ + SCE_NP_TICKET_SERIAL_ID_SIZE = 20, + SCE_NP_SUBJECT_REGION_SIZE = 4, + SCE_NP_SUBJECT_DOMAIN_SIZE = 4, + SCE_NP_SERVICE_ID_SIZE = 24, +}; + struct SceNpDrmKey { u8 keydata[16]; diff --git a/rpcs3/Emu/NP/np_handler.cpp b/rpcs3/Emu/NP/np_handler.cpp index a2ac4de4ac..d49f8b7c73 100644 --- a/rpcs3/Emu/NP/np_handler.cpp +++ b/rpcs3/Emu/NP/np_handler.cpp @@ -47,9 +47,294 @@ LOG_CHANNEL(sceNp); LOG_CHANNEL(rpcn_log, "rpcn"); LOG_CHANNEL(nph_log, "NPHandler"); +LOG_CHANNEL(ticket_log, "Ticket"); namespace np { + ticket::ticket(std::vector&& raw_data) + : raw_data(raw_data) + { + parse(); + } + + std::size_t ticket::size() const + { + return raw_data.size(); + } + + const u8* ticket::data() const + { + return raw_data.data(); + } + + bool ticket::empty() const + { + return raw_data.empty(); + } + + bool ticket::get_value(s32 param_id, vm::ptr param) const + { + if (!parse_success) + { + return false; + } + + switch (param_id) + { + case SCE_NP_TICKET_PARAM_SERIAL_ID: + { + const auto& node = nodes[0].data.data_nodes[0]; + if (node.len != SCE_NP_TICKET_SERIAL_ID_SIZE) + { + return false; + } + + memcpy(param->data, node.data.data_vec.data(), SCE_NP_TICKET_SERIAL_ID_SIZE); + break; + } + case SCE_NP_TICKET_PARAM_ISSUER_ID: + { + const auto& node = nodes[0].data.data_nodes[1]; + param->ui32 = node.data.data_u32; + break; + } + case SCE_NP_TICKET_PARAM_ISSUED_DATE: + { + const auto& node = nodes[0].data.data_nodes[2]; + param->ui64 = node.data.data_u64; + break; + } + case SCE_NP_TICKET_PARAM_EXPIRE_DATE: + { + const auto& node = nodes[0].data.data_nodes[3]; + param->ui64 = node.data.data_u64; + break; + } + case SCE_NP_TICKET_PARAM_SUBJECT_ACCOUNT_ID: + { + const auto& node = nodes[0].data.data_nodes[4]; + param->ui64 = node.data.data_u64; + break; + } + case SCE_NP_TICKET_PARAM_SUBJECT_ONLINE_ID: + { + const auto& node = nodes[0].data.data_nodes[5]; + if (node.len != 0x20) + { + return false; + } + + memcpy(param->data, node.data.data_vec.data(), 0x20); + break; + } + case SCE_NP_TICKET_PARAM_SUBJECT_REGION: + { + const auto& node = nodes[0].data.data_nodes[6]; + if (node.len != SCE_NP_SUBJECT_REGION_SIZE) + { + return false; + } + + memcpy(param->data, node.data.data_vec.data(), SCE_NP_SUBJECT_REGION_SIZE); + break; + } + case SCE_NP_TICKET_PARAM_SUBJECT_DOMAIN: + { + const auto& node = nodes[0].data.data_nodes[7]; + if (node.len != SCE_NP_SUBJECT_DOMAIN_SIZE) + { + return false; + } + + memcpy(param->data, node.data.data_vec.data(), SCE_NP_SUBJECT_DOMAIN_SIZE); + break; + } + case SCE_NP_TICKET_PARAM_SERVICE_ID: + { + const auto& node = nodes[0].data.data_nodes[8]; + if (node.len != SCE_NP_SERVICE_ID_SIZE) + { + return false; + } + + memcpy(param->data, node.data.data_vec.data(), SCE_NP_SERVICE_ID_SIZE); + break; + } + case SCE_NP_TICKET_PARAM_SUBJECT_STATUS: + { + const auto& node = nodes[0].data.data_nodes[9]; + param->ui32 = node.data.data_u32; + break; + } + case SCE_NP_TICKET_PARAM_STATUS_DURATION: + case SCE_NP_TICKET_PARAM_SUBJECT_DOB: + { + param->ui64 = 0; + break; + } + default: + sceNp.fatal("Invalid ticket param id requested!"); + return false; + } + + return true; + } + + std::optional ticket::parse_node(std::size_t index) const + { + if ((index + MIN_TICKET_DATA_SIZE) > size()) + { + ticket_log.error("node didn't meet minimum size requirements"); + return std::nullopt; + } + + ticket_data tdata{}; + const auto* ptr = data() + index; + tdata.id = *reinterpret_cast*>(ptr); + tdata.len = *reinterpret_cast*>(ptr + 2); + const auto* data_ptr = data() + 4; + + auto check_size = [&](std::size_t expected) -> bool + { + if ((index + MIN_TICKET_DATA_SIZE + expected) > size()) + { + return false; + } + return true; + }; + + switch (tdata.id) + { + case 0: + if (tdata.len != 0) + { + return std::nullopt; + } + break; + case 1: + if (tdata.len != 4 || !check_size(4)) + { + return std::nullopt; + } + tdata.data.data_u32 = *reinterpret_cast*>(data_ptr); + break; + case 2: + case 7: + if (tdata.len != 8 || !check_size(8)) + { + return std::nullopt; + } + tdata.data.data_u64 = *reinterpret_cast*>(data_ptr); + break; + case 4: + case 8: + if (!check_size(tdata.len)) + { + return std::nullopt; + } + tdata.data.data_vec = std::vector(tdata.len); + memcpy(tdata.data.data_vec.data(), data_ptr, tdata.len); + break; + default: + if ((tdata.id & 0x3000) == 0x3000) + { + if (!check_size(tdata.len)) + { + return std::nullopt; + } + + std::size_t sub_index = 0; + tdata.data.data_nodes = {}; + while (sub_index < tdata.len) + { + auto sub_node = parse_node(sub_index + index + 4); + if (!sub_node) + { + ticket_log.error("Failed to parse subnode at %d", sub_index + index + 4); + return std::nullopt; + } + sub_index += sub_node->len + MIN_TICKET_DATA_SIZE; + tdata.data.data_nodes.push_back(std::move(*sub_node)); + } + break; + } + return std::nullopt; + } + + return tdata; + } + + void ticket::parse() + { + nodes.clear(); + parse_success = false; + + if (size() < (sizeof(u32) * 2)) + { + return; + } + + version = *reinterpret_cast*>(data()); + if (version != 0x21010000) + { + ticket_log.error("Invalid version: 0x%08x", version); + return; + } + + u32 given_size = *reinterpret_cast*>(data() + 4); + if ((given_size + 8) != size()) + { + ticket_log.error("Size mismatch (gs: %d vs s: %d)", given_size, size()); + return; + } + + std::size_t index = 8; + while (index < size()) + { + auto node = parse_node(index); + if (!node) + { + ticket_log.error("Failed to parse node at index %d", index); + return; + } + + index += (node->len + MIN_TICKET_DATA_SIZE); + nodes.push_back(std::move(*node)); + } + + // Check that everything expected is there + if (nodes.size() != 2) + { + ticket_log.error("Expected 2 blobs, found %d", nodes.size()); + return; + } + + if (nodes[0].id != 0x3000 && nodes[1].id != 0x3002) + { + ticket_log.error("The 2 blobs ids are incorrect"); + return; + } + + if (nodes[0].data.data_nodes.size() < 12) + { + ticket_log.error("Expected at least 12 sub-nodes, found %d", nodes[0].data.data_nodes.size()); + return; + } + + const auto& subnodes = nodes[0].data.data_nodes; + + if (subnodes[0].id != 8 || subnodes[1].id != 1 || subnodes[2].id != 7 || subnodes[3].id != 7 || + subnodes[4].id != 2 || subnodes[5].id != 4 || subnodes[6].id != 8 || subnodes[7].id != 4 || + subnodes[8].id != 8 || subnodes[9].id != 1) + { + ticket_log.error("Mismatched node"); + return; + } + + parse_success = true; + return; + } + np_handler::np_handler() { g_fxo->need>(); diff --git a/rpcs3/Emu/NP/np_handler.h b/rpcs3/Emu/NP/np_handler.h index c8af8f3ba4..fd91ef22f2 100644 --- a/rpcs3/Emu/NP/np_handler.h +++ b/rpcs3/Emu/NP/np_handler.h @@ -17,6 +17,45 @@ namespace np { + struct ticket_data + { + u16 id{}, len{}; + + struct + { + u32 data_u32{}; + u64 data_u64{}; + std::vector data_vec; + std::vector data_nodes; + } data; + }; + + class ticket + { + public: + ticket() = default; + ticket(std::vector&& raw_data); + + std::size_t size() const; + const u8* data() const; + bool empty() const; + + bool get_value(s32 param_id, vm::ptr param) const; + + private: + std::optional parse_node(std::size_t index) const; + void parse(); + + private: + static constexpr std::size_t MIN_TICKET_DATA_SIZE = 4; + + std::vector raw_data; + + bool parse_success = false; + u32 version{}; + std::vector nodes; + }; + struct basic_event { s32 event = 0; @@ -121,10 +160,7 @@ namespace np // Misc stuff void req_ticket(u32 version, const SceNpId* npid, const char* service_id, const u8* cookie, u32 cookie_size, const char* entitlement_id, u32 consumed_count); - const std::vector& get_ticket() const - { - return current_ticket; - } + const ticket& get_ticket() const; u32 add_players_to_history(vm::cptr npids, u32 count); // For signaling @@ -207,7 +243,7 @@ namespace np bool is_connected = false; bool is_psn_active = false; - std::vector current_ticket; + ticket current_ticket; // IP & DNS info std::string hostname = "localhost"; diff --git a/rpcs3/Emu/NP/np_requests.cpp b/rpcs3/Emu/NP/np_requests.cpp index 1b1777ef29..ad2909b53d 100644 --- a/rpcs3/Emu/NP/np_requests.cpp +++ b/rpcs3/Emu/NP/np_requests.cpp @@ -213,7 +213,7 @@ namespace np return 0; }); } - + return true; } @@ -675,7 +675,12 @@ namespace np return; } - bool np_handler::reply_req_ticket(u32 /*req_id*/, std::vector& reply_data) + const ticket& np_handler::get_ticket() const + { + return current_ticket; + } + + bool np_handler::reply_req_ticket([[maybe_unused]] u32 req_id, std::vector& reply_data) { vec_stream reply(reply_data, 1); auto ticket_raw = reply.get_rawdata(); @@ -683,7 +688,7 @@ namespace np if (reply.is_error()) return error_and_disconnect("Malformed reply to RequestTicket command"); - current_ticket = std::move(ticket_raw); + current_ticket = ticket(std::move(ticket_raw)); auto ticket_size = static_cast(current_ticket.size()); if (manager_cb)