Refactor MAC check in Takion

This commit is contained in:
Florian Märkl 2019-04-03 21:36:24 +02:00
commit fbd79f8796
No known key found for this signature in database
GPG key ID: 125BC8A5A6A1E857
6 changed files with 98 additions and 63 deletions

View file

@ -40,6 +40,7 @@ typedef enum
CHIAKI_ERR_CANCELED,
CHIAKI_ERR_TIMEOUT,
CHIAKI_ERR_INVALID_RESPONSE,
CHIAKI_ERR_INVALID_MAC,
CHIAKI_ERR_UNINITIALIZED
} ChiakiErrorCode;

View file

@ -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);

View file

@ -21,6 +21,7 @@
#include "common.h"
#include "thread.h"
#include "log.h"
#include "gkcrypt.h"
#include <netinet/in.h>
@ -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

View file

@ -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);
}

View file

@ -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)

View file

@ -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;