diff --git a/lib/include/chiaki/common.h b/lib/include/chiaki/common.h index 88585d2..20cdd1a 100644 --- a/lib/include/chiaki/common.h +++ b/lib/include/chiaki/common.h @@ -40,6 +40,7 @@ typedef enum CHIAKI_ERR_CANCELED, CHIAKI_ERR_TIMEOUT, CHIAKI_ERR_INVALID_RESPONSE, + CHIAKI_ERR_INVALID_MAC, CHIAKI_ERR_UNINITIALIZED } ChiakiErrorCode; diff --git a/lib/include/chiaki/nagare.h b/lib/include/chiaki/nagare.h index aa163f7..456ddb4 100644 --- a/lib/include/chiaki/nagare.h +++ b/lib/include/chiaki/nagare.h @@ -37,8 +37,8 @@ typedef struct chiaki_nagare_t ChiakiTakion takion; ChiakiMirai mirai; uint8_t *ecdh_secret; - ChiakiGKCrypt *gkcrypt_a; - ChiakiGKCrypt *gkcrypt_b; + ChiakiGKCrypt *gkcrypt_local; + ChiakiGKCrypt *gkcrypt_remote; } ChiakiNagare; CHIAKI_EXPORT ChiakiErrorCode chiaki_nagare_run(struct chiaki_session_t *session); diff --git a/lib/include/chiaki/takion.h b/lib/include/chiaki/takion.h index 171402e..ee3e63a 100644 --- a/lib/include/chiaki/takion.h +++ b/lib/include/chiaki/takion.h @@ -21,6 +21,7 @@ #include "common.h" #include "thread.h" #include "log.h" +#include "gkcrypt.h" #include @@ -32,7 +33,6 @@ extern "C" { typedef void (*ChiakiTakionDataCallback)(uint8_t *buf, size_t buf_size, void *user); typedef void (*ChiakiTakionAVCallback)(uint8_t *buf, size_t buf_size, uint8_t base_type, uint32_t key_pos, void *user); -typedef ChiakiErrorCode (*ChiakiTakionMACCallback)(uint8_t *buf, size_t buf_size, size_t key_pos, uint8_t *mac_out, void *user); typedef struct chiaki_takion_connect_info_t @@ -44,20 +44,18 @@ typedef struct chiaki_takion_connect_info_t void *data_cb_user; ChiakiTakionAVCallback av_cb; void *av_cb_user; - ChiakiTakionMACCallback mac_cb; - void *mac_cb_user; } ChiakiTakionConnectInfo; typedef struct chiaki_takion_t { ChiakiLog *log; + ChiakiGKCrypt *gkcrypt_local; // if NULL (default), no gmac is calculated and nothing is encrypted + ChiakiGKCrypt *gkcrypt_remote; // if NULL (default), remote gmacs are IGNORED (!) and everything is expected to be unencrypted ChiakiTakionDataCallback data_cb; void *data_cb_user; ChiakiTakionAVCallback av_cb; void *av_cb_user; - ChiakiTakionMACCallback mac_cb; - void *mac_cb_user; int sock; ChiakiThread thread; int stop_pipe[2]; @@ -75,6 +73,11 @@ CHIAKI_EXPORT void chiaki_takion_close(ChiakiTakion *takion); CHIAKI_EXPORT ChiakiErrorCode chiaki_takion_send_raw(ChiakiTakion *takion, uint8_t *buf, size_t buf_size); CHIAKI_EXPORT ChiakiErrorCode chiaki_takion_send_message_data(ChiakiTakion *takion, uint32_t key_pos, uint8_t type_b, uint16_t channel, uint8_t *buf, size_t buf_size); +static inline void chiaki_takion_set_crypt(ChiakiTakion *takion, ChiakiGKCrypt *gkcrypt_local, ChiakiGKCrypt *gkcrypt_remote) { + takion->gkcrypt_local = gkcrypt_local; + takion->gkcrypt_remote = gkcrypt_remote; +} + #ifdef __cplusplus } #endif diff --git a/lib/src/nagare.c b/lib/src/nagare.c index ea02f9e..48957b6 100644 --- a/lib/src/nagare.c +++ b/lib/src/nagare.c @@ -91,8 +91,6 @@ CHIAKI_EXPORT ChiakiErrorCode chiaki_nagare_run(ChiakiSession *session) takion_info.data_cb_user = nagare; takion_info.av_cb = nagare_takion_av; takion_info.av_cb_user = nagare; - takion_info.mac_cb = nagare_takion_mac; - takion_info.mac_cb_user = nagare; err = chiaki_takion_connect(&nagare->takion, &takion_info); free(takion_info.sa); @@ -132,19 +130,26 @@ CHIAKI_EXPORT ChiakiErrorCode chiaki_nagare_run(ChiakiSession *session) CHIAKI_LOGI(&session->log, "Nagare successfully received bang\n"); - nagare->gkcrypt_a = chiaki_gkcrypt_new(&session->log, 0 /* TODO */, 2, session->handshake_key, nagare->ecdh_secret); - if(!nagare->gkcrypt_a) + nagare->gkcrypt_local = chiaki_gkcrypt_new(&session->log, 0 /* TODO */, 2, session->handshake_key, nagare->ecdh_secret); + if(!nagare->gkcrypt_local) { CHIAKI_LOGE(&session->log, "Nagare failed to initialize GKCrypt with index 2\n"); goto error_takion; } - nagare->gkcrypt_b = chiaki_gkcrypt_new(&session->log, 0 /* TODO */, 3, session->handshake_key, nagare->ecdh_secret); - if(!nagare->gkcrypt_b) + nagare->gkcrypt_remote = chiaki_gkcrypt_new(&session->log, 0 /* TODO */, 3, session->handshake_key, nagare->ecdh_secret); + if(!nagare->gkcrypt_remote) { CHIAKI_LOGE(&session->log, "Nagare failed to initialize GKCrypt with index 3\n"); goto error_gkcrypt_a; } + // TODO: IMPORTANT!!! + // This all needs to be synchronized correctly!!! + // After receiving the bang, we MUST wait for the gkcrypts to be set before handling any new takion packets! + // Otherwise we might ignore some macs. + // Also, access to the gkcrypts must be synchronized for key_pos and everything. + chiaki_takion_set_crypt(&nagare->takion, nagare->gkcrypt_local, nagare->gkcrypt_remote); + err = chiaki_mirai_request_begin(&nagare->mirai, NAGARE_MIRAI_REQUEST_STREAMINFO, false); assert(err == CHIAKI_ERR_SUCCESS); err = chiaki_mirai_request_wait(&nagare->mirai, EXPECT_TIMEOUT_MS, false); @@ -174,9 +179,9 @@ CHIAKI_EXPORT ChiakiErrorCode chiaki_nagare_run(ChiakiSession *session) err = CHIAKI_ERR_SUCCESS; // TODO: can't roll everything back like this, takion has to be closed first always. - chiaki_gkcrypt_free(nagare->gkcrypt_b); + chiaki_gkcrypt_free(nagare->gkcrypt_remote); error_gkcrypt_a: - chiaki_gkcrypt_free(nagare->gkcrypt_a); + chiaki_gkcrypt_free(nagare->gkcrypt_local); error_takion: chiaki_takion_close(&nagare->takion); CHIAKI_LOGI(&session->log, "Nagare closed takion\n"); @@ -214,6 +219,7 @@ static void nagare_takion_data(uint8_t *buf, size_t buf_size, void *user) if(!r) { CHIAKI_LOGE(nagare->log, "Nagare failed to decode data protobuf\n"); + chiaki_log_hexdump(nagare->log, CHIAKI_LOG_ERROR, buf, buf_size); return; } } @@ -500,7 +506,7 @@ static void nagare_takion_av(uint8_t *buf, size_t buf_size, uint8_t base_type, u { ChiakiNagare *nagare = user; - chiaki_gkcrypt_decrypt(nagare->gkcrypt_b, key_pos + CHIAKI_GKCRYPT_BLOCK_SIZE, buf, buf_size); + chiaki_gkcrypt_decrypt(nagare->gkcrypt_remote, key_pos + CHIAKI_GKCRYPT_BLOCK_SIZE, buf, buf_size); if(buf[0] == 0xf4 && buf_size >= 0x50) { @@ -514,12 +520,3 @@ static void nagare_takion_av(uint8_t *buf, size_t buf_size, uint8_t base_type, u //CHIAKI_LOGD(nagare->log, "Nagare AV %lu\n", buf_size); //chiaki_log_hexdump(nagare->log, CHIAKI_LOG_DEBUG, buf, buf_size); } - - -static ChiakiErrorCode nagare_takion_mac(uint8_t *buf, size_t buf_size, size_t key_pos, uint8_t *mac_out, void *user) -{ - ChiakiNagare *nagare = user; - if(!nagare->gkcrypt_b) - return CHIAKI_ERR_UNINITIALIZED; - return chiaki_gkcrypt_gmac(nagare->gkcrypt_b, key_pos, buf, buf_size, mac_out); -} \ No newline at end of file diff --git a/lib/src/senkusha.c b/lib/src/senkusha.c index 699dae3..20ae0fa 100644 --- a/lib/src/senkusha.c +++ b/lib/src/senkusha.c @@ -73,9 +73,6 @@ CHIAKI_EXPORT ChiakiErrorCode chiaki_senkusha_run(ChiakiSession *session) takion_info.data_cb = senkusha_takion_data; takion_info.data_cb_user = &senkusha; - takion_info.mac_cb = NULL; // TODO: Do we need the MAC here? - takion_info.mac_cb_user = NULL; - err = chiaki_takion_connect(&senkusha.takion, &takion_info); free(takion_info.sa); if(err != CHIAKI_ERR_SUCCESS) diff --git a/lib/src/takion.c b/lib/src/takion.c index ce0ad2b..d016193 100644 --- a/lib/src/takion.c +++ b/lib/src/takion.c @@ -103,12 +103,12 @@ CHIAKI_EXPORT ChiakiErrorCode chiaki_takion_connect(ChiakiTakion *takion, Chiaki ChiakiErrorCode ret; takion->log = info->log; + takion->gkcrypt_local = NULL; + takion->gkcrypt_remote = NULL; takion->data_cb = info->data_cb; takion->data_cb_user = info->data_cb_user; takion->av_cb = info->av_cb; takion->av_cb_user = info->av_cb_user; - takion->mac_cb = info->mac_cb; - takion->mac_cb_user = info->mac_cb_user; takion->something = TAKION_LOCAL_SOMETHING; takion->tag_local = 0x4823; // "random" tag @@ -371,10 +371,75 @@ static ChiakiErrorCode takion_recv(ChiakiTakion *takion, uint8_t *buf, size_t *b } +static ChiakiErrorCode takion_handle_packet_mac(ChiakiTakion *takion, uint8_t base_type, uint8_t *buf, size_t buf_size) +{ + if(!takion->gkcrypt_remote) + return CHIAKI_ERR_SUCCESS; + + size_t mac_offset; + size_t key_pos_offset; + switch(base_type) + { + case TAKION_PACKET_TYPE_MESSAGE: + mac_offset = 5; + key_pos_offset = 0x9; + break; + case TAKION_PACKET_TYPE_2: + case TAKION_PACKET_TYPE_3: + mac_offset = 0xa; + key_pos_offset = 0xe; + break; + default: + CHIAKI_LOGW(takion->log, "Takion packet with unknown type %#x received\n", base_type); + //chiaki_log_hexdump(takion->log, CHIAKI_LOG_WARNING, buf, buf_size); + return CHIAKI_ERR_INVALID_DATA; + } + + if(buf_size < mac_offset + CHIAKI_GKCRYPT_GMAC_SIZE || buf_size < key_pos_offset + 4) { + CHIAKI_LOGW(takion->log, "Takion packet with type %#x too short\n", base_type); + return CHIAKI_ERR_INVALID_DATA; + } + + uint32_t key_pos = ntohl(*((uint32_t *)(buf + key_pos_offset))); + uint8_t mac[CHIAKI_GKCRYPT_GMAC_SIZE]; + memcpy(mac, buf + mac_offset, sizeof(mac)); + memset(buf + mac_offset, 0, sizeof(mac)); + + uint8_t mac_expected[CHIAKI_GKCRYPT_GMAC_SIZE]; + memset(mac_expected, 0, sizeof(mac_expected)); + + //CHIAKI_LOGD(takion->log, "calculating GMAC for:\n"); + //chiaki_log_hexdump(takion->log, CHIAKI_LOG_DEBUG, buf, buf_size); + + ChiakiErrorCode err = chiaki_gkcrypt_gmac(takion->gkcrypt_remote, key_pos, buf, buf_size, mac_expected); + if(err != CHIAKI_ERR_SUCCESS) + { + CHIAKI_LOGE(takion->log, "Takion failed to calculate MAC\n"); + return err; + } + if(memcmp(mac_expected, mac, sizeof(mac)) != 0) + { + CHIAKI_LOGE(takion->log, "Takion packet MAC mismatch for packet type %#x with key_pos %#lx\n", base_type, key_pos); + chiaki_log_hexdump(takion->log, CHIAKI_LOG_ERROR, buf, buf_size); + CHIAKI_LOGD(takion->log, "GMAC:\n"); + chiaki_log_hexdump(takion->log, CHIAKI_LOG_DEBUG, mac, sizeof(mac)); + CHIAKI_LOGD(takion->log, "GMAC expected:\n"); + chiaki_log_hexdump(takion->log, CHIAKI_LOG_DEBUG, mac_expected, sizeof(mac_expected)); + return CHIAKI_ERR_INVALID_MAC; + } + + return CHIAKI_ERR_SUCCESS; +} + + static void takion_handle_packet(ChiakiTakion *takion, uint8_t *buf, size_t buf_size) { assert(buf_size > 0); - uint8_t base_type = buf[0]; + uint8_t base_type = (uint8_t)(buf[0] & 0xf); + + if(takion_handle_packet_mac(takion, base_type, buf, buf_size) != CHIAKI_ERR_SUCCESS) + return; + switch(base_type) { case TAKION_PACKET_TYPE_MESSAGE: @@ -385,7 +450,8 @@ static void takion_handle_packet(ChiakiTakion *takion, uint8_t *buf, size_t buf_ takion_handle_packet_av(takion, base_type, buf, buf_size); break; default: - CHIAKI_LOGW(takion->log, "Takion packet with unknown type %#x received\n", buf[0]); + CHIAKI_LOGW(takion->log, "Takion packet with unknown type %#x received\n", base_type); + //chiaki_log_hexdump(takion->log, CHIAKI_LOG_WARNING, buf, buf_size); break; } } @@ -666,14 +732,14 @@ static void takion_handle_packet_av(ChiakiTakion *takion, uint8_t base_type, uin { // HHIxIIx - assert(base_type == 2 || base_type == 3); // TODO: should actually be base_type & 0xf + assert(base_type == 2 || base_type == 3); size_t av_size = buf_size-1; TakionAVPacket packet = {0}; packet.is_2 = base_type == 2; - packet.byte_at_0x1a = ((base_type >> 4) & 1) != 0; // TODO: pass actual base_type, not just 2 and 3 + packet.byte_at_0x1a = ((buf[0] >> 4) & 1) != 0; size_t av_header_size = packet.is_2 ? AV_HEADER_SIZE_2 : AV_HEADER_SIZE_3; if(av_size < av_header_size + 1) // TODO: compare av_size or buf_size? @@ -703,40 +769,11 @@ static void takion_handle_packet_av(ChiakiTakion *takion, uint8_t base_type, uin packet.codec = av[8]; - uint8_t gmac[4]; - memcpy(gmac, av + 9, sizeof(gmac)); - memset(av + 9, 0, sizeof(gmac)); uint32_t key_pos = ntohl(*((uint32_t *)(av + 0xd))); uint8_t unknown_1 = av[0x11]; - CHIAKI_LOGD(takion->log, "av packet %d %d %d %x %d %d\n", base_type, packet.word_at_0xa, packet.word_at_0xc, packet.word_at_0xe, packet.codec, unknown_1); - - if(takion->mac_cb) - { - uint8_t gmac_expected[4]; - memset(gmac_expected, 0, sizeof(gmac_expected)); - - //CHIAKI_LOGD(takion->log, "calculating GMAC for:\n"); - //chiaki_log_hexdump(takion->log, CHIAKI_LOG_DEBUG, buf, buf_size); - - if(takion->mac_cb(buf, buf_size, key_pos, gmac_expected, takion->mac_cb_user) != CHIAKI_ERR_SUCCESS) - { - CHIAKI_LOGE(takion->log, "Takion failed to calculate MAC\n"); - return; - } - //CHIAKI_LOGD(takion->log, "GMAC:\n"); - //chiaki_log_hexdump(takion->log, CHIAKI_LOG_DEBUG, gmac, sizeof(gmac)); - //CHIAKI_LOGD(takion->log, "GMAC expected:\n"); - //chiaki_log_hexdump(takion->log, CHIAKI_LOG_DEBUG, gmac_expected, sizeof(gmac_expected)); - if(memcmp(gmac_expected, gmac, sizeof(gmac)) != 0) - { - CHIAKI_LOGE(takion->log, "Takion packet MAC mismatch for key_pos %#lx\n", key_pos); - return; - } - CHIAKI_LOGD(takion->log, "Takion packet MAC correct for key pos %#lx\n", key_pos); - } av += 0x12; av_size -= 12;