diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index c6647bd..289fca4 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -11,7 +11,10 @@ set(HEADER_FILES include/chiaki/takion.h include/chiaki/senkusha.h include/chiaki/nagare.h - include/chiaki/mirai.h) + include/chiaki/mirai.h + include/chiaki/ecdh.h + include/chiaki/launchspec.h + include/chiaki/random.h) set(SOURCE_FILES src/common.c @@ -27,7 +30,10 @@ set(SOURCE_FILES src/utils.h src/pb_utils.h src/nagare.c - src/mirai.c) + src/mirai.c + src/ecdh.c + src/launchspec.c + src/random.c) add_subdirectory(protobuf) include_directories("${NANOPB_SOURCE_DIR}") diff --git a/lib/include/chiaki/ecdh.h b/lib/include/chiaki/ecdh.h new file mode 100644 index 0000000..50162ad --- /dev/null +++ b/lib/include/chiaki/ecdh.h @@ -0,0 +1,39 @@ +/* + * 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_ECDH_H +#define CHIAKI_ECDH_H + +#include "common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct chiaki_ecdh_t +{ + +} ChiakiECDH; + +CHIAKI_EXPORT ChiakiErrorCode chiaki_ecdh_init(ChiakiECDH *ecdh); +CHIAKI_EXPORT void chiaki_ecdh_fini(ChiakiECDH *ecdh); + +#ifdef __cplusplus +} +#endif + +#endif // CHIAKI_ECDH_H diff --git a/lib/include/chiaki/launchspec.h b/lib/include/chiaki/launchspec.h new file mode 100644 index 0000000..a23716c --- /dev/null +++ b/lib/include/chiaki/launchspec.h @@ -0,0 +1,43 @@ +/* + * 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_LAUNCHSPEC_H +#define CHIAKI_LAUNCHSPEC_H + +#include "common.h" + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct chiaki_launch_spec_t +{ + unsigned int mtu; + unsigned int rtt; + uint8_t *handshake_key; +} ChiakiLaunchSpec; + +CHIAKI_EXPORT ssize_t chiaki_launchspec_format(char *buf, size_t buf_size, ChiakiLaunchSpec *launch_spec); + +#ifdef __cplusplus +} +#endif + +#endif // CHIAKI_LAUNCHSPEC_H diff --git a/lib/include/chiaki/random.h b/lib/include/chiaki/random.h new file mode 100644 index 0000000..ee35f20 --- /dev/null +++ b/lib/include/chiaki/random.h @@ -0,0 +1,36 @@ +/* + * 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_RANDOM_H +#define CHIAKI_RANDOM_H + +#include "common.h" + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +CHIAKI_EXPORT ChiakiErrorCode chiaki_random_bytes(uint8_t *buf, size_t buf_size); + +#ifdef __cplusplus +} +#endif + +#endif // CHIAKI_RANDOM_H diff --git a/lib/include/chiaki/session.h b/lib/include/chiaki/session.h index 1a85128..41dbaca 100644 --- a/lib/include/chiaki/session.h +++ b/lib/include/chiaki/session.h @@ -36,6 +36,7 @@ extern "C" { #define CHIAKI_RP_DID_SIZE 32 #define CHIAKI_SESSION_ID_SIZE_MAX 80 +#define CHIAKI_HANDSHAKE_KEY_SIZE 0x10 typedef struct chiaki_connect_info_t { @@ -96,7 +97,10 @@ typedef struct chiaki_session_t uint8_t nonce[CHIAKI_KEY_BYTES]; ChiakiRPCrypt rpcrypt; - char session_id[CHIAKI_SESSION_ID_SIZE_MAX]; + char session_id[CHIAKI_SESSION_ID_SIZE_MAX]; // zero-terminated + uint8_t handshake_key[CHIAKI_HANDSHAKE_KEY_SIZE]; + unsigned int mtu; + unsigned int rtt; ChiakiQuitReason quit_reason; diff --git a/lib/src/ecdh.c b/lib/src/ecdh.c new file mode 100644 index 0000000..0cf366d --- /dev/null +++ b/lib/src/ecdh.c @@ -0,0 +1,28 @@ +/* + * 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 + +CHIAKI_EXPORT ChiakiErrorCode chiaki_ecdh_init(ChiakiECDH *ecdh) +{ + return CHIAKI_ERR_SUCCESS; +} + +CHIAKI_EXPORT void chiaki_ecdh_fini(ChiakiECDH *ecdh) +{ + +} \ No newline at end of file diff --git a/lib/src/launchspec.c b/lib/src/launchspec.c new file mode 100644 index 0000000..014fe58 --- /dev/null +++ b/lib/src/launchspec.c @@ -0,0 +1,90 @@ +/* + * 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 +#include +#include + +#include + +static const char launchspec_fmt[] = + "{" + "\"sessionId\":\"sessionId4321\"," + "\"streamResolutions\":[" + "{" + "\"resolution\":" + "{" + "\"width\":640," + "\"height\":360" + "}," + "\"maxFps\":30," + "\"score\":10" + "}" + "]," + "\"network\":{" + "\"bwKbpsSent\":2000," + "\"bwLoss\":0.001000," + "\"mtu\":%u," // 0 + "\"rtt\":%u," // 1 + "\"ports\":[53,2053]" + "}," + "\"slotId\":1," + "\"appSpecification\":{" + "\"minFps\":30," + "\"minBandwidth\":0," + "\"extTitleId\":\"ps3\"," + "\"version\":1," + "\"timeLimit\":1," + "\"startTimeout\":100," + "\"afkTimeout\":100," + "\"afkTimeoutDisconnect\":100" + "}," + "\"konan\":{" + "\"ps3AccessToken\":\"accessToken\"," + "\"ps3RefreshToken\":\"refreshToken\"" + "},\"requestGameSpecification\":{" + "\"model\":\"bravia_tv\"," + "\"platform\":\"android\"," + "\"audioChannels\":\"5.1\"," + "\"language\":\"sp\"," + "\"acceptButton\":\"X\"," + "\"connectedControllers\":[\"xinput\",\"ds3\",\"ds4\"]," + "\"yuvCoefficient\":\"bt601\"," + "\"videoEncoderProfile\":\"hw4.1\"," + "\"audioEncoderProfile\":\"audio1\"" + "}," + "\"userProfile\":{" + "\"onlineId\":\"psnId\"," + "\"npId\":\"npId\"," + "\"region\":\"US\"," + "\"languagesUsed\":[\"en\",\"jp\"]" + "}," + "\"handshakeKey\":\"%s\"" // 2 + "}"; + +CHIAKI_EXPORT ssize_t chiaki_launchspec_format(char *buf, size_t buf_size, ChiakiLaunchSpec *launch_spec) +{ + char handshake_key_b64[CHIAKI_HANDSHAKE_KEY_SIZE * 2]; + ChiakiErrorCode err = chiaki_base64_encode(launch_spec->handshake_key, CHIAKI_HANDSHAKE_KEY_SIZE, handshake_key_b64, sizeof(handshake_key_b64)); + if(err != CHIAKI_ERR_SUCCESS) + return -1; + + int written = snprintf(buf, buf_size, launchspec_fmt, launch_spec->mtu, launch_spec->rtt, handshake_key_b64); + if(written < 0 || written >= buf_size) + return -1; + return written; +} \ No newline at end of file diff --git a/lib/src/nagare.c b/lib/src/nagare.c index a8c72ef..22b652f 100644 --- a/lib/src/nagare.c +++ b/lib/src/nagare.c @@ -18,6 +18,8 @@ #include #include +#include +#include #include #include @@ -43,6 +45,7 @@ static ChiakiErrorCode nagare_send_disconnect(ChiakiNagare *nagare); CHIAKI_EXPORT ChiakiErrorCode chiaki_nagare_run(ChiakiSession *session) { ChiakiNagare *nagare = &session->nagare; + nagare->session = session; nagare->log = &session->log; ChiakiErrorCode err = chiaki_mirai_init(&nagare->bang_mirai); @@ -144,20 +147,71 @@ static void nagare_takion_data(uint8_t *buf, size_t buf_size, void *user) } } + +static bool chiaki_pb_encode_zero_encrypted_key(pb_ostream_t *stream, const pb_field_t *field, void *const *arg) +{ + if (!pb_encode_tag_for_field(stream, field)) + return false; + uint8_t data[] = { 0, 0, 0, 0 }; + return pb_encode_string(stream, data, sizeof(data)); +} + +#define LAUNCH_SPEC_JSON_BUF_SIZE 1024 + static ChiakiErrorCode nagare_send_big(ChiakiNagare *nagare) { + ChiakiSession *session = nagare->session; + + ChiakiLaunchSpec launch_spec; + launch_spec.mtu = session->mtu; + launch_spec.rtt = session->rtt; + launch_spec.handshake_key = session->handshake_key; + + union + { + char json[LAUNCH_SPEC_JSON_BUF_SIZE]; + char b64[LAUNCH_SPEC_JSON_BUF_SIZE * 2]; + } launch_spec_buf; + ssize_t launch_spec_json_size = chiaki_launchspec_format(launch_spec_buf.json, sizeof(launch_spec_buf.json), &launch_spec); + if(launch_spec_json_size != CHIAKI_ERR_SUCCESS) + { + CHIAKI_LOGE(nagare->log, "Nagare failed to format LaunchSpec json\n"); + return CHIAKI_ERR_UNKNOWN; + } + launch_spec_json_size += 1; // we also want the trailing 0 + + uint8_t launch_spec_json_enc[LAUNCH_SPEC_JSON_BUF_SIZE]; + memset(launch_spec_json_enc, 0, (size_t)launch_spec_json_size); + ChiakiErrorCode err = chiaki_rpcrypt_encrypt(&session->rpcrypt, 0, launch_spec_json_enc, launch_spec_json_enc, + (size_t)launch_spec_json_size); + if(err != CHIAKI_ERR_SUCCESS) + { + CHIAKI_LOGE(nagare->log, "Nagare failed to encrypt LaunchSpec\n"); + return err; + } + + xor_bytes(launch_spec_json_enc, (uint8_t *)launch_spec_buf.json, (size_t)launch_spec_json_size); + err = chiaki_base64_encode(launch_spec_json_enc, (size_t)launch_spec_json_size, launch_spec_buf.b64, sizeof(launch_spec_buf.b64)); + if(err != CHIAKI_ERR_SUCCESS) + { + CHIAKI_LOGE(nagare->log, "Nagare failed to encode LaunchSpec as base64\n"); + return err; + } + tkproto_TakionMessage msg; memset(&msg, 0, sizeof(msg)); msg.type = tkproto_TakionMessage_PayloadType_BIG; msg.has_big_payload = true; msg.big_payload.client_version = 9; - msg.big_payload.session_key.arg = ""; + msg.big_payload.session_key.arg = session->session_id; msg.big_payload.session_key.funcs.encode = chiaki_pb_encode_string; - msg.big_payload.launch_spec.arg = ""; + msg.big_payload.launch_spec.arg = launch_spec_buf.b64; msg.big_payload.launch_spec.funcs.encode = chiaki_pb_encode_string; - msg.big_payload.encrypted_key.arg = ""; - msg.big_payload.encrypted_key.funcs.encode = chiaki_pb_encode_string; + msg.big_payload.encrypted_key.funcs.encode = chiaki_pb_encode_zero_encrypted_key; + + // TODO: msg.big_payload.ecdh_pub_key + // TODO: msg.big_payload.ecdh_sig uint8_t buf[12]; size_t buf_size; @@ -171,7 +225,7 @@ static ChiakiErrorCode nagare_send_big(ChiakiNagare *nagare) } buf_size = stream.bytes_written; - ChiakiErrorCode err = chiaki_takion_send_message_data(&nagare->takion, 0, 1, 1, buf, buf_size); + err = chiaki_takion_send_message_data(&nagare->takion, 0, 1, 1, buf, buf_size); return err; } diff --git a/lib/src/random.c b/lib/src/random.c new file mode 100644 index 0000000..18960c7 --- /dev/null +++ b/lib/src/random.c @@ -0,0 +1,28 @@ +/* + * 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 + +#include + +CHIAKI_EXPORT ChiakiErrorCode chiaki_random_bytes(uint8_t *buf, size_t buf_size) +{ + int r = RAND_bytes(buf, (int)buf_size); + if(!r) + return CHIAKI_ERR_UNKNOWN; + return CHIAKI_ERR_SUCCESS; +} \ No newline at end of file diff --git a/lib/src/session.c b/lib/src/session.c index dc96193..d3bf85e 100644 --- a/lib/src/session.c +++ b/lib/src/session.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -163,8 +164,19 @@ static void *session_thread_func(void *arg) goto quit_ctrl; } + // TODO: Senkusha should set that + session->mtu = 1454; + session->rtt = 12; + CHIAKI_LOGI(&session->log, "Senkusha completed successfully\n"); + err = chiaki_random_bytes(session->handshake_key, sizeof(session->handshake_key)); + if(err != CHIAKI_ERR_SUCCESS) + { + CHIAKI_LOGE(&session->log, "Session failed to generate handshake key\n"); + goto quit_ctrl; + } + err = chiaki_nagare_run(session); if(err != CHIAKI_ERR_SUCCESS) { @@ -375,4 +387,3 @@ static bool session_thread_request_session(ChiakiSession *session) close(session_sock); return response.success; } - diff --git a/lib/src/utils.h b/lib/src/utils.h index 6d4a191..b1e2ff0 100644 --- a/lib/src/utils.h +++ b/lib/src/utils.h @@ -32,4 +32,15 @@ static inline ChiakiErrorCode set_port(struct sockaddr *sa, in_port_t port) return CHIAKI_ERR_SUCCESS; } +static inline void xor_bytes(uint8_t *dst, uint8_t *src, size_t sz) +{ + while(sz > 0) + { + *dst ^= *src; + dst++; + src++; + sz--; + } +} + #endif // CHIAKI_UTILS_H