]> gitweb.ps.run Git - matrix_esp_thesis/commitdiff
matrix.c: add masterKey and verified, add HandleSync/HandleEvent
authorPatrick <patrick.schoenberger@posteo.de>
Mon, 13 Nov 2023 21:29:42 +0000 (22:29 +0100)
committerPatrick <patrick.schoenberger@posteo.de>
Mon, 13 Nov 2023 21:29:42 +0000 (22:29 +0100)
src/matrix.c

index fe8e1a3e241fdabb1b9ac94cc62dbb39e5b897f6..4ce78ce6248e12b2e370b13cddf9f893795abe6a 100644 (file)
@@ -3,6 +3,7 @@
 #include <time.h>\r
 #include <stdio.h>\r
 #include <mjson.h>\r
+#include <olm/sas.h>\r
 \r
 #ifdef ESP_PLATFORM\r
 #include <esp_random.h>\r
@@ -369,6 +370,7 @@ MatrixOlmSessionEncrypt(
     STATIC uint8_t random[OLM_ENCRYPT_RANDOM_SIZE];\r
     Randomize(random, OLM_ENCRYPT_RANDOM_SIZE);\r
 \r
+    memset(outBuffer, 0, outBufferCap);\r
     size_t res = olm_encrypt(session->session,\r
         plaintext, strlen(plaintext),\r
         random, OLM_ENCRYPT_RANDOM_SIZE,\r
@@ -393,7 +395,7 @@ MatrixOlmSessionDecrypt(
             encrypted, strlen(encrypted),\r
             outBuffer, outBufferCap);\r
     \r
-    if (res != olm_error() && res < outBufferCap)\r
+    if (res != olm_error() && (int)res < outBufferCap)\r
         outBuffer[res] = '\0';\r
 \r
     return res != olm_error();\r
@@ -494,6 +496,7 @@ MatrixMegolmOutSessionEncrypt(
     const char * plaintext,\r
     char * outBuffer, int outBufferCap)\r
 {\r
+    memset(outBuffer, 0, outBufferCap);\r
     size_t res = olm_group_encrypt(session->session,\r
         (uint8_t *)plaintext, strlen(plaintext),\r
         (uint8_t *)outBuffer, outBufferCap);\r
@@ -501,73 +504,6 @@ MatrixMegolmOutSessionEncrypt(
     return res != olm_error();\r
 }\r
 \r
-bool\r
-MatrixMegolmOutSessionSave(\r
-    MatrixMegolmOutSession * session,\r
-    const char * filename,\r
-    const char * key)\r
-{\r
-    FILE * f = fopen(filename, "w");\r
-\r
-    size_t roomIdLen = strlen(session->roomId);\r
-    fwrite(&roomIdLen, sizeof(size_t), 1, f);\r
-    fwrite(session->roomId, 1, roomIdLen, f);\r
-\r
-    size_t pickleBufferLen =\r
-        olm_pickle_outbound_group_session_length(\r
-            session->session);\r
-    void * pickleBuffer = malloc(pickleBufferLen);\r
-\r
-    olm_pickle_outbound_group_session(\r
-        session->session,\r
-        key, strlen(key),\r
-        pickleBuffer, pickleBufferLen);\r
-    \r
-    fwrite(&pickleBufferLen, sizeof(size_t), 1, f);\r
-    fwrite(pickleBuffer, 1, pickleBufferLen, f);\r
-    free(pickleBuffer);\r
-\r
-    fclose(f);\r
-\r
-    return true;\r
-}\r
-\r
-bool\r
-MatrixMegolmOutSessionLoad(\r
-    MatrixMegolmOutSession * session,\r
-    const char * filename,\r
-    const char * key)\r
-{\r
-    FILE * f = fopen(filename, "r");\r
-\r
-    size_t roomIdLen;\r
-    fread(&roomIdLen, sizeof(size_t), 1, f);\r
-    fread(session->roomId, 1, roomIdLen, f);\r
-    for (int i = roomIdLen; i < ROOM_ID_SIZE; i++)\r
-        session->roomId[i] = '\0';\r
-\r
-    size_t pickleBufferLen;\r
-    fread(&pickleBufferLen, sizeof(size_t), 1, f);\r
-\r
-    void * pickleBuffer = malloc(pickleBufferLen);\r
-    fread(pickleBuffer, 1, pickleBufferLen, f);\r
-\r
-    olm_unpickle_outbound_group_session(\r
-        session->session,\r
-        key, strlen(key),\r
-        pickleBuffer, pickleBufferLen);\r
-    \r
-    free(pickleBuffer);\r
-\r
-    olm_outbound_group_session_id(session->session, (uint8_t *)session->id, MEGOLM_SESSION_ID_SIZE);\r
-    olm_outbound_group_session_key(session->session, (uint8_t *)session->key, MEGOLM_SESSION_KEY_SIZE);\r
-\r
-    fclose(f);\r
-\r
-    return true;\r
-}\r
-\r
-\r
 \r
 bool\r
 MatrixClientInit(\r
@@ -581,70 +517,6 @@ MatrixClientInit(
     return true;\r
 }\r
 \r
-bool\r
-MatrixClientSave(\r
-    MatrixClient * client,\r
-    const char * filename)\r
-{\r
-    FILE * f = fopen(filename, "w");\r
-    \r
-    \r
-    char thisDeviceKey[DEVICE_KEY_SIZE];\r
-    MatrixOlmAccountGetDeviceKey(&client->olmAccount, thisDeviceKey, DEVICE_KEY_SIZE);\r
-    char thisSigningKey[DEVICE_KEY_SIZE];\r
-    MatrixOlmAccountGetSigningKey(&client->olmAccount, thisSigningKey, DEVICE_KEY_SIZE);\r
-\r
-\r
-    fwrite(thisDeviceKey, 1, DEVICE_KEY_SIZE, f);\r
-    fwrite(thisSigningKey, 1, DEVICE_KEY_SIZE, f);\r
-    fwrite(client->userId, 1, USER_ID_SIZE, f);\r
-    fwrite(client->accessToken, 1, ACCESS_TOKEN_SIZE, f);\r
-    fwrite(client->deviceId, 1, DEVICE_ID_SIZE, f);\r
-    fwrite(client->expireMs, 1, EXPIRE_MS_SIZE, f);\r
-    fwrite(client->refreshToken, 1, REFRESH_TOKEN_SIZE, f);\r
-\r
-    fwrite(&client->numDevices, sizeof(int), 1, f);\r
-    for (int i = 0; i < client->numDevices; i++) {\r
-        fwrite(client->devices[i].deviceId, 1, DEVICE_ID_SIZE, f);\r
-        fwrite(client->devices[i].deviceKey, 1, DEVICE_KEY_SIZE, f);\r
-    }\r
-\r
-    fclose(f);\r
-    return true;\r
-}\r
-\r
-bool\r
-MatrixClientLoad(\r
-    MatrixClient * client,\r
-    const char * filename)\r
-{\r
-    FILE * f = fopen(filename, "r");\r
-    \r
-    \r
-    char thisDeviceKey[DEVICE_KEY_SIZE];\r
-    MatrixOlmAccountGetDeviceKey(&client->olmAccount, thisDeviceKey, DEVICE_KEY_SIZE);\r
-    char thisSigningKey[DEVICE_KEY_SIZE];\r
-    MatrixOlmAccountGetSigningKey(&client->olmAccount, thisSigningKey, DEVICE_KEY_SIZE);\r
-\r
-\r
-    fread(thisDeviceKey, 1, DEVICE_KEY_SIZE, f);\r
-    fread(thisSigningKey, 1, DEVICE_KEY_SIZE, f);\r
-    fread(client->userId, 1, USER_ID_SIZE, f);\r
-    fread(client->accessToken, 1, ACCESS_TOKEN_SIZE, f);\r
-    fread(client->deviceId, 1, DEVICE_ID_SIZE, f);\r
-    fread(client->expireMs, 1, EXPIRE_MS_SIZE, f);\r
-    fread(client->refreshToken, 1, REFRESH_TOKEN_SIZE, f);\r
-\r
-    fread(&client->numDevices, sizeof(int), 1, f);\r
-    for (int i = 0; i < client->numDevices; i++) {\r
-        fread(client->devices[i].deviceId, 1, DEVICE_ID_SIZE, f);\r
-        fread(client->devices[i].deviceKey, 1, DEVICE_KEY_SIZE, f);\r
-    }\r
-\r
-    fclose(f);\r
-    return true;\r
-}\r
-\r
 bool\r
 MatrixClientSetAccessToken(\r
     MatrixClient * client,\r
@@ -762,7 +634,7 @@ MatrixClientUploadOnetimeKeys(
 \r
 // https://spec.matrix.org/v1.7/client-server-api/#post_matrixclientv3keysupload\r
 bool\r
-MatrixClientUploadDeviceKey(\r
+MatrixClientUploadDeviceKeys(\r
     MatrixClient * client)\r
 {\r
     char thisDeviceKey[DEVICE_KEY_SIZE];\r
@@ -998,12 +870,447 @@ MatrixClientSendEventEncrypted(
         g_EncryptedEventBuffer);\r
 }\r
 \r
+void\r
+MatrixClientHandleEvent(\r
+    MatrixClient * client,\r
+    const char * event, int eventLen\r
+) {\r
+    STATIC char eventType[128];\r
+    memset(eventType, 0, sizeof(eventType));\r
+    mjson_get_string(event, eventLen, "$.type", eventType, 128);\r
+\r
+    static char transactionId[64];\r
+    static char verifyFromDeviceId[DEVICE_ID_SIZE];\r
+    static OlmSAS * olmSas = NULL;\r
+\r
+    if (strcmp(eventType, "m.key.verification.request") == 0) {\r
+        memset(transactionId, 0, 64);\r
+        if (olmSas != NULL)\r
+            free(olmSas);\r
+        \r
+        mjson_get_string(event, eventLen, "$.content.transaction_id", transactionId, 64);\r
+        mjson_get_string(event, eventLen, "$.content.from_device", verifyFromDeviceId, DEVICE_ID_SIZE);\r
+        \r
+        char verificationReadyBuffer[2048];\r
+        snprintf(verificationReadyBuffer, 2048,\r
+            "{"\r
+            "\"from_device\":\"%s\","\r
+            "\"methods\":[\"m.sas.v1\"],"\r
+            "\"transaction_id\":\"%s\""\r
+            "}",\r
+            client->deviceId,\r
+            transactionId);\r
+        \r
+        MatrixClientSendToDevice(client,\r
+            client->userId,\r
+            verifyFromDeviceId,\r
+            verificationReadyBuffer,\r
+            "m.key.verification.ready");\r
+    }\r
+    else if (strcmp(eventType, "m.key.verification.start") == 0) {\r
+        olmSas = olm_sas(malloc(olm_sas_size()));\r
+        void * sasRandomBytes = malloc(olm_create_sas_random_length(olmSas));\r
+        olm_create_sas(olmSas,\r
+            sasRandomBytes,\r
+            olm_create_sas_random_length(olmSas));\r
+        \r
+        OlmUtility * olmUtil = olm_utility(malloc(olm_utility_size()));\r
+        \r
+        STATIC char publicKey[64];\r
+        STATIC char keyStartJsonCanonical[512];\r
+        STATIC char concat[512+64];\r
+        STATIC char commitment[1024];\r
+        olm_sas_get_pubkey(olmSas,\r
+            publicKey,\r
+            64);\r
+        printf("public key: %.*s\n", olm_sas_pubkey_length(olmSas), publicKey);\r
+\r
+        const char * keyStartJson;\r
+        int keyStartJsonLen;\r
+        mjson_find(event, eventLen, "$.content", &keyStartJson, &keyStartJsonLen);\r
+        JsonCanonicalize(keyStartJson, keyStartJsonLen, keyStartJsonCanonical, 512);\r
+\r
+        printf("json:\n%.*s\ncanonical json:\n%s\n", keyStartJsonLen, keyStartJson, keyStartJsonCanonical);\r
+\r
+        int concatLen =\r
+            snprintf(concat, 512+64, "%.*s%s", olm_sas_pubkey_length(olmSas), publicKey, keyStartJsonCanonical);\r
+\r
+        int commitmentLen =\r
+            olm_sha256(olmUtil, concat, concatLen, commitment, 1024);\r
+        \r
+        STATIC char verificationAcceptBuffer[512];\r
+        snprintf(verificationAcceptBuffer, 512,\r
+            "{"\r
+            "\"commitment\":\"%.*s\","\r
+            "\"hash\":\"sha256\","\r
+            "\"key_agreement_protocol\":\"curve25519\","\r
+            "\"message_authentication_code\":\"hkdf-hmac-sha256.v2\","\r
+            "\"method\":\"m.sas.v1\","\r
+            "\"short_authentication_string\":[\"decimal\"],"\r
+            "\"transaction_id\":\"%s\""\r
+            "}",\r
+            commitmentLen, commitment,\r
+            transactionId);\r
+        \r
+        MatrixClientSendToDevice(client,\r
+            client->userId,\r
+            verifyFromDeviceId,\r
+            verificationAcceptBuffer,\r
+            "m.key.verification.accept");\r
+    }\r
+    else if (strcmp(eventType, "m.key.verification.key") == 0) {\r
+        STATIC char publicKey[128];\r
+        olm_sas_get_pubkey(olmSas,\r
+            publicKey,\r
+            128);\r
+\r
+        STATIC char theirPublicKey[128];\r
+        int theirPublicKeyLen =\r
+            mjson_get_string(event, eventLen, "$.content.key", theirPublicKey, 128);\r
+        \r
+        printf("event: %.*s\n", eventLen, event);\r
+        printf("theirPublicKey: %.*s\n", theirPublicKeyLen, theirPublicKey);\r
+        printf("publicKey: %.*s\n", olm_sas_pubkey_length(olmSas), publicKey);\r
+\r
+        olm_sas_set_their_key(olmSas, theirPublicKey, theirPublicKeyLen);\r
+        \r
+        STATIC char verificationKeyBuffer[256];\r
+        snprintf(verificationKeyBuffer, 256,\r
+            "{"\r
+            "\"key\":\"%.*s\","\r
+            "\"transaction_id\":\"%s\""\r
+            "}",\r
+            olm_sas_pubkey_length(olmSas), publicKey,\r
+            transactionId);\r
+        \r
+        MatrixClientSendToDevice(client,\r
+            client->userId,\r
+            verifyFromDeviceId,\r
+            verificationKeyBuffer,\r
+            "m.key.verification.key");\r
+        \r
+        // sas\r
+        STATIC char hkdfInfo[1024];\r
+        int hkdfInfoLen =\r
+            snprintf(hkdfInfo, 1024,\r
+                "MATRIX_KEY_VERIFICATION_SAS%s%s%s%s%s",\r
+                client->userId,\r
+                verifyFromDeviceId,\r
+                client->userId,\r
+                client->deviceId,\r
+                transactionId);\r
+\r
+        unsigned char sasBytes[5];\r
+        olm_sas_generate_bytes(olmSas,\r
+            hkdfInfo, hkdfInfoLen,\r
+            sasBytes, 5);\r
+        int b0 = sasBytes[0];\r
+        int b1 = sasBytes[1];\r
+        int b2 = sasBytes[2];\r
+        int b3 = sasBytes[3];\r
+        int b4 = sasBytes[4];\r
+        \r
+        printf("%d %d %d %d %d\n", b0, b1, b2, b3, b4);\r
+\r
+        // https://spec.matrix.org/v1.7/client-server-api/#sas-method-decimal\r
+        printf("%d | %d | %d\n",\r
+            (b0 << 5 | b1 >> 3) + 1000,\r
+            ((b1 & 0x7) << 10 | b2 << 2 | b3 >> 6) + 1000,\r
+            ((b3 & 0x3F) << 7 | b4 >> 1) + 1000);\r
+        printf("%d | %d | %d\n",\r
+            ((b0 << 5) | (b1 >> 3)) + 1000,\r
+            (((b1 & 0x7) << 10) | (b2 << 2) | (b3 >> 6)) + 1000,\r
+            (((b3 & 0x3F) << 7) | (b4 >> 1)) + 1000);\r
+    }\r
+    else if (strcmp(eventType, "m.key.verification.mac") == 0) {        \r
+        // mac\r
+        STATIC char masterKey[123];\r
+        MatrixClientRequestMasterKey(client, masterKey, 123);\r
+\r
+        STATIC char keyList[256];\r
+        STATIC char keyListMac[256];\r
+        STATIC char key1Id[128];\r
+        STATIC char key1[128];\r
+        STATIC char key1Mac[128];\r
+        STATIC char key2Id[128];\r
+        STATIC char key2[128];\r
+        STATIC char key2Mac[128];\r
+\r
+        if (strcmp(masterKey, client->deviceId) < 0) {\r
+            snprintf(key1Id, 1024, "ed25519:%s", masterKey);\r
+            strcpy(key1, masterKey);\r
+            snprintf(key2Id, 1024, "ed25519:%s", client->deviceId);\r
+            MatrixOlmAccountGetSigningKey(&client->olmAccount, key2, 1024);\r
+        }\r
+        else {\r
+            snprintf(key1Id, 1024, "ed25519:%s", client->deviceId);\r
+            MatrixOlmAccountGetSigningKey(&client->olmAccount, key1, 1024);\r
+            snprintf(key2Id, 1024, "ed25519:%s", masterKey);\r
+            strcpy(key2, masterKey);\r
+        }\r
+\r
+        snprintf(keyList, 1024,\r
+            "%s,%s", key1Id, key2Id);\r
+        \r
+        STATIC char macInfo[1024];\r
+        int macInfoLen;\r
+        {\r
+            macInfoLen =\r
+                snprintf(macInfo, 1024,\r
+                    "MATRIX_KEY_VERIFICATION_MAC%s%s%s%s%s%s",\r
+                    client->userId,\r
+                    client->deviceId,\r
+                    client->userId,\r
+                    verifyFromDeviceId,\r
+                    transactionId,\r
+                    "KEY_IDS");\r
+            olm_sas_calculate_mac_fixed_base64(olmSas, keyList, strlen(keyList), macInfo, macInfoLen, keyListMac, 1024);\r
+        }\r
+        {\r
+            macInfoLen =\r
+                snprintf(macInfo, 1024,\r
+                    "MATRIX_KEY_VERIFICATION_MAC%s%s%s%s%s%s",\r
+                    client->userId,\r
+                    client->deviceId,\r
+                    client->userId,\r
+                    verifyFromDeviceId,\r
+                    transactionId,\r
+                    key1Id);\r
+            olm_sas_calculate_mac_fixed_base64(olmSas, key1, strlen(key1), macInfo, macInfoLen, key1Mac, 1024);\r
+        }\r
+        {\r
+            macInfoLen =\r
+                snprintf(macInfo, 1024,\r
+                    "MATRIX_KEY_VERIFICATION_MAC%s%s%s%s%s%s",\r
+                    client->userId,\r
+                    client->deviceId,\r
+                    client->userId,\r
+                    verifyFromDeviceId,\r
+                    transactionId,\r
+                    key2Id);\r
+            olm_sas_calculate_mac_fixed_base64(olmSas, key2, strlen(key2), macInfo, macInfoLen, key2Mac, 1024);\r
+        }\r
+\r
+        STATIC char verificationMacBuffer[1024];\r
+        snprintf(verificationMacBuffer, 1024,\r
+            "{"\r
+            "\"keys\":\"%s\","\r
+            "\"mac\":{"\r
+            "\"%s\":\"%s\","\r
+            "\"%s\":\"%s\""\r
+            "},"\r
+            "\"transaction_id\":\"%s\""\r
+            "}",\r
+            keyListMac,\r
+            key1Id,\r
+            key1Mac,\r
+            key2Id,\r
+            key2Mac,\r
+            transactionId);\r
+        \r
+        MatrixClientSendToDevice(client,\r
+            client->userId,\r
+            verifyFromDeviceId,\r
+            verificationMacBuffer,\r
+            "m.key.verification.mac");\r
+\r
+        STATIC char verificationDoneBuffer[128];\r
+        snprintf(verificationDoneBuffer, 128,\r
+            "{"\r
+            "\"transaction_id\":\"%s\""\r
+            "}",\r
+            transactionId);\r
+        \r
+        MatrixClientSendToDevice(client,\r
+            client->userId,\r
+            verifyFromDeviceId,\r
+            verificationDoneBuffer,\r
+            "m.key.verification.done");\r
+        \r
+        free(olmSas);\r
+        client->verified = true;\r
+    }\r
+    else if (strcmp(eventType, "m.room.encrypted") == 0) {\r
+        STATIC char algorithm[128];\r
+        mjson_get_string(event, eventLen, "$.content.algorithm", algorithm, 128);\r
+\r
+        if (strcmp(algorithm, "m.olm.v1.curve25519-aes-sha2") == 0) {\r
+            STATIC char thisDeviceKey[DEVICE_KEY_SIZE];\r
+            MatrixOlmAccountGetDeviceKey(&client->olmAccount, thisDeviceKey, DEVICE_KEY_SIZE);\r
+\r
+            STATIC char jp[128];\r
+            snprintf(jp, 128, "$.content.ciphertext.%s.type", thisDeviceKey);\r
+\r
+            double messageType;\r
+            mjson_get_number(event, eventLen, jp, &messageType);\r
+            int messageTypeInt = (int)messageType;\r
+\r
+            snprintf(jp, 128, "$.content.ciphertext.%s.body", thisDeviceKey);\r
+\r
+            mjson_get_string(event, eventLen, jp, g_EncryptedEventBuffer, 2048);\r
+\r
+            MatrixOlmSession * olmSession;\r
+            \r
+            if (! MatrixClientGetOlmSession(client, client->userId, verifyFromDeviceId, &olmSession))\r
+            {\r
+                if (messageTypeInt == 0) {\r
+                    MatrixClientNewOlmSessionIn(client,\r
+                        client->userId,\r
+                        verifyFromDeviceId,\r
+                        g_EncryptedEventBuffer,\r
+                        &olmSession);\r
+                }\r
+                else {\r
+                    MatrixClientNewOlmSessionOut(client,\r
+                        client->userId,\r
+                        verifyFromDeviceId,\r
+                        &olmSession);\r
+                }\r
+            }\r
+            \r
+            STATIC char decrypted[2048];\r
+            MatrixOlmSessionDecrypt(olmSession,\r
+                messageTypeInt, g_EncryptedEventBuffer, decrypted, 2048);\r
+            \r
+            MatrixClientHandleEvent(client, decrypted, strlen(decrypted));\r
+        }\r
+    }\r
+    else if (strcmp(eventType, "m.room_key") == 0 ||\r
+             strcmp(eventType, "m.forwarded_room_key") == 0) {\r
+        STATIC char roomId[128];\r
+        STATIC char sessionId[128];\r
+        STATIC char sessionKey[1024];\r
+        mjson_get_string(event, eventLen, "$.content.room_id", roomId, 128);\r
+        mjson_get_string(event, eventLen, "$.content.session_id", sessionId, 128);\r
+        mjson_get_string(event, eventLen, "$.content.session_key", sessionKey, 1024);\r
+        \r
+        printf("sessionId: %s\n", sessionId);\r
+        printf("sessionKey: %s\n", sessionKey);\r
+\r
+        MatrixMegolmInSession * megolmInSession;\r
+        MatrixClientNewMegolmInSession(client, roomId, sessionId, sessionKey, &megolmInSession);\r
+    }\r
+}\r
+\r
+void\r
+MatrixClientHandleRoomEvent(\r
+    MatrixClient * client,\r
+    const char * room, int roomLen,\r
+    const char * event, int eventLen)\r
+{\r
+    STATIC char eventType[128];\r
+    memset(eventType, 0, sizeof(eventType));\r
+    mjson_get_string(event, eventLen, "$.type", eventType, 128);\r
+\r
+    if (strcmp(eventType, "m.room.encrypted") == 0) {\r
+        STATIC char algorithm[128];\r
+        mjson_get_string(event, eventLen, "$.content.algorithm", algorithm, 128);\r
+\r
+        if (strcmp(algorithm, "m.megolm.v1.aes-sha2") == 0) {\r
+            STATIC char sessionId[128];\r
+            int sessionIdLen =\r
+                mjson_get_string(event, eventLen, "$.content.session_id", sessionId, 128);\r
+\r
+            bool res;\r
+\r
+            MatrixMegolmInSession * megolmInSession;\r
+            res = MatrixClientGetMegolmInSession(client,\r
+                room, roomLen,\r
+                sessionId, sessionIdLen,\r
+                &megolmInSession);\r
+\r
+            if (res) {\r
+                mjson_get_string(event, eventLen, "$.content.ciphertext", g_EncryptedEventBuffer, 2048);\r
+\r
+                STATIC char decrypted[2048];\r
+                MatrixMegolmInSessionDecrypt(megolmInSession, g_EncryptedEventBuffer, strlen(g_EncryptedEventBuffer), decrypted, 2048);\r
+\r
+                MatrixClientHandleEvent(client, decrypted, strlen(decrypted));\r
+            }\r
+            else {\r
+                printf("megolm session not known\n");\r
+            }\r
+        }\r
+    }\r
+    MatrixClientHandleEvent(client, event, eventLen);\r
+}\r
+\r
+void\r
+MatrixClientHandleSync(\r
+    MatrixClient * client,\r
+    char * syncBuffer, int syncBufferLen,\r
+    char * nextBatch, int nextBatchCap)\r
+{    \r
+    int res;\r
+\r
+    const char * s = syncBuffer;\r
+    int slen = syncBufferLen;\r
+\r
+    mjson_get_string(s, slen, "$.next_batch", nextBatch, nextBatchCap);\r
+\r
+    // to_device\r
+\r
+    const char * events;\r
+    int eventsLen;\r
+    res =\r
+        mjson_find(s, slen, "$.to_device.events", &events, &eventsLen);\r
+    \r
+    if (res != MJSON_TOK_INVALID) {\r
+        {\r
+        int koff, klen, voff, vlen, vtype, off = 0;\r
+        for (off = 0; (off = mjson_next(events, eventsLen, off, &koff, &klen,\r
+                                        &voff, &vlen, &vtype)) != 0; ) {\r
+            const char * v = events + voff;\r
+\r
+            MatrixClientHandleEvent(client, v, vlen);\r
+        }\r
+        }\r
+    }\r
+\r
+    // rooms\r
+    \r
+    const char * rooms;\r
+    int roomsLen;\r
+    res =\r
+        mjson_find(s, slen, "$.rooms.join", &rooms, &roomsLen);\r
+    \r
+    if (res != MJSON_TOK_INVALID) {\r
+        {\r
+        int koff, klen, voff, vlen, vtype, off = 0;\r
+        for (off = 0; (off = mjson_next(rooms, roomsLen, off, &koff, &klen,\r
+                                        &voff, &vlen, &vtype)) != 0; ) {\r
+            const char * k = rooms + koff;\r
+            const char * v = rooms + voff;\r
+\r
+            const char * events;\r
+            int eventsLen;\r
+            res =\r
+                mjson_find(v, vlen, "$.timeline.events", &events, &eventsLen);\r
+            \r
+            if (res != MJSON_TOK_INVALID) {\r
+                {\r
+                int koff2, klen2, voff2, vlen2, vtype2, off2 = 0;\r
+                for (off2 = 0; (off2 = mjson_next(events, eventsLen, off2, &koff2, &klen2,\r
+                                                &voff2, &vlen2, &vtype2)) != 0; ) {\r
+                    const char * v2 = events + voff2;\r
+\r
+                    MatrixClientHandleRoomEvent(client,\r
+                        k+1, klen-2,\r
+                        v2, vlen2);\r
+                }\r
+                }\r
+            }\r
+        }\r
+        }\r
+    }\r
+}\r
+\r
 // https://spec.matrix.org/v1.6/client-server-api/#get_matrixclientv3sync\r
 bool\r
 MatrixClientSync(\r
     MatrixClient * client,\r
     char * outSyncBuffer, int outSyncCap,\r
-    const char * nextBatch)\r
+    char * nextBatch, int nextBatchCap)\r
 {\r
     // filter={\"event_fields\":[\"to_device\"]}\r
     STATIC char url[MAX_URL_LEN];\r
@@ -1030,11 +1337,17 @@ MatrixClientSync(
     }\r
     url[index] = '\0';\r
 \r
-    return\r
+    bool result =\r
         MatrixHttpGet(client->hc,\r
             url,\r
             outSyncBuffer, outSyncCap,\r
             true);\r
+    \r
+    MatrixClientHandleSync(client,\r
+        outSyncBuffer, strlen(outSyncBuffer),\r
+        nextBatch, nextBatchCap);\r
+    \r
+    return result;\r
 }\r
 \r
 // https://spec.matrix.org/v1.7/client-server-api/#get_matrixclientv3roomsroomideventeventid\r
@@ -1258,6 +1571,8 @@ MatrixClientGetOlmSession(
     const char * deviceId,\r
     MatrixOlmSession ** outSession)\r
 {\r
+    (void)userId; //unused for now\r
+\r
     for (int i = 0; i < client->numOlmSessions; i++)\r
     {\r
         if (strcmp(client->olmSessions[i].deviceId, deviceId) == 0)\r
@@ -1278,6 +1593,8 @@ MatrixClientNewOlmSessionIn(
     const char * encrypted,\r
     MatrixOlmSession ** outSession)\r
 {\r
+    (void)userId; //unused for now\r
+    \r
     if (client->numOlmSessions < NUM_OLM_SESSIONS)\r
     {\r
         STATIC char deviceKey[DEVICE_KEY_SIZE];\r
@@ -1406,10 +1723,10 @@ MatrixClientSendToDeviceEncrypted(
         "\"sender\":\"%s\","\r
         "\"recipient\":\"%s\","\r
         "\"recipient_keys\":{"\r
-          "\"ed25519\":\"%s\""\r
+        "\"ed25519\":\"%s\""\r
         "},"\r
         "\"keys\":{"\r
-          "\"ed25519\":\"%s\""\r
+        "\"ed25519\":\"%s\""\r
         "}"\r
         "}",\r
         msgType,\r
@@ -1440,6 +1757,7 @@ MatrixClientSendToDeviceEncrypted(
         "\"sender_key\":\"%s\""\r
         "}",\r
         targetDeviceKey,\r
+        // olm_encrypt_message_length(olmSession->session, strlen(g_TodeviceEventBuffer)), g_EncryptedRequestBuffer,\r
         g_EncryptedRequestBuffer,\r
         olm_session_has_received_message(olmSession->session),\r
         client->deviceId,\r
@@ -1474,6 +1792,15 @@ MatrixClientFindDevice(
     const char * deviceId,\r
     MatrixDevice ** outDevice)\r
 {\r
+    for (int i = 0; i < client->numDevices; i++)\r
+    {\r
+        if (strcmp(client->devices[i].deviceId, deviceId) == 0)\r
+        {\r
+            *outDevice = &client->devices[i];\r
+            return true;\r
+        }\r
+    }\r
+\r
     MatrixClientRequestDeviceKeys(client);\r
 \r
     for (int i = 0; i < client->numDevices; i++)\r
@@ -1539,6 +1866,26 @@ MatrixClientRequestSigningKey(
     return false;\r
 }\r
 \r
+bool\r
+MatrixClientRequestMasterKey(\r
+    MatrixClient * client,\r
+    char * outMasterKey, int outMasterKeyCap)\r
+{\r
+    if (strlen(client->masterKey) > 0) {\r
+        strncpy(outMasterKey, outMasterKeyCap, client->masterKey);\r
+        return true;\r
+    }\r
+\r
+    MatrixClientRequestDeviceKeys(client);\r
+    \r
+    if (strlen(client->masterKey) > 0) {\r
+        strncpy(outMasterKey, outMasterKeyCap, client->masterKey);\r
+        return true;\r
+    }\r
+\r
+    return false;\r
+}\r
+\r
 // https://spec.matrix.org/v1.6/client-server-api/#post_matrixclientv3keysquery\r
 bool\r
 MatrixClientRequestDeviceKeys(\r
@@ -1569,17 +1916,31 @@ MatrixClientRequestDeviceKeys(
 \r
     // query for retrieving device keys for user id\r
     STATIC char query[JSON_QUERY_SIZE];\r
+    const char * s;\r
+    int slen;\r
+\r
+    snprintf(query, JSON_QUERY_SIZE,\r
+        "$.master_keys.%s.keys", userIdEscaped);\r
+    mjson_find(responseBuffer, strlen(responseBuffer),\r
+        query, &s, &slen);\r
+    \r
+    int koff, klen, voff, vlen, vtype, off = 0;\r
+    for (off = 0; (off = mjson_next(s, slen, off, &koff, &klen,\r
+                                    &voff, &vlen, &vtype)) != 0; ) {\r
+        snprintf(client->masterKey, MASTER_KEY_SIZE,\r
+            "%.*s", vlen-2, s+voff+1);\r
+\r
+        printf("found master key: %s\n", client->masterKey);\r
+    }\r
+\r
     snprintf(query, JSON_QUERY_SIZE,\r
         "$.device_keys.%s", userIdEscaped);\r
     \r
-    const char * s;\r
-    int slen;\r
     mjson_find(responseBuffer, strlen(responseBuffer),\r
         query, &s, &slen);\r
     \r
     // loop over keys\r
     \r
-    int koff, klen, voff, vlen, vtype, off = 0;\r
     for (off = 0; (off = mjson_next(s, slen, off, &koff, &klen,\r
                                     &voff, &vlen, &vtype)) != 0; ) {\r
         const char * key = s + koff;\r
@@ -1639,4 +2000,4 @@ MatrixClientDeleteDevice(
     bool res = MatrixHttpPost(client->hc, "/_matrix/client/v3/delete_devices",\r
         deleteRequest, deleteResponse, 1024, true);\r
     return res;\r
-}
\ No newline at end of file
+}\r