]> gitweb.ps.run Git - matrix_esp_thesis/commitdiff
olm session management
authorPatrick <patrick.schoenberger@posteo.de>
Mon, 19 Jun 2023 19:21:16 +0000 (21:21 +0200)
committerPatrick <patrick.schoenberger@posteo.de>
Mon, 19 Jun 2023 19:21:16 +0000 (21:21 +0200)
Makefile
examples/Decrypt.c [new file with mode: 0644]
examples/Login.c
examples/Send.c
examples/SendEncrypted.c
examples/Sync.c
src/fixedbuffer.c [deleted file]
src/fixedbuffer.h [deleted file]
src/matrix.c
src/matrix.h
src/matrix_http_mongoose.c

index 82c259ab65a27aa1862edd8649c5fb4aa731e656..88b1941467427fd1cf0d9ac19d3a5b65a8cccf3e 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -9,11 +9,12 @@ C_OPTS+=-I src/
 C_OPTS+=-I ext/olm/include/\r
 C_OPTS+=-I ext/mjson/src/\r
 C_OPTS+=-I ext/mongoose/\r
+C_OPTS+=-L out/olm/\r
 C_OPTS+=-l ws2_32\r
 C_OPTS+=-l ssl\r
 C_OPTS+=-l crypto\r
+C_OPTS+=-l olm\r
 C_OPTS+=-D MG_ENABLE_OPENSSL=1\r
-C_OPTS+=-g\r
 # C_OPTS+=-I ext/curl/include/\r
 # C_OPTS+=-L ext/curl/build/lib/\r
 # C_OPTS+=-l curl\r
