From 42cca4618f37d14d35312b08f6a669d4fa3dbbeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Wed, 14 Aug 2019 22:23:10 +0200 Subject: [PATCH] Implement Regist Request --- lib/CMakeLists.txt | 6 +- lib/include/chiaki/regist.h | 69 +++++++++++ lib/include/chiaki/rpcrypt.h | 4 +- lib/src/regist.c | 228 +++++++++++++++++++++++++++++++++++ lib/src/rpcrypt.c | 26 +++- lib/src/session.c | 5 +- test/rpcrypt.c | 6 +- 7 files changed, 335 insertions(+), 9 deletions(-) create mode 100644 lib/include/chiaki/regist.h create mode 100644 lib/src/regist.c diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 1b4651c..9d2daca 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -31,7 +31,8 @@ set(HEADER_FILES include/chiaki/controller.h include/chiaki/takionsendbuffer.h include/chiaki/time.h - include/chiaki/fec.h) + include/chiaki/fec.h + include/chiaki/regist.h) set(SOURCE_FILES src/common.c @@ -65,7 +66,8 @@ set(SOURCE_FILES src/controller.c src/takionsendbuffer.c src/time.c - src/fec) + src/fec + src/regist.c) add_subdirectory(protobuf) include_directories("${NANOPB_SOURCE_DIR}") diff --git a/lib/include/chiaki/regist.h b/lib/include/chiaki/regist.h new file mode 100644 index 0000000..e62fb0e --- /dev/null +++ b/lib/include/chiaki/regist.h @@ -0,0 +1,69 @@ +/* + * This file is part of Chiaki. + * + * Chiaki is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Chiaki is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chiaki. If not, see . + */ + +#ifndef CHIAKI_REGIST_H +#define CHIAKI_REGIST_H + +#include "common.h" +#include "log.h" +#include "thread.h" +#include "stoppipe.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct chiaki_regist_info_t +{ + char *host; + char *psn_id; + uint32_t pin; +} ChiakiRegistInfo; + +typedef enum chiaki_regist_event_type_t { + CHIAKI_REGIST_EVENT_TYPE_CONNECTED, + CHIAKI_REGIST_EVENT_TYPE_FINISHED_FAILED, + CHIAKI_REGIST_EVENT_TYPE_FINISHED_SUCCESS +} ChiakiRegistEventType; + +typedef struct chiaki_regist_event_t +{ + ChiakiRegistEventType type; +} ChiakiRegistEvent; + +typedef void (*ChiakiRegistCb)(ChiakiRegistEvent *event, void *user); + +typedef struct chiaki_regist_t +{ + ChiakiLog *log; + ChiakiRegistInfo info; + ChiakiRegistCb cb; + void *cb_user; + + ChiakiThread thread; + ChiakiStopPipe stop_pipe; +} ChiakiRegist; + +CHIAKI_EXPORT ChiakiErrorCode chiaki_regist_start(ChiakiRegist *regist, ChiakiLog *log, ChiakiRegistInfo *info, ChiakiRegistCb cb, void *cb_user); +CHIAKI_EXPORT void chiaki_regist_fini(ChiakiRegist *regist); +CHIAKI_EXPORT void chiaki_regist_stop(ChiakiRegist *regist); + +#ifdef __cplusplus +} +#endif + +#endif // CHIAKI_REGIST_H diff --git a/lib/include/chiaki/rpcrypt.h b/lib/include/chiaki/rpcrypt.h index eba61d9..7928b80 100644 --- a/lib/include/chiaki/rpcrypt.h +++ b/lib/include/chiaki/rpcrypt.h @@ -36,8 +36,10 @@ typedef struct chiaki_rpcrypt_t } ChiakiRPCrypt; CHIAKI_EXPORT void chiaki_rpcrypt_bright_ambassador(uint8_t *bright, uint8_t *ambassador, const uint8_t *nonce, const uint8_t *morning); +CHIAKI_EXPORT void chiaki_rpcrypt_aeropause(uint8_t *aeropause, const uint8_t *nonce); -CHIAKI_EXPORT void chiaki_rpcrypt_init(ChiakiRPCrypt *rpcrypt, const uint8_t *nonce, const uint8_t *morning); +CHIAKI_EXPORT void chiaki_rpcrypt_init_auth(ChiakiRPCrypt *rpcrypt, const uint8_t *nonce, const uint8_t *morning); +CHIAKI_EXPORT void chiaki_rpcrypt_init_regist(ChiakiRPCrypt *rpcrypt, const uint8_t *nonce, uint32_t pin); CHIAKI_EXPORT ChiakiErrorCode chiaki_rpcrypt_generate_iv(ChiakiRPCrypt *rpcrypt, uint8_t *iv, uint64_t counter); CHIAKI_EXPORT ChiakiErrorCode chiaki_rpcrypt_encrypt(ChiakiRPCrypt *rpcrypt, uint64_t counter, uint8_t *in, uint8_t *out, size_t sz); CHIAKI_EXPORT ChiakiErrorCode chiaki_rpcrypt_decrypt(ChiakiRPCrypt *rpcrypt, uint64_t counter, uint8_t *in, uint8_t *out, size_t sz); diff --git a/lib/src/regist.c b/lib/src/regist.c new file mode 100644 index 0000000..4b4dce6 --- /dev/null +++ b/lib/src/regist.c @@ -0,0 +1,228 @@ +/* + * This file is part of Chiaki. + * + * Chiaki is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Chiaki is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chiaki. If not, see . + */ + +#include "utils.h" + +#include +#include +#include + +#include +#include +#include + +#include + +#define REGIST_PORT 9295 + +static void *regist_thread_func(void *user); +static int regist_connect(ChiakiRegist *regist); + +CHIAKI_EXPORT ChiakiErrorCode chiaki_regist_start(ChiakiRegist *regist, ChiakiLog *log, ChiakiRegistInfo *info, ChiakiRegistCb cb, void *cb_user) +{ + regist->log = log; + regist->info = *info; + regist->info.host = strdup(regist->info.host); + if(!regist->info.host) + return CHIAKI_ERR_MEMORY; + regist->info.psn_id = strdup(regist->info.psn_id); + if(!regist->info.psn_id) + goto error_host; + + regist->cb = cb; + regist->cb_user = cb_user; + + ChiakiErrorCode err = chiaki_stop_pipe_init(®ist->stop_pipe); + if(err != CHIAKI_ERR_SUCCESS) + goto error_psn_id; + + err = chiaki_thread_create(®ist->thread, regist_thread_func, regist); + if(err != CHIAKI_ERR_SUCCESS) + goto error_stop_pipe; + + return CHIAKI_ERR_SUCCESS; + +error_stop_pipe: + chiaki_stop_pipe_fini(®ist->stop_pipe); +error_psn_id: + free(regist->info.psn_id); +error_host: + free(regist->info.host); + return err; +} + +CHIAKI_EXPORT void chiaki_regist_fini(ChiakiRegist *regist) +{ + chiaki_thread_join(®ist->thread, NULL); + chiaki_stop_pipe_fini(®ist->stop_pipe); + free(regist->info.host); +} + +CHIAKI_EXPORT void chiaki_regist_stop(ChiakiRegist *regist) +{ + chiaki_stop_pipe_stop(®ist->stop_pipe); +} + +static void regist_event_simple(ChiakiRegist *regist, ChiakiRegistEventType type) +{ + ChiakiRegistEvent event = { 0 }; + event.type = type; + regist->cb(&event, regist->cb_user); +} + +static const char * const request_fmt = + "POST /sce/rp/regist HTTP/1.1\r\n" + "HOST: 10.0.2.15\r\n" // random lol + "User-Agent: remoteplay Windows\r\n" + "Connection: close\r\n" + "Content-Length: %llu\r\n\r\n"; + +static const char * const request_inner_fmt = + "Client-Type: Windows\r\n" + "Np-Online-Id: %s\r\n\r\n"; + +static void *regist_thread_func(void *user) +{ + ChiakiRegist *regist = user; + + uint8_t nonce[CHIAKI_KEY_BYTES]; + ChiakiErrorCode err = chiaki_random_bytes_crypt(nonce, sizeof(nonce)); + if(err != CHIAKI_ERR_SUCCESS) + { + CHIAKI_LOGE(regist->log, "Regist failed to generate random nonce"); + goto fail; + } + + ChiakiRPCrypt crypt; + chiaki_rpcrypt_init_regist(&crypt, nonce, regist->info.pin); + + uint8_t payload[0x400]; + static const size_t inner_header_off = 0x1e0; + memset(payload, 'A', inner_header_off); + chiaki_rpcrypt_aeropause(payload + 0x11c, nonce); + int inner_header_size = snprintf((char *)payload + inner_header_off, sizeof(payload) - inner_header_off, request_inner_fmt, regist->info.psn_id); + if(inner_header_size >= sizeof(payload) - inner_header_off) + { + CHIAKI_LOGE(regist->log, "Regist failed to format payload"); + goto fail; + } + chiaki_rpcrypt_encrypt(&crypt, 0, payload + inner_header_off, payload + inner_header_off, inner_header_size); + size_t payload_size = inner_header_off + inner_header_size; + + char request_header[0x100]; + int request_header_size = snprintf(request_header, sizeof(request_header), request_fmt, (unsigned long long)payload_size); + if(request_header_size >= sizeof(request_header)) + { + CHIAKI_LOGE(regist->log, "Regist failed to format request"); + goto fail; + } + + int sock = regist_connect(regist); + if(sock < 0) + { + CHIAKI_LOGE(regist->log, "Regist eventually failed to connect"); + goto fail; + } + + int s = send(sock, request_header, request_header_size, 0); + if(s < 0) + { + CHIAKI_LOGE(regist->log, "Regist failed to send request header"); + goto fail_socket; + } + + s = send(s, payload, payload_size, 0); + if(s < 0) + { + CHIAKI_LOGE(regist->log, "Regist failed to send payload"); + goto fail_socket; + } + + char recv_buf[0x100]; + ssize_t received; +rerecv: + received = recv(sock, recv_buf, sizeof(recv_buf) - 1, 0); + if(received < 0) + CHIAKI_LOGE(regist->log, "Failed"); + else + { + chiaki_log_hexdump(regist->log, CHIAKI_LOG_DEBUG, (uint8_t *)recv_buf, received); + goto rerecv; + } + + close(sock); + + regist_event_simple(regist, CHIAKI_REGIST_EVENT_TYPE_FINISHED_SUCCESS); // TODO: communicate params + return NULL; + +fail_socket: + close(sock); +fail: + regist_event_simple(regist, CHIAKI_REGIST_EVENT_TYPE_FINISHED_FAILED); + return NULL; +} + +static int regist_connect(ChiakiRegist *regist) +{ + struct addrinfo *addrinfos; + int r = getaddrinfo(regist->info.host, NULL, NULL, &addrinfos); + if(r != 0) + { + CHIAKI_LOGE(regist->log, "Regist failed to getaddrinfo on %s", regist->info.host); + regist_event_simple(regist, CHIAKI_REGIST_EVENT_TYPE_FINISHED_FAILED); + return -1; + } + + int sock = -1; + for(struct addrinfo *ai=addrinfos; ai; ai=ai->ai_next) + { + if(ai->ai_protocol != IPPROTO_TCP) + continue; + + if(ai->ai_addr->sa_family != AF_INET) // TODO: support IPv6 + continue; + + struct sockaddr *sa = malloc(ai->ai_addrlen); + if(!sa) + continue; + memcpy(sa, ai->ai_addr, ai->ai_addrlen); + + set_port(sa, htons(REGIST_PORT)); + + sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if(sock < 0) + { + free(sa); + continue; + } + r = connect(sock, sa, ai->ai_addrlen); + if(r < 0) + { + int errsv = errno; + CHIAKI_LOGE(regist->log, "Regist connect failed: %s", strerror(errsv)); + close(sock); + sock = -1; + free(sa); + continue; + } + free(sa); + break; + } + freeaddrinfo(addrinfos); + + return sock; +} \ No newline at end of file diff --git a/lib/src/rpcrypt.c b/lib/src/rpcrypt.c index e58747f..31bc8ab 100644 --- a/lib/src/rpcrypt.c +++ b/lib/src/rpcrypt.c @@ -23,11 +23,11 @@ #include #include +static const uint8_t echo_b[] = { 0xe1, 0xec, 0x9c, 0x3a, 0xdd, 0xbd, 0x08, 0x85, 0xfc, 0x0e, 0x1d, 0x78, 0x90, 0x32, 0xc0, 0x04 }; CHIAKI_EXPORT void chiaki_rpcrypt_bright_ambassador(uint8_t *bright, uint8_t *ambassador, const uint8_t *nonce, const uint8_t *morning) { static const uint8_t echo_a[] = { 0x01, 0x49, 0x87, 0x9b, 0x65, 0x39, 0x8b, 0x39, 0x4b, 0x3a, 0x8d, 0x48, 0xc3, 0x0a, 0xef, 0x51 }; - static const uint8_t echo_b[] = { 0xe1, 0xec, 0x9c, 0x3a, 0xdd, 0xbd, 0x08, 0x85, 0xfc, 0x0e, 0x1d, 0x78, 0x90, 0x32, 0xc0, 0x04 }; for(uint8_t i=0; ibright, rpcrypt->ambassador, nonce, morning); } +CHIAKI_EXPORT void chiaki_rpcrypt_init_regist(ChiakiRPCrypt *rpcrypt, const uint8_t *nonce, uint32_t pin) +{ + static const uint8_t regist_aes_key[CHIAKI_KEY_BYTES] = { 0x3f, 0x1c, 0xc4, 0xb6, 0xdc, 0xbb, 0x3e, 0xcc, 0x50, 0xba, 0xed, 0xef, 0x97, 0x34, 0xc7, 0xc9 }; + memcpy(rpcrypt->ambassador, nonce, sizeof(rpcrypt->ambassador)); + memcpy(rpcrypt->bright, regist_aes_key, sizeof(rpcrypt->bright)); + rpcrypt->bright[0] ^= (uint8_t)((pin >> 0x18) & 0xff); + rpcrypt->bright[1] ^= (uint8_t)((pin >> 0x10) & 0xff); + rpcrypt->bright[2] ^= (uint8_t)((pin >> 0x08) & 0xff); + rpcrypt->bright[3] ^= (uint8_t)((pin >> 0x00) & 0xff); +} + CHIAKI_EXPORT ChiakiErrorCode chiaki_rpcrypt_generate_iv(ChiakiRPCrypt *rpcrypt, uint8_t *iv, uint64_t counter) { uint8_t hmac_key[] = { 0xac, 0x07, 0x88, 0x83, 0xc8, 0x3a, 0x1f, 0xe8, 0x11, 0x46, 0x3a, 0xf3, 0x9e, 0xe3, 0xe3, 0x77 }; diff --git a/lib/src/session.c b/lib/src/session.c index c435e7e..8ca880c 100644 --- a/lib/src/session.c +++ b/lib/src/session.c @@ -287,7 +287,7 @@ static void *session_thread_func(void *arg) CHIAKI_LOGI(session->log, "Session request successful"); - chiaki_rpcrypt_init(&session->rpcrypt, session->nonce, session->connect_info.morning); + chiaki_rpcrypt_init_auth(&session->rpcrypt, session->nonce, session->connect_info.morning); // PS4 doesn't always react right away, sleep a bit chiaki_cond_timedwait_pred(&session->state_cond, &session->state_mutex, 10, session_check_state_pred, session); @@ -482,7 +482,10 @@ static bool session_thread_request_session(ChiakiSession *session) session_sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if(session_sock < 0) + { + free(sa); continue; + } r = connect(session_sock, sa, ai->ai_addrlen); if(r < 0) { diff --git a/test/rpcrypt.c b/test/rpcrypt.c index 9acd808..2ffaabb 100644 --- a/test/rpcrypt.c +++ b/test/rpcrypt.c @@ -47,7 +47,7 @@ static MunitResult test_iv(const MunitParameter params[], void *user) ChiakiRPCrypt rpcrypt; ChiakiErrorCode err; - chiaki_rpcrypt_init(&rpcrypt, nonce, morning); + chiaki_rpcrypt_init_auth(&rpcrypt, nonce, morning); uint8_t iv[CHIAKI_KEY_BYTES]; @@ -81,7 +81,7 @@ static MunitResult test_encrypt(const MunitParameter params[], void *user) ChiakiRPCrypt rpcrypt; ChiakiErrorCode err; - chiaki_rpcrypt_init(&rpcrypt, nonce, morning); + chiaki_rpcrypt_init_auth(&rpcrypt, nonce, morning); // less than block size uint8_t buf_a[] = { 0x13, 0x37, 0xc0, 0xff, 0xee }; @@ -123,7 +123,7 @@ static MunitResult test_decrypt(const MunitParameter params[], void *user) ChiakiRPCrypt rpcrypt; ChiakiErrorCode err; - chiaki_rpcrypt_init(&rpcrypt, nonce, morning); + chiaki_rpcrypt_init_auth(&rpcrypt, nonce, morning); // less than block size uint8_t buf_a[] = { 0x8d, 0xd2, 0x1d, 0xfb };