8 #define LOGIN_REQUEST_SIZE 1024
\r
9 #define LOGIN_RESPONSE_SIZE 1024
\r
10 #define LOGIN_URL "/_matrix/client/v3/login"
\r
12 #define ENCRYPTED_REQUEST_SIZE (1024*5)
\r
13 #define ENCRYPTED_EVENT_SIZE (1024*10)
\r
14 #define ROOMEVENT_REQUEST_SIZE 256
\r
15 #define ROOMEVENT_RESPONSE_SIZE 1024
\r
16 #define ROOMEVENT_URL "/_matrix/client/v3/rooms/%s/send/%s/%d"
\r
18 #define TODEVICE_EVENT_SIZE (1024*5)
\r
19 #define TODEVICE_URL "/_matrix/client/v3/sendToDevice/%s/%d"
\r
21 #define KEYS_QUERY_URL "/_matrix/client/v3/keys/query"
\r
22 #define KEYS_QUERY_REQUEST_SIZE 256
\r
23 #define KEYS_QUERY_RESPONSE_SIZE (1024*10)
\r
25 #define KEYS_UPLOAD_URL "/_matrix/client/v3/keys/upload"
\r
26 #define KEYS_UPLOAD_REQUEST_SIZE 1024
\r
27 #define KEYS_UPLOAD_REQUEST_SIGNED_SIZE 2048
\r
28 #define KEYS_UPLOAD_RESPONSE_SIZE 2048
\r
30 #define KEYS_CLAIM_URL "/_matrix/client/v3/keys/claim"
\r
31 #define KEYS_CLAIM_REQUEST_SIZE 1024
\r
32 #define KEYS_CLAIM_RESPONSE_SIZE 1024
\r
34 #define JSON_QUERY_SIZE 128
\r
43 static bool first = true;
\r
44 if (first) { srand(time(0)); first = false; }
\r
46 for (int i = 0; i < randomLen; i++)
\r
48 random[i] = rand() % 256;
\r
54 const char * sIn, int sInLen,
\r
55 char * sOut, int sOutCap)
\r
59 for (int i = 0; i < sInLen; i++)
\r
64 if (sIn[i] == '.' ||
\r
68 sOut[sOutIndex++] = '\\';
\r
70 sOut[sOutIndex++] = sIn[i];
\r
73 if (sOutIndex < sOutCap)
\r
74 sOut[sOutIndex] = '\0';
\r
80 MatrixClient * client,
\r
81 const char * sIn, int sInLen,
\r
82 char * sOut, int sOutCap)
\r
84 static char signature[OLM_SIGNATURE_SIZE];
\r
86 olm_account_sign(client->olmAccount.account,
\r
88 signature, OLM_SIGNATURE_SIZE);
\r
90 int signatureLen = res;
\r
92 static char signatureJson[JSON_SIGNATURE_SIZE];
\r
93 int signatureJsonLen =
\r
94 mjson_snprintf(signatureJson, JSON_SIGNATURE_SIZE,
\r
98 "\"ed25519:%s\":\"%.*s\""
\r
104 signatureLen, signature);
\r
106 struct mjson_fixedbuf result = { sOut, sOutCap, 0 };
\r
109 signatureJson, signatureJsonLen,
\r
110 mjson_print_fixed_buf,
\r
118 MatrixOlmAccountInit(
\r
119 MatrixOlmAccount * account)
\r
121 account->account = olm_account(account->memory);
\r
123 static uint8_t random[OLM_ACCOUNT_RANDOM_SIZE];
\r
124 Randomize(random, OLM_ACCOUNT_RANDOM_SIZE);
\r
126 size_t res = olm_create_account(
\r
129 OLM_ACCOUNT_RANDOM_SIZE);
\r
131 return res != olm_error();
\r
134 // TODO: in/outbound sessions
\r
136 MatrixOlmSessionTo(
\r
137 MatrixOlmSession * session,
\r
138 OlmAccount * olmAccount,
\r
139 const char * deviceId,
\r
140 const char * deviceKey,
\r
141 const char * deviceOnetimeKey)
\r
143 memset(session, 0, sizeof(MatrixOlmSession));
\r
145 session->deviceId = deviceId;
\r
148 olm_session(session->memory);
\r
150 static uint8_t random[OLM_OUTBOUND_SESSION_RANDOM_SIZE];
\r
151 Randomize(random, OLM_OUTBOUND_SESSION_RANDOM_SIZE);
\r
154 olm_create_outbound_session(session->session,
\r
156 deviceKey, strlen(deviceKey),
\r
157 deviceOnetimeKey, strlen(deviceOnetimeKey),
\r
158 random, OLM_OUTBOUND_SESSION_RANDOM_SIZE);
\r
160 if (res == olm_error()) {
\r
161 printf("error olm: %s\n", olm_account_last_error(olmAccount));
\r
164 return session->session != NULL;
\r
168 MatrixOlmSessionEncrypt(
\r
169 MatrixOlmSession * session,
\r
170 const char * plaintext,
\r
171 char * outBuffer, int outBufferCap)
\r
173 static uint8_t random[OLM_ENCRYPT_RANDOM_SIZE];
\r
174 Randomize(random, OLM_ENCRYPT_RANDOM_SIZE);
\r
176 size_t res = olm_encrypt(session->session,
\r
177 plaintext, strlen(plaintext),
\r
178 random, OLM_ENCRYPT_RANDOM_SIZE,
\r
179 outBuffer, outBufferCap);
\r
181 return res != olm_error();
\r
184 // https://matrix.org/docs/guides/end-to-end-encryption-implementation-guide#starting-a-megolm-session
\r
186 MatrixMegolmOutSessionInit(
\r
187 MatrixMegolmOutSession * session,
\r
188 const char * roomId)
\r
190 memset(session, 0, sizeof(MatrixMegolmOutSession));
\r
192 static uint8_t random[MEGOLM_INIT_RANDOM_SIZE];
\r
193 Randomize(random, MEGOLM_INIT_RANDOM_SIZE);
\r
195 strncpy(session->roomId, roomId, ROOM_ID_SIZE);
\r
198 olm_outbound_group_session(session->memory);
\r
200 olm_init_outbound_group_session(
\r
203 MEGOLM_INIT_RANDOM_SIZE);
\r
205 olm_outbound_group_session_id(session->session,
\r
206 (uint8_t *)session->id,
\r
207 MEGOLM_SESSION_ID_SIZE);
\r
209 olm_outbound_group_session_key(session->session,
\r
210 (uint8_t *)session->key,
\r
211 MEGOLM_SESSION_KEY_SIZE);
\r
217 MatrixMegolmOutSessionEncrypt(
\r
218 MatrixMegolmOutSession * session,
\r
219 const char * plaintext,
\r
220 char * outBuffer, int outBufferCap)
\r
222 size_t res = olm_group_encrypt(session->session,
\r
223 (uint8_t *)plaintext, strlen(plaintext),
\r
224 (uint8_t *)outBuffer, outBufferCap);
\r
226 return res != olm_error();
\r
230 MatrixMegolmOutSessionSave(
\r
231 MatrixMegolmOutSession * session,
\r
232 const char * filename,
\r
235 FILE * f = fopen(filename, "w");
\r
237 size_t roomIdLen = strlen(session->roomId);
\r
238 fwrite(&roomIdLen, sizeof(size_t), 1, f);
\r
239 fwrite(session->roomId, 1, roomIdLen, f);
\r
241 size_t pickleBufferLen =
\r
242 olm_pickle_outbound_group_session_length(
\r
244 void * pickleBuffer = malloc(pickleBufferLen);
\r
246 olm_pickle_outbound_group_session(
\r
249 pickleBuffer, pickleBufferLen);
\r
251 fwrite(&pickleBufferLen, sizeof(size_t), 1, f);
\r
252 fwrite(pickleBuffer, 1, pickleBufferLen, f);
\r
253 free(pickleBuffer);
\r
261 MatrixMegolmOutSessionLoad(
\r
262 MatrixMegolmOutSession * session,
\r
263 const char * filename,
\r
266 FILE * f = fopen(filename, "r");
\r
269 fread(&roomIdLen, sizeof(size_t), 1, f);
\r
270 fread(session->roomId, 1, roomIdLen, f);
\r
271 for (int i = roomIdLen; i < ROOM_ID_SIZE; i++)
\r
272 session->roomId[i] = '\0';
\r
274 size_t pickleBufferLen;
\r
275 fread(&pickleBufferLen, sizeof(size_t), 1, f);
\r
277 void * pickleBuffer = malloc(pickleBufferLen);
\r
278 fread(pickleBuffer, 1, pickleBufferLen, f);
\r
280 olm_unpickle_outbound_group_session(
\r
283 pickleBuffer, pickleBufferLen);
\r
285 free(pickleBuffer);
\r
287 olm_outbound_group_session_id(session->session, (uint8_t *)session->id, MEGOLM_SESSION_ID_SIZE);
\r
288 olm_outbound_group_session_key(session->session, (uint8_t *)session->key, MEGOLM_SESSION_KEY_SIZE);
\r
299 MatrixClient * client,
\r
300 const char * server)
\r
302 memset(client, 0, sizeof(MatrixClient));
\r
304 strcpy(client->server, server);
\r
306 // init olm account
\r
307 MatrixOlmAccountInit(&client->olmAccount);
\r
310 static char deviceKeysJson[OLM_IDENTITY_KEYS_JSON_SIZE];
\r
312 olm_account_identity_keys(
\r
313 client->olmAccount.account,
\r
315 OLM_IDENTITY_KEYS_JSON_SIZE);
\r
317 mjson_get_string(deviceKeysJson, res,
\r
319 client->deviceKey, DEVICE_KEY_SIZE);
\r
320 mjson_get_string(deviceKeysJson, res,
\r
322 client->signingKey, SIGNING_KEY_SIZE);
\r
329 MatrixClient * client,
\r
330 const char * filename)
\r
332 FILE * f = fopen(filename, "w");
\r
334 fwrite(client->deviceKey, 1, DEVICE_KEY_SIZE, f);
\r
335 fwrite(client->signingKey, 1, DEVICE_KEY_SIZE, f);
\r
336 fwrite(client->userId, 1, USER_ID_SIZE, f);
\r
337 fwrite(client->server, 1, SERVER_SIZE, f);
\r
338 fwrite(client->accessToken, 1, ACCESS_TOKEN_SIZE, f);
\r
339 fwrite(client->deviceId, 1, DEVICE_ID_SIZE, f);
\r
340 fwrite(client->expireMs, 1, EXPIRE_MS_SIZE, f);
\r
341 fwrite(client->refreshToken, 1, REFRESH_TOKEN_SIZE, f);
\r
343 fwrite(&client->numDevices, sizeof(int), 1, f);
\r
344 for (int i = 0; i < client->numDevices; i++) {
\r
345 fwrite(client->devices[i].deviceId, 1, DEVICE_ID_SIZE, f);
\r
346 fwrite(client->devices[i].deviceKey, 1, DEVICE_KEY_SIZE, f);
\r
355 MatrixClient * client,
\r
356 const char * filename)
\r
358 FILE * f = fopen(filename, "r");
\r
360 fread(client->deviceKey, 1, DEVICE_KEY_SIZE, f);
\r
361 fread(client->signingKey, 1, DEVICE_KEY_SIZE, f);
\r
362 fread(client->userId, 1, USER_ID_SIZE, f);
\r
363 fread(client->server, 1, SERVER_SIZE, f);
\r
364 fread(client->accessToken, 1, ACCESS_TOKEN_SIZE, f);
\r
365 fread(client->deviceId, 1, DEVICE_ID_SIZE, f);
\r
366 fread(client->expireMs, 1, EXPIRE_MS_SIZE, f);
\r
367 fread(client->refreshToken, 1, REFRESH_TOKEN_SIZE, f);
\r
369 fread(&client->numDevices, sizeof(int), 1, f);
\r
370 for (int i = 0; i < client->numDevices; i++) {
\r
371 fread(client->devices[i].deviceId, 1, DEVICE_ID_SIZE, f);
\r
372 fread(client->devices[i].deviceKey, 1, DEVICE_KEY_SIZE, f);
\r
380 MatrixClientSetAccessToken(
\r
381 MatrixClient * client,
\r
382 const char * accessToken)
\r
384 for (int i = 0; i < ACCESS_TOKEN_SIZE-1; i++)
\r
385 client->accessToken[i] = accessToken[i];
\r
386 client->accessToken[ACCESS_TOKEN_SIZE-1] = '\0';
\r
392 MatrixClientSetDeviceId(
\r
393 MatrixClient * client,
\r
394 const char * deviceId)
\r
396 for (int i = 0; i < DEVICE_ID_SIZE-1; i++)
\r
397 client->deviceId[i] = deviceId[i];
\r
398 client->deviceId[DEVICE_ID_SIZE-1] = '\0';
\r
404 MatrixClientSetUserId(
\r
405 MatrixClient * client,
\r
406 const char * userId)
\r
408 for (int i = 0; i < USER_ID_SIZE-1; i++)
\r
409 client->userId[i] = userId[i];
\r
410 client->userId[USER_ID_SIZE-1] = '\0';
\r
416 MatrixClientGenerateOnetimeKeys(
\r
417 MatrixClient * client,
\r
420 static uint8_t random[OLM_ONETIME_KEYS_RANDOM_SIZE];
\r
421 Randomize(random, OLM_ONETIME_KEYS_RANDOM_SIZE);
\r
424 olm_account_generate_one_time_keys(client->olmAccount.account,
\r
425 numberOfKeys, random, OLM_ONETIME_KEYS_RANDOM_SIZE);
\r
427 return res != olm_error();
\r
430 // https://spec.matrix.org/v1.7/client-server-api/#post_matrixclientv3keysupload
\r
432 MatrixClientUploadOnetimeKeys(
\r
433 MatrixClient * client)
\r
435 static char requestBuffer[KEYS_UPLOAD_REQUEST_SIZE];
\r
437 mjson_snprintf(requestBuffer, KEYS_UPLOAD_REQUEST_SIZE,
\r
438 "{\"one_time_keys\":{");
\r
440 static char onetimeKeysBuffer[1024];
\r
441 olm_account_one_time_keys(client->olmAccount.account,
\r
442 onetimeKeysBuffer, 1024);
\r
446 mjson_find(onetimeKeysBuffer, strlen(onetimeKeysBuffer), "$.curve25519", &keys, &keysLen);
\r
448 int koff, klen, voff, vlen, vtype, off = 0;
\r
449 while ((off = mjson_next(keys, keysLen, off, &koff, &klen, &voff, &vlen, &vtype)) != 0) {
\r
450 static char keyJson[JSON_ONETIME_KEY_SIZE];
\r
452 snprintf(keyJson, JSON_ONETIME_KEY_SIZE,
\r
453 "{\"key\":\"%.*s\"}",
\r
454 vlen-2, keys + voff+1);
\r
456 static char keyJsonSigned[JSON_ONETIME_KEY_SIGNED_SIZE];
\r
459 keyJson, JSON_ONETIME_KEY_SIZE,
\r
460 keyJsonSigned, JSON_ONETIME_KEY_SIGNED_SIZE);
\r
462 mjson_snprintf(requestBuffer+strlen(requestBuffer), KEYS_UPLOAD_REQUEST_SIZE-strlen(requestBuffer),
\r
463 "\"signed_curve25519:%.*s\":%s,",
\r
464 klen-2, keys + koff+1,
\r
468 mjson_snprintf(requestBuffer+strlen(requestBuffer)-1, KEYS_UPLOAD_REQUEST_SIZE-strlen(requestBuffer),
\r
471 static char responseBuffer[KEYS_UPLOAD_RESPONSE_SIZE];
\r
472 MatrixHttpPost(client,
\r
475 responseBuffer, KEYS_UPLOAD_RESPONSE_SIZE,
\r
481 // https://spec.matrix.org/v1.7/client-server-api/#post_matrixclientv3keysupload
\r
483 MatrixClientUploadDeviceKeys(
\r
484 MatrixClient * client)
\r
486 static char deviceKeysBuffer[KEYS_UPLOAD_REQUEST_SIZE];
\r
488 mjson_snprintf(deviceKeysBuffer, KEYS_UPLOAD_REQUEST_SIZE,
\r
489 "{\"device_keys\":{"
\r
490 "\"algorithms\":[\"m.olm.v1.curve25519-aes-sha2\",\"m.megolm.v1.aes-sha2\"],"
\r
491 "\"device_id\":\"%s\","
\r
493 "\"curve25519:%s\":\"%s\","
\r
494 "\"ed25519:%s\":\"%s\""
\r
496 "\"user_id\":\"%s\""
\r
499 client->deviceId, client->deviceKey,
\r
500 client->deviceId, client->signingKey,
\r
503 static char deviceKeysSignedBuffer[KEYS_UPLOAD_REQUEST_SIGNED_SIZE];
\r
505 deviceKeysBuffer, KEYS_UPLOAD_REQUEST_SIZE,
\r
506 deviceKeysSignedBuffer, KEYS_UPLOAD_REQUEST_SIZE);
\r
509 static char responseBuffer[KEYS_UPLOAD_RESPONSE_SIZE];
\r
510 MatrixHttpPost(client,
\r
512 deviceKeysSignedBuffer,
\r
513 responseBuffer, KEYS_UPLOAD_RESPONSE_SIZE,
\r
519 // https://spec.matrix.org/v1.7/client-server-api/#post_matrixclientv3keysclaim
\r
521 MatrixClientClaimOnetimeKey(
\r
522 MatrixClient * client,
\r
523 const char * userId,
\r
524 const char * deviceId,
\r
525 char * outOnetimeKey, int outOnetimeKeyCap)
\r
527 static char requestBuffer[KEYS_CLAIM_REQUEST_SIZE];
\r
528 mjson_snprintf(requestBuffer, KEYS_CLAIM_REQUEST_SIZE,
\r
530 "\"one_time_keys\": {"
\r
532 "\"%s\": \"signed_curve25519\""
\r
535 "\"timeout\": 10000"
\r
540 static char responseBuffer[KEYS_CLAIM_RESPONSE_SIZE];
\r
541 MatrixHttpPost(client,
\r
544 responseBuffer, KEYS_CLAIM_RESPONSE_SIZE,
\r
547 char userIdEscaped[USER_ID_SIZE];
\r
548 JsonEscape(userId, strlen(userId),
\r
549 userIdEscaped, USER_ID_SIZE);
\r
551 static char query[JSON_QUERY_SIZE];
\r
552 snprintf(query, JSON_QUERY_SIZE,
\r
553 "$.one_time_keys.%s.%s",
\r
557 const char * keyObject;
\r
559 mjson_find(responseBuffer, strlen(responseBuffer),
\r
561 &keyObject, &keyObjectSize);
\r
563 int koff, klen, voff, vlen, vtype;
\r
564 mjson_next(keyObject, keyObjectSize, 0,
\r
565 &koff, &klen, &voff, &vlen, &vtype);
\r
567 mjson_get_string(keyObject + voff, vlen,
\r
568 "$.key", outOnetimeKey, outOnetimeKeyCap);
\r
570 // TODO: verify signature
\r
575 // https://spec.matrix.org/v1.6/client-server-api/#post_matrixclientv3login
\r
577 MatrixClientLoginPassword(
\r
578 MatrixClient * client,
\r
579 const char * username,
\r
580 const char * password,
\r
581 const char * displayName)
\r
583 static char requestBuffer[LOGIN_REQUEST_SIZE];
\r
585 mjson_snprintf(requestBuffer, LOGIN_REQUEST_SIZE,
\r
587 "\"type\": \"m.login.password\","
\r
588 "\"identifier\": {"
\r
589 "\"type\": \"m.id.user\","
\r
592 "\"password\": \"%s\","
\r
593 "\"initial_device_display_name\": \"%s\""
\r
599 static char responseBuffer[LOGIN_RESPONSE_SIZE];
\r
601 MatrixHttpPost(client,
\r
604 responseBuffer, LOGIN_RESPONSE_SIZE,
\r
607 int responseLen = strlen(responseBuffer);
\r
612 mjson_get_string(responseBuffer, responseLen,
\r
614 client->accessToken, ACCESS_TOKEN_SIZE);
\r
615 mjson_get_string(responseBuffer, responseLen,
\r
617 client->deviceId, DEVICE_ID_SIZE);
\r
618 mjson_get_string(responseBuffer, responseLen,
\r
620 client->expireMs, EXPIRE_MS_SIZE);
\r
621 mjson_get_string(responseBuffer, responseLen,
\r
623 client->refreshToken, REFRESH_TOKEN_SIZE);
\r
628 // https://spec.matrix.org/v1.6/client-server-api/#put_matrixclientv3roomsroomidsendeventtypetxnid
\r
630 MatrixClientSendEvent(
\r
631 MatrixClient * client,
\r
632 const char * roomId,
\r
633 const char * msgType,
\r
634 const char * msgBody)
\r
636 static char requestUrl[MAX_URL_LEN];
\r
637 sprintf(requestUrl,
\r
638 ROOMEVENT_URL, roomId, msgType, (int)time(NULL));
\r
640 static char responseBuffer[ROOMEVENT_RESPONSE_SIZE];
\r
642 MatrixHttpPut(client,
\r
645 responseBuffer, ROOMEVENT_RESPONSE_SIZE,
\r
651 // https://spec.matrix.org/v1.6/client-server-api/#mroomencrypted
\r
652 // https://matrix.org/docs/guides/end-to-end-encryption-implementation-guide#sending-an-encrypted-message-event
\r
654 MatrixClientSendEventEncrypted(
\r
655 MatrixClient * client,
\r
656 const char * roomId,
\r
657 const char * msgType,
\r
658 const char * msgBody)
\r
661 static char requestBuffer[ROOMEVENT_REQUEST_SIZE];
\r
662 sprintf(requestBuffer,
\r
666 "\"room_id\":\"%s\""
\r
672 // get megolm session
\r
673 MatrixMegolmOutSession * outSession;
\r
674 MatrixClientGetMegolmOutSession(client, roomId, &outSession);
\r
677 static char encryptedBuffer[ENCRYPTED_REQUEST_SIZE];
\r
678 MatrixMegolmOutSessionEncrypt(outSession,
\r
680 encryptedBuffer, ENCRYPTED_REQUEST_SIZE);
\r
682 // encrypted event json
\r
683 const char * senderKey = client->deviceKey;
\r
684 const char * sessionId = outSession->id;
\r
685 const char * deviceId = client->deviceId;
\r
687 static char encryptedEventBuffer[ENCRYPTED_EVENT_SIZE];
\r
688 sprintf(encryptedEventBuffer,
\r
690 "\"algorithm\":\"m.megolm.v1.aes-sha2\","
\r
691 "\"ciphertext\":\"%s\","
\r
692 "\"device_id\":\"%s\","
\r
693 "\"sender_key\":\"%s\","
\r
694 "\"session_id\":\"%s\""
\r
702 return MatrixClientSendEvent(client,
\r
704 "m.room.encrypted",
\r
705 encryptedEventBuffer);
\r
708 // https://spec.matrix.org/v1.6/client-server-api/#get_matrixclientv3sync
\r
711 MatrixClient * client,
\r
712 char * outSyncBuffer, int outSyncCap)
\r
715 MatrixHttpGet(client,
\r
716 "/_matrix/client/v3/sync",
\r
717 outSyncBuffer, outSyncCap,
\r
722 MatrixClientShareMegolmOutSession(
\r
723 MatrixClient * client,
\r
724 const char * userId,
\r
725 const char * deviceId,
\r
726 MatrixMegolmOutSession * session)
\r
728 // generate room key event
\r
729 static char eventBuffer[KEY_SHARE_EVENT_LEN];
\r
730 sprintf(eventBuffer,
\r
732 "\"algorithm\":\"m.megolm.v1.aes-sha2\","
\r
733 "\"room_id\":\"%s\","
\r
734 "\"session_id\":\"%s\","
\r
735 "\"session_key\":\"%s\""
\r
742 // // get olm session
\r
743 // MatrixOlmSession * olmSession;
\r
744 // MatrixClientGetOlmSession(client, userId, deviceId, &olmSession);
\r
747 // char encryptedBuffer[KEY_SHARE_EVENT_LEN];
\r
748 // MatrixOlmSessionEncrypt(olmSession,
\r
750 // encryptedBuffer, KEY_SHARE_EVENT_LEN);
\r
753 MatrixClientSendToDeviceEncrypted(client,
\r
763 MatrixClientShareMegolmOutSessionTest(
\r
764 MatrixClient * client,
\r
765 const char * deviceId,
\r
766 MatrixMegolmOutSession * session)
\r
768 // generate room key event
\r
769 char eventBuffer[KEY_SHARE_EVENT_LEN];
\r
770 sprintf(eventBuffer,
\r
772 "\"algorithm\":\"m.megolm.v1.aes-sha2\","
\r
773 "\"room_id\":\"%s\","
\r
774 "\"session_id\":\"%s\","
\r
775 "\"session_key\":\"%s\""
\r
783 MatrixClientSendToDevice(client,
\r
793 // MatrixClientSetMegolmOutSession(
\r
794 // MatrixClient * client,
\r
795 // const char * roomId,
\r
796 // MatrixMegolmOutSession session)
\r
798 // if (client->numMegolmOutSessions < 10)
\r
800 // session.roomId = roomId;
\r
801 // client->megolmOutSessions[client->numMegolmOutSessions] = session;
\r
802 // client->numMegolmOutSessions++;
\r
810 MatrixClientGetMegolmOutSession(
\r
811 MatrixClient * client,
\r
812 const char * roomId,
\r
813 MatrixMegolmOutSession ** outSession)
\r
815 for (int i = 0; i < client->numMegolmOutSessions; i++)
\r
817 if (strcmp(client->megolmOutSessions[i].roomId, roomId) == 0)
\r
819 *outSession = &client->megolmOutSessions[i];
\r
824 if (MatrixClientInitMegolmOutSession(client, roomId)) {
\r
825 *outSession = &client->megolmOutSessions[client->numMegolmOutSessions-1];
\r
833 MatrixClientInitMegolmOutSession(
\r
834 MatrixClient * client,
\r
835 const char * roomId)
\r
837 if (client->numMegolmOutSessions < NUM_MEGOLM_SESSIONS)
\r
839 MatrixMegolmOutSessionInit(
\r
840 &client->megolmOutSessions[client->numMegolmOutSessions],
\r
843 client->numMegolmOutSessions++;
\r
851 MatrixClientGetOlmSession(
\r
852 MatrixClient * client,
\r
853 const char * userId,
\r
854 const char * deviceId,
\r
855 MatrixOlmSession ** outSession)
\r
857 for (int i = 0; i < client->numOlmSessions; i++)
\r
859 if (strcmp(client->olmSessions[i].deviceId, deviceId) == 0)
\r
861 *outSession = &client->olmSessions[i];
\r
866 if (client->numOlmSessions < NUM_OLM_SESSIONS)
\r
868 static char deviceKey[DEVICE_KEY_SIZE];
\r
869 MatrixClientGetDeviceKey(client,
\r
871 deviceKey, DEVICE_KEY_SIZE);
\r
873 char onetimeKey[ONETIME_KEY_SIZE];
\r
874 MatrixClientClaimOnetimeKey(client,
\r
877 onetimeKey, ONETIME_KEY_SIZE);
\r
879 MatrixOlmSessionTo(
\r
880 &client->olmSessions[client->numOlmSessions],
\r
881 client->olmAccount.account,
\r
886 *outSession = &client->olmSessions[client->numOlmSessions];
\r
888 client->numOlmSessions++;
\r
896 // https://spec.matrix.org/v1.6/client-server-api/#put_matrixclientv3sendtodeviceeventtypetxnid
\r
898 MatrixClientSendToDevice(
\r
899 MatrixClient * client,
\r
900 const char * userId,
\r
901 const char * deviceId,
\r
902 const char * message,
\r
903 const char * msgType)
\r
905 static char requestUrl[MAX_URL_LEN];
\r
906 sprintf(requestUrl,
\r
907 TODEVICE_URL, msgType, (int)time(NULL));
\r
909 static char eventBuffer[TODEVICE_EVENT_SIZE];
\r
910 snprintf(eventBuffer, TODEVICE_EVENT_SIZE,
\r
922 static char responseBuffer[ROOMEVENT_RESPONSE_SIZE];
\r
924 MatrixHttpPut(client,
\r
927 responseBuffer, ROOMEVENT_RESPONSE_SIZE,
\r
934 MatrixClientSendToDeviceEncrypted(
\r
935 MatrixClient * client,
\r
936 const char * userId,
\r
937 const char * deviceId,
\r
938 const char * message,
\r
939 const char * msgType)
\r
942 MatrixOlmSession * olmSession;
\r
943 MatrixClientGetOlmSession(client, userId, deviceId, &olmSession);
\r
945 // create event json
\r
946 char deviceKey[DEVICE_KEY_SIZE];
\r
947 MatrixClientGetDeviceKey(client, deviceId, deviceKey, DEVICE_KEY_SIZE);
\r
949 static char eventBuffer[TODEVICE_EVENT_SIZE];
\r
950 sprintf(eventBuffer,
\r
952 "\"type\": \"%s\","
\r
953 "\"content\": \"%s\","
\r
954 "\"sender\": \"%s\","
\r
955 "\"recipient\": \"%s\","
\r
956 "\"recipient_keys\": {"
\r
957 "\"ed25519\": \"%s\""
\r
960 "\"ed25519\": \"%s\""
\r
966 userId, // recipient user id
\r
967 deviceKey, // recipient device key
\r
968 client->deviceKey);
\r
971 static char encryptedBuffer[ENCRYPTED_REQUEST_SIZE];
\r
972 MatrixOlmSessionEncrypt(olmSession,
\r
974 encryptedBuffer, ENCRYPTED_REQUEST_SIZE);
\r
976 static char encryptedEventBuffer[ENCRYPTED_EVENT_SIZE];
\r
977 sprintf(encryptedEventBuffer,
\r
979 "\"algorithm\":\"m.olm.v1.curve25519-aes-sha2\","
\r
986 "\"device_id\":\"%s\","
\r
987 "\"sender_key\":\"%s\""
\r
991 0, //olmSession->type,
\r
993 client->deviceKey);
\r
996 return MatrixClientSendToDevice(
\r
1000 encryptedEventBuffer,
\r
1001 "m.room.encrypted");
\r
1005 MatrixClientFindDevice(
\r
1006 MatrixClient * client,
\r
1007 const char * deviceId,
\r
1008 MatrixDevice ** outDevice)
\r
1010 MatrixClientRequestDeviceKeys(client);
\r
1012 for (int i = 0; i < client->numDevices; i++)
\r
1014 if (strcmp(client->devices[i].deviceId, deviceId) == 0)
\r
1016 *outDevice = &client->devices[i];
\r
1021 *outDevice = NULL;
\r
1026 MatrixClientGetDeviceKey(
\r
1027 MatrixClient * client,
\r
1028 const char * deviceId,
\r
1029 char * outDeviceKey, int outDeviceKeyCap)
\r
1031 MatrixDevice * device;
\r
1033 if (MatrixClientFindDevice(client, deviceId, &device))
\r
1035 strncpy(outDeviceKey, device->deviceKey, outDeviceKeyCap);
\r
1039 MatrixClientRequestDeviceKeys(client);
\r
1041 if (MatrixClientFindDevice(client, deviceId, &device))
\r
1043 strncpy(outDeviceKey, device->deviceKey, outDeviceKeyCap);
\r
1050 // https://spec.matrix.org/v1.6/client-server-api/#post_matrixclientv3keysquery
\r
1052 MatrixClientRequestDeviceKeys(
\r
1053 MatrixClient * client)
\r
1055 static char userIdEscaped[USER_ID_SIZE];
\r
1056 JsonEscape(client->userId, strlen(client->userId),
\r
1057 userIdEscaped, USER_ID_SIZE);
\r
1059 static char request[KEYS_QUERY_REQUEST_SIZE];
\r
1060 snprintf(request, KEYS_QUERY_REQUEST_SIZE,
\r
1061 "{\"device_keys\":{\"%s\":[]}}", client->userId);
\r
1063 static char responseBuffer[KEYS_QUERY_RESPONSE_SIZE];
\r
1064 bool requestResult = MatrixHttpPost(client,
\r
1067 responseBuffer, KEYS_QUERY_RESPONSE_SIZE,
\r
1070 if (! requestResult)
\r
1073 // query for retrieving device keys for user id
\r
1074 static char query[JSON_QUERY_SIZE];
\r
1075 snprintf(query, JSON_QUERY_SIZE,
\r
1076 "$.device_keys.%s", userIdEscaped);
\r
1080 mjson_find(responseBuffer, strlen(responseBuffer),
\r
1081 query, &s, &slen);
\r
1085 int koff, klen, voff, vlen, vtype, off = 0;
\r
1086 for (off = 0; (off = mjson_next(s, slen, off, &koff, &klen,
\r
1087 &voff, &vlen, &vtype)) != 0; ) {
\r
1088 const char * key = s + koff;
\r
1089 const char * val = s + voff;
\r
1091 // set device id, "key" is the JSON key
\r
1093 snprintf(d.deviceId, DEVICE_ID_SIZE,
\r
1094 "%.*s", klen-2, key+1);
\r
1096 // look for device key in value
\r
1097 static char deviceKeyQuery[JSON_QUERY_SIZE];
\r
1098 snprintf(deviceKeyQuery, JSON_QUERY_SIZE,
\r
1099 "$.keys.curve25519:%s", d.deviceId);
\r
1100 mjson_get_string(val, vlen,
\r
1101 deviceKeyQuery, d.deviceKey, DEVICE_KEY_SIZE);
\r
1104 if (client->numDevices < NUM_DEVICES)
\r
1106 client->devices[client->numDevices] = d;
\r
1107 client->numDevices++;
\r