@@ -23,7 +24,6 @@ C_OPTS+=-g
 out/examples/%: examples/%.c src/*\r
        $(CC) -o out/examples/$* examples/$*.c $(C_OPTS)\r
 \r
-\r
 .PHONY: examples\r
 \r
 examples: out/examples/Login out/examples/Send out/examples/SendEncrypted out/examples/Sync
\ No newline at end of file
diff --git a/examples/Decrypt.c b/examples/Decrypt.c
new file mode 100644 (file)
index 0000000..b496780
--- /dev/null
@@ -0,0 +1,45 @@
+#include <matrix.h>\r
+#include <stdio.h>\r
+\r
+#define SERVER       "https://matrix.org"\r
+#define ACCESS_TOKEN "syt_cHNjaG8_yBvTjVTquGCikvsAenOJ_49mBMO"\r
+#define DEVICE_ID    "MAZNCCZLBR"\r
+#define ROOM_ID      "!koVStwyiiKcBVbXZYz:matrix.org"\r
+#define EVENT_ID     ""\r
+\r
+int\r
+main(void)\r
+{\r
+    MatrixClient client;\r
+    MatrixClientInit(&client,\r
+        SERVER);\r
+    \r
+    MatrixHttpInit(&client);\r
+\r
+    MatrixClientSetAccessToken(&client,\r
+        ACCESS_TOKEN);\r
+\r
+    static char eventBuffer[1024];\r
+    MatrixClientGetRoomEvent(&client,\r
+        ROOM_ID,\r
+        EVENT_ID,\r
+        eventBuffer, 1024);\r
+\r
+    MatrixMegolmInSession megolmSession;\r
+    \r
+    MatrixClientRequestMegolmSession(&client,\r
+        ROOM_ID,\r
+        EVENT_ID,\r
+        &megolmSession);\r
+\r
+    static char decryptedBuffer[1024];\r
+    MatrixMegolmSessionDecrypt(&megolmSession,\r
+        eventBuffer,\r
+        decryptedBuffer, 1024);\r
+\r
+    printf("%s\n", decryptedBuffer);\r
+        \r
+    MatrixHttpDeinit(&client);\r
+\r
+    return 0;\r
+}
\ No newline at end of file
index 45204fc4fb5fcbb287273a202c941fa8cb8a7c30..1ffbbc0a44897e017d81332bef4980072d49e79c 100644 (file)
@@ -8,7 +8,7 @@
 \r
 \r
 int\r
-main()\r
+main(void)\r
 {\r
     MatrixClient client;\r
     MatrixClientInit(&client,\r
@@ -21,10 +21,10 @@ main()
         PASSWORD,\r
         DISPLAYNAME);\r
 \r
-    printf("Access Token: %s\n", client.accessTokenBuffer);\r
-    printf("Device ID: %s\n", client.deviceIdBuffer);\r
-    printf("Expires in (ms): %s\n", client.expireMsBuffer);\r
-    printf("Refresh Token: %s\n", client.refreshTokenBuffer);\r
+    printf("Access Token: %s\n", client.accessToken);\r
+    printf("Device ID: %s\n", client.deviceId);\r
+    printf("Expires in (ms): %s\n", client.expireMs);\r
+    printf("Refresh Token: %s\n", client.refreshToken);\r
     \r
     MatrixHttpDeinit(&client);\r
 \r
index e595ba7011688c537882233ef977e7da1072761c..cddb96615ef3e1a56f6a1d3cc833e07b13cd01d0 100644 (file)
@@ -6,7 +6,7 @@
 #define ROOM_ID      "!koVStwyiiKcBVbXZYz:matrix.org"\r
 \r
 int\r
-main()\r
+main(void)\r
 {\r
     MatrixClient client;\r
     MatrixClientInit(&client,\r
index 2d3bd744dd0fdb77d4932c510a96a0263699d3f4..db2f83cb9e1710099a29ca255b92fbfc5e2dc968 100644 (file)
@@ -1,32 +1,36 @@
 #include <matrix.h>\r
+#include <stdio.h>\r
 \r
-#define SERVER FixedBuf("matrix.org")\r
-#define ACCESS_TOKEN FixedBuf("abc")\r
-#define ROOM_ID FixedBuf("!jhpZBTbckszblMYjMK:matrix.org")\r
+#define SERVER       "https://matrix.org"\r
+#define ACCESS_TOKEN "syt_cHNjaG8_yBvTjVTquGCikvsAenOJ_49mBMO"\r
+#define DEVICE_ID    "MAZNCCZLBR"\r
+#define ROOM_ID      "!koVStwyiiKcBVbXZYz:matrix.org"\r
 \r
 int\r
-main(\r
-    int argc,\r
-    char **argv)\r
+main(void)\r
 {\r
     MatrixClient client;\r
-    MatrixClientCreate(&client,\r
+    MatrixClientInit(&client,\r
         SERVER);\r
+    \r
+    MatrixHttpInit(&client);\r
 \r
     MatrixClientSetAccessToken(&client,\r
         ACCESS_TOKEN);\r
 \r
-    MatrixMegolmSession megolm;\r
-    MatrixMegolmSessionInit(&megolm);\r
-        \r
-    MatrixRoomShareMegolmSession(&client,\r
-        ROOM_ID,\r
-        megolm);\r
-    \r
-    MatrixClientSendGroupEncrypted(&client,\r
+    // MatrixMegolmOutSession megolmOutSession;\r
+    // MatrixMegolmOutSessionInit(&megolmOutSession);\r
+\r
+    // MatrixClientSetMegolmOutSession(&client,\r
+    //     ROOM_ID,\r
+    //     megolmOutSession);\r
+\r
+    MatrixClientSendEventEncrypted(&client,\r
         ROOM_ID,\r
-        FixedBuf("m.room.message"),\r
-        FixedBuf("{\"body\":\"Hello\",\"msgtype\":\"m.text\"}"));\r
+        "m.room.message",\r
+        "{\"body\":\"Hello\",\"msgtype\":\"m.text\"}");\r
+        \r
+    MatrixHttpDeinit(&client);\r
 \r
     return 0;\r
-}
\ No newline at end of file
+}\r
index 5043884ac8197602808b0dfecd06f2e773d09664..a49cf652256952f521c5732aa884f898d75f051d 100644 (file)
@@ -1,31 +1,28 @@
 #include <matrix.h>\r
+#include <stdio.h>\r
 \r
-#define SERVER "matrix.org"\r
-#define ACCESS_TOKEN "abc"\r
-#define ROOM_ID "!jhpZBTbckszblMYjMK:matrix.org"\r
+#define SERVER       "https://matrix.org"\r
+#define ACCESS_TOKEN "syt_cHNjaG8_yBvTjVTquGCikvsAenOJ_49mBMO"\r
+#define DEVICE_ID    "MAZNCCZLBR"\r
 \r
 int\r
-main(\r
-    int argc,\r
-    char **argv)\r
+main(void)\r
 {\r
     MatrixClient client;\r
-    MatrixClientCreate(&client,\r
+    MatrixClientInit(&client,\r
         SERVER);\r
+    \r
+    MatrixHttpInit(&client);\r
 \r
     MatrixClientSetAccessToken(&client,\r
         ACCESS_TOKEN);\r
 \r
-    static char syncCharBuffer[1024];\r
-    FixedBuffer syncBuffer = { syncCharBuffer, 1024, 0 };\r
-    int syncN = 1;\r
-\r
-    while (syncN > 0)\r
-    {\r
-        MatrixClientSyncN(&client, &syncBuffer, &syncN);\r
-        printf("%.*s", syncBuffer.len, (char *)syncBuffer.ptr);\r
-    }\r
-    printf("\n");\r
+    static char syncBuffer[20000];\r
+    MatrixClientSync(&client,\r
+        syncBuffer, 20000);\r
+    printf("%s", syncBuffer);\r
+        \r
+    MatrixHttpDeinit(&client);\r
 \r
     return 0;\r
 }
\ No newline at end of file
diff --git a/src/fixedbuffer.c b/src/fixedbuffer.c
deleted file mode 100644 (file)
index ad99897..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-#include "fixedbuffer.h"\r
-\r
-#include <string.h>\r
-\r
-FixedBuffer\r
-FixedBuf(const char * str)\r
-{\r
-    int len = strlen(str);\r
-    FixedBuffer result;\r
-    result.ptr = (char *)str;\r
-    result.cap = len;\r
-    result.len = len;\r
-    return result;\r
-}\r
-\r
-bool\r
-FixedBufferToInt(FixedBuffer fb, int * outInt)\r
-{\r
-    bool valid = false;\r
-    int result = 0;\r
-\r
-    bool negative = false;\r
-\r
-    for (int i = 0; i < fb.len; i++)\r
-    {\r
-        if (i == 0 && fb.ptr[i] == '-')\r
-        {\r
-            negative = true;\r
-            continue;\r
-        }\r
-\r
-        int val = fb.ptr[i] - '0';\r
-        if (val < 0 || val > 9)\r
-            return false;\r
-\r
-        result *= 10;\r
-        result += val;\r
-        valid = true;\r
-    }\r
-\r
-    *outInt = result;\r
-    return valid;\r
-}
\ No newline at end of file
diff --git a/src/fixedbuffer.h b/src/fixedbuffer.h
deleted file mode 100644 (file)
index d8055cb..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef FIXEDBUFFER__H\r
-#define FIXEDBUFFER__H\r
-\r
-#include <stdbool.h>\r
-\r
-typedef struct FixedBuffer {\r
-    char * ptr;\r
-    int cap;\r
-    int len;\r
-} FixedBuffer;\r
-\r
-FixedBuffer\r
-FixedBuf(const char * str);\r
-\r
-bool\r
-FixedBufferToInt(FixedBuffer fb, int * outInt);\r
-\r
-\r
-#endif
\ No newline at end of file
index 7cc4de9fe11d25a054d6e62a44563d3f97682b82..28e7634e9b367d4f9bd13a7934195167f6a5d910 100644 (file)
 #define LOGIN_RESPONSE_SIZE 1024\r
 #define LOGIN_URL "/_matrix/client/v3/login"\r
 \r
-#define ROOMEVENT_REQUEST_SIZE 1024\r
+#define ENCRYPTED_REQUEST_SIZE 512\r
+#define ENCRYPTED_EVENT_SIZE 1024\r
+#define ROOMEVENT_REQUEST_SIZE 256\r
 #define ROOMEVENT_RESPONSE_SIZE 1024\r
 #define ROOMEVENT_URL "/_matrix/client/v3/rooms/%s/send/%s/%d"\r
 \r
+#define TODEVICE_EVENT_SIZE 512\r
+#define TODEVICE_URL "/_matrix/client/v3/sendToDevice/%s/%d"\r
+\r
+#define KEYS_QUERY_URL "/_matrix/client/v3/keys/query"\r
+#define KEYS_QUERY_REQUEST_SIZE 256\r
+#define KEYS_QUERY_RESPONSE_SIZE 1024\r
+\r
+#define JSON_QUERY_SIZE 128\r
+\r
+\r
+\r
+void\r
+Randomize(uint8_t * random, int randomLen)\r
+{\r
+    static bool first = false;\r
+    if (first) { srand(time(0)); first = false; }\r
+\r
+    for (int i = 0; i < randomLen; i++)\r
+    {\r
+        random[i] = rand() % 256;\r
+    }\r
+}\r
+\r
+bool\r
+JsonEscape(\r
+    char * sIn, int sInLen,\r
+    char * sOut, int sOutCap)\r
+{\r
+    int sOutIndex = 0;\r
+\r
+    for (int i = 0; i < sInLen; i++)\r
+    {\r
+        if (i >= sOutCap)\r
+            return false;\r
+        \r
+        if (sIn[i] == '.' ||\r
+            sIn[i] == '[' ||\r
+            sIn[i] == ']'\r
+        ) {\r
+            sOut[sOutIndex++] = '\\';\r
+        }\r
+        sOut[sOutIndex++] = sIn[i];\r
+    }\r
+\r
+    if (sOutIndex < sOutCap)\r
+        sOut[sOutIndex] = '\0';\r
+\r
+    return true;\r
+}\r
+\r
+// TODO: in/outbound sessions\r
+bool\r
+MatrixOlmSessionInit(\r
+    MatrixOlmSession * session,\r
+    const char * deviceId)\r
+{\r
+    memset(session, 0, sizeof(MatrixOlmSession));\r
+\r
+    static uint8_t random[MEGOLM_INIT_RANDOM_SIZE];\r
+    Randomize(random, MEGOLM_INIT_RANDOM_SIZE);\r
+\r
+    session->deviceId = deviceId;\r
+\r
+    session->session =\r
+        olm_session(session->memory);\r
+\r
+    return session->session != NULL;\r
+}\r
+\r
+bool\r
+MatrixOlmSessionEncrypt(\r
+    MatrixOlmSession * session,\r
+    const char * plaintext,\r
+    char * outBuffer, int outBufferCap)\r
+{\r
+    static uint8_t random[OLM_ENCRYPT_RANDOM_SIZE];\r
+    Randomize(random, OLM_ENCRYPT_RANDOM_SIZE);\r
+\r
+    size_t res = olm_encrypt(session->session,\r
+        plaintext, strlen(plaintext),\r
+        random, OLM_ENCRYPT_RANDOM_SIZE,\r
+        outBuffer, outBufferCap);\r
+\r
+    return res != olm_error();\r
+}\r
+\r
+// https://matrix.org/docs/guides/end-to-end-encryption-implementation-guide#starting-a-megolm-session\r
+bool\r
+MatrixMegolmOutSessionInit(\r
+    MatrixMegolmOutSession * session,\r
+    const char * roomId)\r
+{\r
+    memset(session, 0, sizeof(MatrixMegolmOutSession));\r
+\r
+    static uint8_t random[MEGOLM_INIT_RANDOM_SIZE];\r
+    Randomize(random, MEGOLM_INIT_RANDOM_SIZE);\r
+\r
+    session->roomId = roomId;\r
+\r
+    session->session =\r
+        olm_outbound_group_session(session->memory);\r
+\r
+    olm_init_outbound_group_session(\r
+        session->session,\r
+        random,\r
+        MEGOLM_INIT_RANDOM_SIZE);\r
+\r
+    olm_outbound_group_session_id(session->session,\r
+        (uint8_t *)session->id,\r
+        MEGOLM_SESSION_ID_SIZE);\r
+        \r
+    olm_outbound_group_session_key(session->session,\r
+        (uint8_t *)session->key,\r
+        MEGOLM_SESSION_KEY_SIZE);\r
+    \r
+    return true;\r
+}\r
+\r
+bool\r
+MatrixMegolmOutSessionEncrypt(\r
+    MatrixMegolmOutSession * session,\r
+    const char * plaintext,\r
+    char * outBuffer, int outBufferCap)\r
+{\r
+    size_t res = olm_group_encrypt(session->session,\r
+        (uint8_t *)plaintext, strlen(plaintext),\r
+        (uint8_t *)outBuffer, outBufferCap);\r
+\r
+    return res != olm_error();\r
+}\r
+\r
+\r
 \r
 bool\r
 MatrixClientInit(\r
     MatrixClient * client,\r
     const char * server)\r
 {\r
-    strcpy_s(\r
-        client->server,\r
-        SERVER_SIZE,\r
-        server\r
-    );\r
+    memset(client, 0, sizeof(MatrixClient));\r
+\r
+    strcpy(client->server, server);\r
 \r
     return true;\r
 }\r
@@ -39,7 +171,7 @@ MatrixClientSetAccessToken(
         return false;\r
 \r
     for (int i = 0; i < accessTokenLen; i++)\r
-        client->accessTokenBuffer[i] = accessToken[i];\r
+        client->accessToken[i] = accessToken[i];\r
 \r
     return true;\r
 }\r
@@ -83,20 +215,21 @@ MatrixClientLoginPassword(
 \r
     mjson_get_string(responseBuffer, responseLen,\r
         "$.access_token",\r
-        client->accessTokenBuffer, ACCESS_TOKEN_SIZE);\r
+        client->accessToken, ACCESS_TOKEN_SIZE);\r
     mjson_get_string(responseBuffer, responseLen,\r
         "$.device_id",\r
-        client->deviceIdBuffer, DEVICE_ID_SIZE);\r
+        client->deviceId, DEVICE_ID_SIZE);\r
     mjson_get_string(responseBuffer, responseLen,\r
         "$.expires_in_ms",\r
-        client->expireMsBuffer, EXPIRE_MS_SIZE);\r
+        client->expireMs, EXPIRE_MS_SIZE);\r
     mjson_get_string(responseBuffer, responseLen,\r
         "$.refresh_token",\r
-        client->refreshTokenBuffer, REFRESH_TOKEN_SIZE);\r
+        client->refreshToken, REFRESH_TOKEN_SIZE);\r
 \r
     return true;\r
 }\r
-    \r
+\r
+// https://spec.matrix.org/v1.6/client-server-api/#put_matrixclientv3roomsroomidsendeventtypetxnid\r
 bool\r
 MatrixClientSendEvent(\r
     MatrixClient * client,\r
@@ -105,8 +238,8 @@ MatrixClientSendEvent(
     const char * msgBody)\r
 {    \r
     static char requestUrl[MAX_URL_LEN];\r
-    sprintf_s(requestUrl, MAX_URL_LEN,\r
-        ROOMEVENT_URL, roomId, msgType, time(NULL));\r
+    sprintf(requestUrl,\r
+        ROOMEVENT_URL, roomId, msgType, (int)time(NULL));\r
 \r
     static char responseBuffer[ROOMEVENT_RESPONSE_SIZE];\r
     bool result =\r
@@ -119,3 +252,408 @@ MatrixClientSendEvent(
     return result;\r
 }\r
 \r
+// https://spec.matrix.org/v1.6/client-server-api/#mroomencrypted\r
+// https://matrix.org/docs/guides/end-to-end-encryption-implementation-guide#sending-an-encrypted-message-event\r
+bool\r
+MatrixClientSendEventEncrypted(\r
+    MatrixClient * client,\r
+    const char * roomId,\r
+    const char * msgType,\r
+    const char * msgBody)\r
+{\r
+    // event json\r
+    static char requestBuffer[ROOMEVENT_REQUEST_SIZE];\r
+    sprintf(requestBuffer,\r
+        "{"\r
+        "\"type\":\"%s\","\r
+        "\"content\":%s,"\r
+        "\"room_id\":\"%s\""\r
+        "}",\r
+        msgType,\r
+        msgBody,\r
+        roomId);\r
+\r
+    // get megolm session\r
+    MatrixMegolmOutSession * outSession;\r
+    MatrixClientGetMegolmOutSession(client, roomId, &outSession);\r
+        \r
+    // encrypt\r
+    static char encryptedBuffer[ENCRYPTED_REQUEST_SIZE];\r
+    MatrixMegolmOutSessionEncrypt(outSession,\r
+        requestBuffer,\r
+        encryptedBuffer, ENCRYPTED_REQUEST_SIZE);\r
+\r
+    // encrypted event json\r
+    const char * senderKey = client->deviceKey;\r
+    const char * sessionId = outSession->id;\r
+    const char * deviceId = client->deviceId;\r
+\r
+    static char encryptedEventBuffer[ENCRYPTED_EVENT_SIZE];\r
+    sprintf(encryptedEventBuffer,\r
+        "{"\r
+        "\"algorithm\":\"m.megolm.v1.aes-sha2\","\r
+        "\"sender_key\":\"%s\","\r
+        "\"ciphertext\":\"%s\","\r
+        "\"session_id\":%s,"\r
+        "\"device_id\":\"%s\""\r
+        "}",\r
+        senderKey,\r
+        encryptedBuffer,\r
+        sessionId,\r
+        deviceId);\r
+\r
+    // send\r
+    return MatrixClientSendEvent(client,\r
+        roomId,\r
+        "m.room.encrypted",\r
+        encryptedEventBuffer);\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
+{\r
+    return\r
+        MatrixHttpGet(client,\r
+            "/_matrix/client/v3/sync",\r
+            outSyncBuffer, outSyncCap,\r
+            true);\r
+}\r
+\r
+bool\r
+MatrixClientShareMegolmOutSession(\r
+    MatrixClient * client,\r
+    const char * deviceId,\r
+    MatrixMegolmOutSession * session)\r
+{\r
+    // generate room key event\r
+    char eventBuffer[KEY_SHARE_EVENT_LEN];\r
+    sprintf(eventBuffer,\r
+        "{"\r
+            "\"algorithm\":\"m.megolm.v1.aes-sha2\","\r
+            "\"room_id\":\"%s\","\r
+            "\"session_id\":\"%s\","\r
+            "\"session_key\":\"%s\""\r
+        "}",\r
+        session->roomId,\r
+        session->id,\r
+        session->key\r
+    );\r
+\r
+    // get olm session\r
+    MatrixOlmSession * olmSession;\r
+    MatrixClientGetOlmSession(client, deviceId, &olmSession);\r
+\r
+    // encrypt\r
+    char encryptedBuffer[KEY_SHARE_EVENT_LEN];\r
+    MatrixOlmSessionEncrypt(olmSession,\r
+        eventBuffer,\r
+        encryptedBuffer, KEY_SHARE_EVENT_LEN);\r
+\r
+    // send\r
+    MatrixClientSendToDeviceEncrypted(client,\r
+        client->userId,\r
+        deviceId,\r
+        encryptedBuffer,\r
+        "m.room_key");\r
+\r
+    return true;\r
+}\r
+\r
+// bool\r
+// MatrixClientSetMegolmOutSession(\r
+//     MatrixClient * client,\r
+//     const char * roomId,\r
+//     MatrixMegolmOutSession session)\r
+// {\r
+//     if (client->numMegolmOutSessions < 10)\r
+//     {\r
+//         session.roomId = roomId;\r
+//         client->megolmOutSessions[client->numMegolmOutSessions] = session;\r
+//         client->numMegolmOutSessions++;\r
+\r
+//         return true;\r
+//     }\r
+//     return false;\r
+// }\r
+\r
+bool\r
+MatrixClientGetMegolmOutSession(\r
+    MatrixClient * client,\r
+    const char * roomId,\r
+    MatrixMegolmOutSession ** outSession)\r
+{\r
+    for (int i = 0; i < client->numMegolmOutSessions; i++)\r
+    {\r
+        if (strcmp(client->megolmOutSessions[i].roomId, roomId) == 0)\r
+        {\r
+            *outSession = &client->megolmOutSessions[i];\r
+            return true;\r
+        }\r
+    }\r
+\r
+    if (client->numMegolmOutSessions < NUM_MEGOLM_SESSIONS)\r
+    {\r
+        MatrixMegolmOutSessionInit(\r
+            &client->megolmOutSessions[client->numMegolmOutSessions],\r
+            roomId);\r
+\r
+        *outSession = &client->megolmOutSessions[client->numMegolmOutSessions];\r
+        \r
+        client->numMegolmOutSessions++;\r
+\r
+        return true;\r
+    }\r
+\r
+    return false;\r
+}\r
+\r
+bool\r
+MatrixClientGetOlmSession(\r
+    MatrixClient * client,\r
+    const char * deviceId,\r
+    MatrixOlmSession ** outSession)\r
+{\r
+    for (int i = 0; i < client->numOlmSessions; i++)\r
+    {\r
+        if (strcmp(client->olmSessions[i].deviceId, deviceId) == 0)\r
+        {\r
+            *outSession = &client->olmSessions[i];\r
+            return true;\r
+        }\r
+    }\r
+\r
+    if (client->numOlmSessions < NUM_OLM_SESSIONS)\r
+    {\r
+        MatrixOlmSessionInit(\r
+            &client->olmSessions[client->numOlmSessions],\r
+            deviceId);\r
+\r
+        *outSession = &client->olmSessions[client->numOlmSessions];\r
+        \r
+        client->numOlmSessions++;\r
+\r
+        return true;\r
+    }\r
+\r
+    return false;\r
+}\r
+\r
+// https://spec.matrix.org/v1.6/client-server-api/#put_matrixclientv3sendtodeviceeventtypetxnid\r
+bool\r
+MatrixClientSendToDevice(\r
+    MatrixClient * client,\r
+    const char * userId,\r
+    const char * deviceId,\r
+    const char * message,\r
+    const char * msgType)\r
+{\r
+    static char requestUrl[MAX_URL_LEN];\r
+    sprintf(requestUrl,\r
+        TODEVICE_URL, msgType, (int)time(NULL));\r
+\r
+    static char eventBuffer[TODEVICE_EVENT_SIZE];\r
+    snprintf(eventBuffer, TODEVICE_EVENT_SIZE,\r
+        "{"\r
+            "\"messages\": {"\r
+                "\"%s\": {"\r
+                    "\"%s\":%s"\r
+                "}"\r
+            "}"\r
+        "}",\r
+        userId,\r
+        deviceId,\r
+        message);\r
+\r
+    static char responseBuffer[ROOMEVENT_RESPONSE_SIZE];\r
+    bool result =\r
+        MatrixHttpPut(client,\r
+            requestUrl,\r
+            eventBuffer,\r
+            responseBuffer, ROOMEVENT_RESPONSE_SIZE,\r
+            true);\r
+    \r
+    return result;\r
+}\r
+\r
+bool\r
+MatrixClientSendToDeviceEncrypted(\r
+    MatrixClient * client,\r
+    const char * userId,\r
+    const char * deviceId,\r
+    const char * message,\r
+    const char * msgType)\r
+{\r
+    // get olm session\r
+    MatrixOlmSession * olmSession;\r
+    MatrixClientGetOlmSession(client, deviceId, &olmSession);\r
+\r
+    // create event json\r
+    char deviceKey[DEVICE_KEY_SIZE];\r
+    MatrixClientGetDeviceKey(client, deviceId, deviceKey, DEVICE_KEY_SIZE);\r
+    const char * senderKey = client->deviceKey;\r
+    \r
+    static char eventBuffer[TODEVICE_EVENT_SIZE];\r
+    sprintf(eventBuffer,\r
+        "{"\r
+        "\"type\": \"%s\","\r
+        "\"content\": \"%s\","\r
+        "\"sender\": \"%s\","\r
+        "\"recipient\": \"%s\","\r
+        "\"recipient_keys\": {"\r
+          "\"ed25519\": \"%s\""\r
+        "},"\r
+        "\"keys\": {"\r
+          "\"ed25519\": \"%s\""\r
+        "}"\r
+        "}",\r
+        msgType,\r
+        message,\r
+        client->userId,\r
+        userId, // recipient user id\r
+        deviceKey, // recipient device key\r
+        client->deviceKey);\r
+\r
+    // encrypt\r
+    static char encryptedBuffer[ENCRYPTED_REQUEST_SIZE];\r
+    MatrixOlmSessionEncrypt(olmSession,\r
+        eventBuffer,\r
+        encryptedBuffer, ENCRYPTED_REQUEST_SIZE);\r
+\r
+    static char encryptedEventBuffer[ENCRYPTED_EVENT_SIZE];\r
+    sprintf(encryptedEventBuffer,\r
+        "{"\r
+        "\"algorithm\":\"m.megolm.v1.aes-sha2\","\r
+        "\"sender_key\":\"%s\","\r
+        "\"ciphertext\":{"\r
+          "\"%s\":{"\r
+            "\"body\":\"%s\","\r
+            "\"type\":\"%d\""\r
+          "}"\r
+        "}"\r
+        "}",\r
+        senderKey,\r
+        deviceKey,\r
+        encryptedBuffer,\r
+        olmSession->type);\r
+\r
+    // send\r
+    return MatrixClientSendToDevice(\r
+        client,\r
+        userId,\r
+        deviceId,\r
+        encryptedEventBuffer,\r
+        "m.room.encrypted");\r
+}\r
+\r
+bool\r
+MatrixClientFindDevice(\r
+    MatrixClient * client,\r
+    const char * deviceId,\r
+    MatrixDevice ** outDevice)\r
+{\r
+    MatrixClientRequestDeviceKeys(client);\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
+    *outDevice = NULL;\r
+    return false;\r
+}\r
+\r
+bool\r
+MatrixClientGetDeviceKey(\r
+    MatrixClient * client,\r
+    const char * deviceId,\r
+    char * outDeviceKey, int outDeviceKeyCap)\r
+{\r
+    MatrixClientRequestDeviceKeys(client);\r
+    \r
+    MatrixDevice * device;\r
+    if (MatrixClientFindDevice(client, deviceId, &device))\r
+    {\r
+        strncpy(outDeviceKey, device->deviceKey, outDeviceKeyCap);\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
+    MatrixClient * client)\r
+{\r
+    char userIdEscaped[USER_ID_SIZE];\r
+    JsonEscape(client->userId, strlen(client->userId),\r
+        userIdEscaped, USER_ID_SIZE);\r
+\r
+    char request[KEYS_QUERY_REQUEST_SIZE];\r
+    snprintf(request, KEYS_QUERY_REQUEST_SIZE,\r
+        "{\"device_keys\":{\"%s\":[]}}", userIdEscaped);\r
+\r
+    char responseBuffer[KEYS_QUERY_RESPONSE_SIZE];\r
+    bool requestResult = MatrixHttpPost(client,\r
+        KEYS_QUERY_URL,\r
+        request,\r
+        responseBuffer, KEYS_QUERY_RESPONSE_SIZE,\r
+        true);\r
+\r
+    if (requestResult)\r
+    {\r
+        // query for retrieving device keys for user id\r
+        char query[JSON_QUERY_SIZE];\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;\r
+        for (off = 0; (off = mjson_next(s, slen, off, &koff, &klen,\r
+                                        &voff, &vlen, &vtype)) != 0; ) {\r
+            const char * key = s + koff;\r
+            const char * val = s + voff;\r
+\r
+            // set device id, "key" is the JSON key\r
+            MatrixDevice d;\r
+            strncpy(d.deviceId, key, klen);\r
+\r
+            // look for device key in value\r
+            char deviceKeyQuery[JSON_QUERY_SIZE];\r
+            snprintf(deviceKeyQuery, JSON_QUERY_SIZE,\r
+                "$.keys.curve25519:%.*s", klen, key);\r
+            mjson_get_string(val, vlen,\r
+                deviceKeyQuery, d.deviceKey, DEVICE_KEY_SIZE);\r
+\r
+            // add device\r
+            if (client->numDevices < NUM_DEVICES)\r
+            {\r
+                client->devices[client->numDevices] = d;\r
+                client->numDevices++;\r
+            }\r
+            else\r
+            {\r
+                return false;\r
+            }\r
+        }\r
+\r
+        return true;\r
+    }\r
+    else\r
+    {\r
+        return false;\r
+    }\r
+}\r
index 2e7f6e0e9a6861fa70839a18b2c8dd8ca27b19b4..6538e12d7fa6f16a464d388e11fa41edc18dc44f 100644 (file)
@@ -2,13 +2,15 @@
 #define MATRIX__H\r
 \r
 #include <stdbool.h>\r
+#include <stdlib.h>\r
 #include <string.h>\r
+#include <time.h>\r
 \r
 #include <olm/olm.h>\r
 \r
 \r
 \r
-// TODO: fix\r
+#define USER_ID_SIZE 64\r
 #define SERVER_SIZE 20\r
 #define ACCESS_TOKEN_SIZE 40\r
 #define DEVICE_ID_SIZE 20\r
 #define REFRESH_TOKEN_SIZE 20\r
 #define MAX_URL_LEN 128\r
 \r
+#define DEVICE_KEY_SIZE 20\r
+\r
+#define KEY_SHARE_EVENT_LEN 1024\r
+\r
+#define OLM_SESSION_MEMORY_SIZE 3352\r
+#define OLM_ENCRYPT_RANDOM_SIZE 32\r
+\r
+#define MEGOLM_OUTBOUND_SESSION_MEMORY_SIZE 232\r
+#define MEGOLM_SESSION_ID_SIZE 44\r
+#define MEGOLM_SESSION_KEY_SIZE 306\r
+#define MEGOLM_INIT_RANDOM_SIZE (4*32 + 32)\r
+\r
+#define NUM_MEGOLM_SESSIONS 10\r
+#define NUM_OLM_SESSIONS 10\r
+#define NUM_DEVICES 10\r
+\r
+void\r
+Randomize(uint8_t * random, int randomLen);\r
+\r
+bool\r
+JsonEscape(\r
+    char * sIn, int sInLen,\r
+    char * sOut, int sOutCap);\r
+\r
+typedef struct MatrixDevice {\r
+    char deviceId[DEVICE_ID_SIZE];\r
+    char deviceKey[DEVICE_KEY_SIZE];\r
+} MatrixDevice;\r
+\r
+typedef struct MatrixOlmSession {\r
+    const char * deviceId;\r
+\r
+    int type;\r
+    OlmSession * session;\r
+    char memory[OLM_SESSION_MEMORY_SIZE];\r
+} MatrixOlmSession;\r
+\r
+bool\r
+MatrixOlmSessionInit(\r
+    MatrixOlmSession * session,\r
+    const char * deviceId);\r
+\r
+bool\r
+MatrixOlmSessionEncrypt(\r
+    MatrixOlmSession * session,\r
+    const char * plaintext,\r
+    char * outBuffer, int outBufferCap);\r
+\r
+\r
+\r
+typedef struct MatrixMegolmInSession {\r
+    OlmInboundGroupSession * session;\r
+} MatrixMegolmInSession;\r
+\r
+typedef struct MatrixMegolmOutSession {\r
+    const char * roomId;\r
+\r
+    OlmOutboundGroupSession * session;\r
+    char memory[MEGOLM_OUTBOUND_SESSION_MEMORY_SIZE];\r
+\r
+    char id[MEGOLM_SESSION_ID_SIZE];\r
+    char key[MEGOLM_SESSION_KEY_SIZE];\r
+} MatrixMegolmOutSession;\r
+\r
+bool\r
+MatrixMegolmOutSessionInit(\r
+    MatrixMegolmOutSession * session,\r
+    const char * roomId);\r
+    \r
+bool\r
+MatrixMegolmOutSessionEncrypt(\r
+    MatrixMegolmOutSession * session,\r
+    const char * plaintext,\r
+    char * outBuffer, int outBufferCap);\r
+\r
+\r
 \r
 typedef struct MatrixClient {\r
     OlmAccount * olmAccount;\r
     OlmSession * olmSession;\r
+\r
+    MatrixMegolmInSession megolmInSessions[NUM_MEGOLM_SESSIONS];\r
+    int numMegolmInSessions;\r
+    MatrixMegolmOutSession megolmOutSessions[NUM_MEGOLM_SESSIONS];\r
+    int numMegolmOutSessions;\r
+    MatrixOlmSession olmSessions[NUM_OLM_SESSIONS];\r
+    int numOlmSessions;\r
     \r
-    char server[SERVER_SIZE+1];\r
-    char accessTokenBuffer[ACCESS_TOKEN_SIZE];\r
-    char deviceIdBuffer[DEVICE_ID_SIZE];\r
-    char expireMsBuffer[EXPIRE_MS_SIZE];\r
-    char refreshTokenBuffer[REFRESH_TOKEN_SIZE];\r
+    MatrixDevice devices[NUM_DEVICES];\r
+    int numDevices;\r
+    \r
+    char deviceKey[DEVICE_KEY_SIZE];\r
+\r
+    char userId[USER_ID_SIZE];\r
+    char server[SERVER_SIZE];\r
+    char accessToken[ACCESS_TOKEN_SIZE];\r
+    char deviceId[DEVICE_ID_SIZE];\r
+    char expireMs[EXPIRE_MS_SIZE];\r
+    char refreshToken[REFRESH_TOKEN_SIZE];\r
 \r
     void * httpUserData;\r
 } MatrixClient;\r
@@ -53,6 +144,76 @@ MatrixClientSendEvent(
     const char * roomId,\r
     const char * msgType,\r
     const char * msgBody);\r
+    \r
+bool\r
+MatrixClientSendEventEncrypted(\r
+    MatrixClient * client,\r
+    const char * roomId,\r
+    const char * msgType,\r
+    const char * msgBody);\r
+\r
+bool\r
+MatrixClientSync(\r
+    MatrixClient * client,\r
+    char * outSyncBuffer, int outSyncCap);\r
+\r
+bool\r
+MatrixClientShareMegolmOutSession(\r
+    MatrixClient * client,\r
+    const char * deviceId,\r
+    MatrixMegolmOutSession * session);\r
+\r
+bool\r
+MatrixClientGetMegolmOutSession(\r
+    MatrixClient * client,\r
+    const char * roomId,\r
+    MatrixMegolmOutSession ** outSession);\r
+\r
+bool\r
+MatrixClientSetMegolmOutSession(\r
+    MatrixClient * client,\r
+    const char * roomId,\r
+    MatrixMegolmOutSession session);\r
+\r
+bool\r
+MatrixClientGetOlmSession(\r
+    MatrixClient * client,\r
+    const char * deviceId,\r
+    MatrixOlmSession ** outSession);\r
+\r
+bool\r
+MatrixClientSendToDevice(\r
+    MatrixClient * client,\r
+    const char * userId,\r
+    const char * deviceId,\r
+    const char * message,\r
+    const char * msgType);\r
+\r
+bool\r
+MatrixClientSendToDeviceEncrypted(\r
+    MatrixClient * client,\r
+    const char * userId,\r
+    const char * deviceId,\r
+    const char * message,\r
+    const char * msgType);\r
+\r
+bool\r
+MatrixClientGetDeviceKey(\r
+    MatrixClient * client,\r
+    const char * deviceId,\r
+    char * outDeviceKey, int outDeviceKeyCap);\r
+\r
+bool\r
+MatrixClientGetDeviceKey(\r
+    MatrixClient * client,\r
+    const char * deviceId,\r
+    char * outDeviceKey, int outDeviceKeyCap);\r
+\r
+bool\r
+MatrixClientRequestDeviceKeys(\r
+    MatrixClient * client);\r
+\r
+\r
 \r
 bool\r
 MatrixHttpInit(\r
index 8d575e5213bf37407c5590a85c03ce72123e74be..020b4c8cf638256edc9db6a521b454f7c71c4f98 100644 (file)
@@ -1,4 +1,5 @@
 #include "matrix.h"\r
+\r
 #include <stdio.h>\r
 #include <stdlib.h>\r
 #include <string.h>\r
@@ -35,7 +36,8 @@ MatrixHttpCallback(
         // If s_url is https://, tell client connection to use TLS\r
         if (mg_url_is_ssl(client->server))\r
         {\r
-            struct mg_tls_opts opts = {.srvname = host};\r
+            struct mg_tls_opts opts;\r
+            opts.srvname = host;\r
             mg_tls_init(c, &opts);\r
         }\r
 \r
@@ -48,10 +50,13 @@ MatrixHttpCallback(
         struct mg_http_message *hm = (struct mg_http_message *)ev_data;\r
         // memcpy_s(client->data, 1024, hm->message.ptr, hm->message.len);\r
         // client->dataLen = hm->message.len;\r
-        memcpy_s(conn->data, conn->dataCap, hm->body.ptr, hm->body.len);\r
+        memcpy(conn->data, hm->body.ptr, hm->body.len);\r
+        // memcpy_s(conn->data, conn->dataCap, hm->body.ptr, hm->body.len);\r
         conn->data[hm->body.len] = '\0';\r
         conn->dataLen = hm->body.len;\r
         conn->dataReceived = true;\r
+\r
+        printf("received[%d]:\n%.*s\n", conn->dataLen, conn->dataLen, conn->data);\r
     }\r
 }\r
 \r
@@ -100,10 +105,14 @@ MatrixHttpGet(
 \r
     struct mg_str host = mg_url_host(client->server);\r
 \r
-    static char authorizationHeader[AUTHORIZATION_HEADER_LEN] = "\0";\r
+    static char authorizationHeader[AUTHORIZATION_HEADER_LEN];\r
     if (authenticated)\r
-        sprintf_s(authorizationHeader, AUTHORIZATION_HEADER_LEN,\r
-            "Authorization: Bearer %s\r\n", client->accessTokenBuffer);\r
+        sprintf(authorizationHeader,\r
+            "Authorization: Bearer %s\r\n", client->accessToken);\r
+        // sprintf_s(authorizationHeader, AUTHORIZATION_HEADER_LEN,\r
+        //     "Authorization: Bearer %s\r\n", client->accessToken);\r
+    else\r
+        authorizationHeader[0] = '\0';\r
 \r
     mg_printf(conn->connection,\r
         "GET %s HTTP/1.1\r\n"\r
@@ -137,10 +146,12 @@ MatrixHttpPost(
 \r
     struct mg_str host = mg_url_host(client->server);\r
 \r
-    static char authorizationHeader[AUTHORIZATION_HEADER_LEN] = "\0";\r
+    static char authorizationHeader[AUTHORIZATION_HEADER_LEN];\r
     if (authenticated)\r
-        sprintf_s(authorizationHeader, AUTHORIZATION_HEADER_LEN,\r
-            "Authorization: Bearer %s\r\n", client->accessTokenBuffer);\r
+        sprintf(authorizationHeader,\r
+            "Authorization: Bearer %s\r\n", client->accessToken);\r
+    else\r
+        authorizationHeader[0] = '\0';\r
 \r
     mg_printf(conn->connection,\r
             "POST %s HTTP/1.0\r\n"\r
@@ -182,8 +193,8 @@ MatrixHttpPut(
 \r
     static char authorizationHeader[AUTHORIZATION_HEADER_LEN];\r
     if (authenticated)\r
-        sprintf_s(authorizationHeader, AUTHORIZATION_HEADER_LEN,\r
-            "Authorization: Bearer %s\r\n", client->accessTokenBuffer);\r
+        sprintf(authorizationHeader,\r
+            "Authorization: Bearer %s\r\n", client->accessToken);\r
     else\r
         authorizationHeader[0] = '\0';\r
 \r