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 ROOM_SEND_REQUEST_SIZE 256
\r
15 #define ROOM_SEND_RESPONSE_SIZE 1024
\r
16 #define ROOM_SEND_URL "/_matrix/client/v3/rooms/%s/send/%s/%d"
\r
18 #define ROOMKEY_REQUEST_SIZE (1024*4)
\r
20 #define TODEVICE_EVENT_SIZE (1024*5)
\r
21 #define TODEVICE_URL "/_matrix/client/v3/sendToDevice/%s/%d"
\r
23 #define KEYS_QUERY_URL "/_matrix/client/v3/keys/query"
\r
24 #define KEYS_QUERY_REQUEST_SIZE 256
\r
25 #define KEYS_QUERY_RESPONSE_SIZE (1024*10)
\r
27 #define KEYS_UPLOAD_URL "/_matrix/client/v3/keys/upload"
\r
28 #define KEYS_UPLOAD_REQUEST_SIZE 1024*4
\r
29 #define KEYS_UPLOAD_REQUEST_SIGNED_SIZE 2048*4
\r
30 #define KEYS_UPLOAD_RESPONSE_SIZE 2048
\r
32 #define KEYS_CLAIM_URL "/_matrix/client/v3/keys/claim"
\r
33 #define KEYS_CLAIM_REQUEST_SIZE 1024
\r
34 #define KEYS_CLAIM_RESPONSE_SIZE 1024
\r
36 #define JSON_QUERY_SIZE 128
\r
45 static bool first = true;
\r
46 if (first) { srand(time(0)); first = false; }
\r
48 for (int i = 0; i < randomLen; i++)
\r
50 random[i] = rand() % 256;
\r
56 const char * sIn, int sInLen,
\r
57 char * sOut, int sOutCap)
\r
61 for (int i = 0; i < sInLen; i++)
\r
66 if (sIn[i] == '.' ||
\r
70 sOut[sOutIndex++] = '\\';
\r
72 sOut[sOutIndex++] = sIn[i];
\r
75 if (sOutIndex < sOutCap)
\r
76 sOut[sOutIndex] = '\0';
\r
82 MatrixClient * client,
\r
83 const char * sIn, int sInLen,
\r
84 char * sOut, int sOutCap)
\r
86 static char signature[OLM_SIGNATURE_SIZE];
\r
88 olm_account_sign(client->olmAccount.account,
\r
90 signature, OLM_SIGNATURE_SIZE);
\r
92 int signatureLen = res;
\r
94 static char thisSigningKey[SIGNING_KEY_SIZE];
\r
95 MatrixOlmAccountGetSigningKey(&client->olmAccount, thisSigningKey, SIGNING_KEY_SIZE);
\r
97 static char signatureJson[JSON_SIGNATURE_SIZE];
\r
98 int signatureJsonLen =
\r
99 mjson_snprintf(signatureJson, JSON_SIGNATURE_SIZE,
\r
103 "\"ed25519:%s\":\"%.*s\""
\r
110 signatureLen, signature);
\r
112 struct mjson_fixedbuf result = { sOut, sOutCap, 0 };
\r
115 signatureJson, signatureJsonLen,
\r
116 mjson_print_fixed_buf,
\r
124 MatrixOlmAccountInit(
\r
125 MatrixOlmAccount * account)
\r
127 account->account = olm_account(account->memory);
\r
129 static uint8_t random[OLM_ACCOUNT_RANDOM_SIZE];
\r
130 Randomize(random, OLM_ACCOUNT_RANDOM_SIZE);
\r
132 size_t res = olm_create_account(
\r
135 OLM_ACCOUNT_RANDOM_SIZE);
\r
137 return res != olm_error();
\r
141 MatrixOlmAccountUnpickle(
\r
142 MatrixOlmAccount * account,
\r
143 void * pickled, int pickledLen,
\r
144 const void * key, int keyLen)
\r
147 res = olm_unpickle_account(account->account,
\r
149 pickled, pickledLen);
\r
150 if (res == olm_error()) {
\r
151 printf("error unpickling olm account:%s\n",
\r
152 olm_account_last_error(account->account));
\r
154 return res != olm_error();
\r
158 MatrixOlmAccountGetDeviceKey(
\r
159 MatrixOlmAccount * account,
\r
160 char * key, int keyCap)
\r
162 static char deviceKeysJson[OLM_IDENTITY_KEYS_JSON_SIZE];
\r
164 olm_account_identity_keys(account->account,
\r
165 deviceKeysJson, OLM_IDENTITY_KEYS_JSON_SIZE);
\r
166 mjson_get_string(deviceKeysJson, res,
\r
173 MatrixOlmAccountGetSigningKey(
\r
174 MatrixOlmAccount * account,
\r
175 char * key, int keyCap)
\r
177 static char deviceKeysJson[OLM_IDENTITY_KEYS_JSON_SIZE];
\r
179 olm_account_identity_keys(account->account,
\r
180 deviceKeysJson, OLM_IDENTITY_KEYS_JSON_SIZE);
\r
181 mjson_get_string(deviceKeysJson, res,
\r
187 // TODO:in/outbound sessions
\r
189 MatrixOlmSessionTo(
\r
190 MatrixOlmSession * session,
\r
191 OlmAccount * olmAccount,
\r
192 const char * deviceId,
\r
193 const char * deviceKey,
\r
194 const char * deviceOnetimeKey)
\r
196 memset(session, 0, sizeof(MatrixOlmSession));
\r
198 session->deviceId = deviceId;
\r
201 olm_session(session->memory);
\r
203 static uint8_t random[OLM_OUTBOUND_SESSION_RANDOM_SIZE];
\r
204 Randomize(random, OLM_OUTBOUND_SESSION_RANDOM_SIZE);
\r
207 olm_create_outbound_session(session->session,
\r
209 deviceKey, strlen(deviceKey),
\r
210 deviceOnetimeKey, strlen(deviceOnetimeKey),
\r
211 random, OLM_OUTBOUND_SESSION_RANDOM_SIZE);
\r
213 if (res == olm_error()) {
\r
214 printf("error olm:%s\n", olm_session_last_error(session->session));
\r
217 return session->session != NULL;
\r
221 MatrixOlmSessionUnpickle(
\r
222 MatrixOlmSession * session,
\r
223 const char * deviceId,
\r
224 void * pickled, int pickledLen,
\r
225 const void * key, int keyLen)
\r
227 memset(session, 0, sizeof(MatrixOlmSession));
\r
229 session->deviceId = deviceId;
\r
232 olm_session(session->memory);
\r
235 res = olm_unpickle_session(session->session,
\r
237 pickled, pickledLen);
\r
239 if (res == olm_error()) {
\r
240 printf("error unpickling olm session:%s\n", olm_session_last_error(session->session));
\r
243 return res != olm_error();
\r
247 MatrixOlmSessionEncrypt(
\r
248 MatrixOlmSession * session,
\r
249 const char * plaintext,
\r
250 char * outBuffer, int outBufferCap)
\r
252 static uint8_t random[OLM_ENCRYPT_RANDOM_SIZE];
\r
253 Randomize(random, OLM_ENCRYPT_RANDOM_SIZE);
\r
255 size_t res = olm_encrypt(session->session,
\r
256 plaintext, strlen(plaintext),
\r
257 random, OLM_ENCRYPT_RANDOM_SIZE,
\r
258 outBuffer, outBufferCap);
\r
260 return res != olm_error();
\r
264 MatrixOlmSessionDecrypt(
\r
265 MatrixOlmSession * session,
\r
266 size_t messageType,
\r
268 char * outBuffer, int outBufferCap)
\r
270 static uint8_t random[OLM_ENCRYPT_RANDOM_SIZE];
\r
271 Randomize(random, OLM_ENCRYPT_RANDOM_SIZE);
\r
274 olm_decrypt(session->session,
\r
276 encrypted, strlen(encrypted),
\r
277 outBuffer, outBufferCap);
\r
279 if (res != olm_error() && res < outBufferCap)
\r
280 outBuffer[outBufferCap] = '\0';
\r
282 return res != olm_error();
\r
285 // https://matrix.org/docs/guides/end-to-end-encryption-implementation-guide#starting-a-megolm-session
\r
287 MatrixMegolmOutSessionInit(
\r
288 MatrixMegolmOutSession * session,
\r
289 const char * roomId)
\r
291 memset(session, 0, sizeof(MatrixMegolmOutSession));
\r
293 static uint8_t random[MEGOLM_INIT_RANDOM_SIZE];
\r
294 Randomize(random, MEGOLM_INIT_RANDOM_SIZE);
\r
296 strncpy(session->roomId, roomId, ROOM_ID_SIZE);
\r
299 olm_outbound_group_session(session->memory);
\r
301 olm_init_outbound_group_session(
\r
304 MEGOLM_INIT_RANDOM_SIZE);
\r
306 olm_outbound_group_session_id(session->session,
\r
307 (uint8_t *)session->id,
\r
308 MEGOLM_SESSION_ID_SIZE);
\r
310 olm_outbound_group_session_key(session->session,
\r
311 (uint8_t *)session->key,
\r
312 MEGOLM_SESSION_KEY_SIZE);
\r
318 MatrixMegolmOutSessionEncrypt(
\r
319 MatrixMegolmOutSession * session,
\r
320 const char * plaintext,
\r
321 char * outBuffer, int outBufferCap)
\r
323 size_t res = olm_group_encrypt(session->session,
\r
324 (uint8_t *)plaintext, strlen(plaintext),
\r
325 (uint8_t *)outBuffer, outBufferCap);
\r
327 return res != olm_error();
\r
331 MatrixMegolmOutSessionSave(
\r
332 MatrixMegolmOutSession * session,
\r
333 const char * filename,
\r
336 FILE * f = fopen(filename, "w");
\r
338 size_t roomIdLen = strlen(session->roomId);
\r
339 fwrite(&roomIdLen, sizeof(size_t), 1, f);
\r
340 fwrite(session->roomId, 1, roomIdLen, f);
\r
342 size_t pickleBufferLen =
\r
343 olm_pickle_outbound_group_session_length(
\r
345 void * pickleBuffer = malloc(pickleBufferLen);
\r
347 olm_pickle_outbound_group_session(
\r
350 pickleBuffer, pickleBufferLen);
\r
352 fwrite(&pickleBufferLen, sizeof(size_t), 1, f);
\r
353 fwrite(pickleBuffer, 1, pickleBufferLen, f);
\r
354 free(pickleBuffer);
\r
362 MatrixMegolmOutSessionLoad(
\r
363 MatrixMegolmOutSession * session,
\r
364 const char * filename,
\r
367 FILE * f = fopen(filename, "r");
\r
370 fread(&roomIdLen, sizeof(size_t), 1, f);
\r
371 fread(session->roomId, 1, roomIdLen, f);
\r
372 for (int i = roomIdLen; i < ROOM_ID_SIZE; i++)
\r
373 session->roomId[i] = '\0';
\r
375 size_t pickleBufferLen;
\r
376 fread(&pickleBufferLen, sizeof(size_t), 1, f);
\r
378 void * pickleBuffer = malloc(pickleBufferLen);
\r
379 fread(pickleBuffer, 1, pickleBufferLen, f);
\r
381 olm_unpickle_outbound_group_session(
\r
384 pickleBuffer, pickleBufferLen);
\r
386 free(pickleBuffer);
\r
388 olm_outbound_group_session_id(session->session, (uint8_t *)session->id, MEGOLM_SESSION_ID_SIZE);
\r
389 olm_outbound_group_session_key(session->session, (uint8_t *)session->key, MEGOLM_SESSION_KEY_SIZE);
\r
400 MatrixClient * client,
\r
401 const char * server)
\r
403 memset(client, 0, sizeof(MatrixClient));
\r
405 strcpy(client->server, server);
\r
407 // init olm account
\r
408 MatrixOlmAccountInit(&client->olmAccount);
\r
415 MatrixClient * client,
\r
416 const char * filename)
\r
418 FILE * f = fopen(filename, "w");
\r
421 char thisDeviceKey[DEVICE_KEY_SIZE];
\r
422 MatrixOlmAccountGetDeviceKey(&client->olmAccount, thisDeviceKey, DEVICE_KEY_SIZE);
\r
423 char thisSigningKey[DEVICE_KEY_SIZE];
\r
424 MatrixOlmAccountGetSigningKey(&client->olmAccount, thisSigningKey, DEVICE_KEY_SIZE);
\r
427 fwrite(thisDeviceKey, 1, DEVICE_KEY_SIZE, f);
\r
428 fwrite(thisSigningKey, 1, DEVICE_KEY_SIZE, f);
\r
429 fwrite(client->userId, 1, USER_ID_SIZE, f);
\r
430 fwrite(client->server, 1, SERVER_SIZE, f);
\r
431 fwrite(client->accessToken, 1, ACCESS_TOKEN_SIZE, f);
\r
432 fwrite(client->deviceId, 1, DEVICE_ID_SIZE, f);
\r
433 fwrite(client->expireMs, 1, EXPIRE_MS_SIZE, f);
\r
434 fwrite(client->refreshToken, 1, REFRESH_TOKEN_SIZE, f);
\r
436 fwrite(&client->numDevices, sizeof(int), 1, f);
\r
437 for (int i = 0; i < client->numDevices; i++) {
\r
438 fwrite(client->devices[i].deviceId, 1, DEVICE_ID_SIZE, f);
\r
439 fwrite(client->devices[i].deviceKey, 1, DEVICE_KEY_SIZE, f);
\r
448 MatrixClient * client,
\r
449 const char * filename)
\r
451 FILE * f = fopen(filename, "r");
\r
454 char thisDeviceKey[DEVICE_KEY_SIZE];
\r
455 MatrixOlmAccountGetDeviceKey(&client->olmAccount, thisDeviceKey, DEVICE_KEY_SIZE);
\r
456 char thisSigningKey[DEVICE_KEY_SIZE];
\r
457 MatrixOlmAccountGetSigningKey(&client->olmAccount, thisSigningKey, DEVICE_KEY_SIZE);
\r
460 fread(thisDeviceKey, 1, DEVICE_KEY_SIZE, f);
\r
461 fread(thisSigningKey, 1, DEVICE_KEY_SIZE, f);
\r
462 fread(client->userId, 1, USER_ID_SIZE, f);
\r
463 fread(client->server, 1, SERVER_SIZE, f);
\r
464 fread(client->accessToken, 1, ACCESS_TOKEN_SIZE, f);
\r
465 fread(client->deviceId, 1, DEVICE_ID_SIZE, f);
\r
466 fread(client->expireMs, 1, EXPIRE_MS_SIZE, f);
\r
467 fread(client->refreshToken, 1, REFRESH_TOKEN_SIZE, f);
\r
469 fread(&client->numDevices, sizeof(int), 1, f);
\r
470 for (int i = 0; i < client->numDevices; i++) {
\r
471 fread(client->devices[i].deviceId, 1, DEVICE_ID_SIZE, f);
\r
472 fread(client->devices[i].deviceKey, 1, DEVICE_KEY_SIZE, f);
\r
480 MatrixClientSetAccessToken(
\r
481 MatrixClient * client,
\r
482 const char * accessToken)
\r
484 for (int i = 0; i < ACCESS_TOKEN_SIZE-1; i++)
\r
485 client->accessToken[i] = accessToken[i];
\r
486 client->accessToken[ACCESS_TOKEN_SIZE-1] = '\0';
\r
492 MatrixClientSetDeviceId(
\r
493 MatrixClient * client,
\r
494 const char * deviceId)
\r
496 for (int i = 0; i < DEVICE_ID_SIZE-1; i++)
\r
497 client->deviceId[i] = deviceId[i];
\r
498 client->deviceId[DEVICE_ID_SIZE-1] = '\0';
\r
504 MatrixClientSetUserId(
\r
505 MatrixClient * client,
\r
506 const char * userId)
\r
508 for (int i = 0; i < USER_ID_SIZE-1; i++)
\r
509 client->userId[i] = userId[i];
\r
510 client->userId[USER_ID_SIZE-1] = '\0';
\r
516 MatrixClientGenerateOnetimeKeys(
\r
517 MatrixClient * client,
\r
520 static uint8_t random[OLM_ONETIME_KEYS_RANDOM_SIZE];
\r
521 Randomize(random, OLM_ONETIME_KEYS_RANDOM_SIZE);
\r
524 olm_account_generate_one_time_keys(client->olmAccount.account,
\r
525 numberOfKeys, random, OLM_ONETIME_KEYS_RANDOM_SIZE);
\r
527 return res != olm_error();
\r
530 // https://spec.matrix.org/v1.7/client-server-api/#post_matrixclientv3keysupload
\r
532 MatrixClientUploadOnetimeKeys(
\r
533 MatrixClient * client)
\r
535 static char requestBuffer[KEYS_UPLOAD_REQUEST_SIZE];
\r
537 mjson_snprintf(requestBuffer, KEYS_UPLOAD_REQUEST_SIZE,
\r
540 static char onetimeKeysBuffer[1024];
\r
541 olm_account_one_time_keys(client->olmAccount.account,
\r
542 onetimeKeysBuffer, 1024);
\r
546 mjson_find(onetimeKeysBuffer, strlen(onetimeKeysBuffer), "$.curve25519", &keys, &keysLen);
\r
548 int koff, klen, voff, vlen, vtype, off = 0;
\r
549 while ((off = mjson_next(keys, keysLen, off, &koff, &klen, &voff, &vlen, &vtype)) != 0) {
\r
550 static char keyJson[JSON_ONETIME_KEY_SIZE];
\r
553 snprintf(keyJson, JSON_ONETIME_KEY_SIZE,
\r
554 "{\"key\":\"%.*s\"}",
\r
555 vlen-2, keys + voff+1);
\r
557 static char keyJsonSigned[JSON_ONETIME_KEY_SIGNED_SIZE];
\r
560 keyJson, keyJsonLen,
\r
561 keyJsonSigned, JSON_ONETIME_KEY_SIGNED_SIZE);
\r
563 mjson_snprintf(requestBuffer+strlen(requestBuffer), KEYS_UPLOAD_REQUEST_SIZE-strlen(requestBuffer),
\r
564 "\"signed_curve25519:%.*s\":%s,",
\r
565 klen-2, keys + koff+1,
\r
569 if (requestBuffer[strlen(requestBuffer)-1] == ',')
\r
570 requestBuffer[strlen(requestBuffer)-1] = '\0';
\r
572 mjson_snprintf(requestBuffer+strlen(requestBuffer), KEYS_UPLOAD_REQUEST_SIZE-strlen(requestBuffer),
\r
575 // static char onetimeKeysSignedBuffer[KEYS_UPLOAD_REQUEST_SIGNED_SIZE];
\r
576 // JsonSign(client,
\r
577 // requestBuffer, strlen(requestBuffer),
\r
578 // onetimeKeysSignedBuffer, KEYS_UPLOAD_REQUEST_SIZE);
\r
580 // static char finalEvent[KEYS_UPLOAD_REQUEST_SIGNED_SIZE];
\r
581 // snprintf(finalEvent, KEYS_UPLOAD_REQUEST_SIGNED_SIZE,
\r
582 // "{\"one_time_keys\":%s}", onetimeKeysSignedBuffer);
\r
583 static char finalEvent[KEYS_UPLOAD_REQUEST_SIGNED_SIZE];
\r
584 snprintf(finalEvent, KEYS_UPLOAD_REQUEST_SIGNED_SIZE,
\r
585 "{\"one_time_keys\":%s}", requestBuffer);
\r
587 static char responseBuffer[KEYS_UPLOAD_RESPONSE_SIZE];
\r
588 MatrixHttpPost(client,
\r
591 responseBuffer, KEYS_UPLOAD_RESPONSE_SIZE,
\r
594 printf("%s\n", responseBuffer);
\r
599 // https://spec.matrix.org/v1.7/client-server-api/#post_matrixclientv3keysupload
\r
601 MatrixClientUploadDeviceKey(
\r
602 MatrixClient * client)
\r
604 char thisDeviceKey[DEVICE_KEY_SIZE];
\r
605 MatrixOlmAccountGetDeviceKey(&client->olmAccount, thisDeviceKey, DEVICE_KEY_SIZE);
\r
606 char thisSigningKey[DEVICE_KEY_SIZE];
\r
607 MatrixOlmAccountGetSigningKey(&client->olmAccount, thisSigningKey, DEVICE_KEY_SIZE);
\r
609 static char deviceKeysBuffer[KEYS_UPLOAD_REQUEST_SIZE];
\r
611 int deviceKeysBufferLen =
\r
612 mjson_snprintf(deviceKeysBuffer, KEYS_UPLOAD_REQUEST_SIZE,
\r
614 "\"algorithms\":[\"m.olm.v1.curve25519-aes-sha2\",\"m.megolm.v1.aes-sha2\"],"
\r
615 "\"device_id\":\"%s\","
\r
617 "\"curve25519:%s\":\"%s\","
\r
618 "\"ed25519:%s\":\"%s\""
\r
620 "\"user_id\":\"%s\""
\r
623 client->deviceId, thisDeviceKey,
\r
624 client->deviceId, thisSigningKey,
\r
627 static char deviceKeysSignedBuffer[KEYS_UPLOAD_REQUEST_SIGNED_SIZE];
\r
629 deviceKeysBuffer, deviceKeysBufferLen,
\r
630 deviceKeysSignedBuffer, KEYS_UPLOAD_REQUEST_SIZE);
\r
632 static char finalEvent[KEYS_UPLOAD_REQUEST_SIGNED_SIZE];
\r
633 snprintf(finalEvent, KEYS_UPLOAD_REQUEST_SIGNED_SIZE,
\r
634 "{\"device_keys\":%s}", deviceKeysSignedBuffer);
\r
636 static char responseBuffer[KEYS_UPLOAD_RESPONSE_SIZE];
\r
637 MatrixHttpPost(client,
\r
640 responseBuffer, KEYS_UPLOAD_RESPONSE_SIZE,
\r
643 printf("%s\n", responseBuffer);
\r
648 // https://spec.matrix.org/v1.7/client-server-api/#post_matrixclientv3keysclaim
\r
650 MatrixClientClaimOnetimeKey(
\r
651 MatrixClient * client,
\r
652 const char * userId,
\r
653 const char * deviceId,
\r
654 char * outOnetimeKey, int outOnetimeKeyCap)
\r
656 static char requestBuffer[KEYS_CLAIM_REQUEST_SIZE];
\r
657 mjson_snprintf(requestBuffer, KEYS_CLAIM_REQUEST_SIZE,
\r
659 "\"one_time_keys\":{"
\r
661 "\"%s\":\"signed_curve25519\""
\r
664 "\"timeout\":10000"
\r
669 static char responseBuffer[KEYS_CLAIM_RESPONSE_SIZE];
\r
670 MatrixHttpPost(client,
\r
673 responseBuffer, KEYS_CLAIM_RESPONSE_SIZE,
\r
676 char userIdEscaped[USER_ID_SIZE];
\r
677 JsonEscape(userId, strlen(userId),
\r
678 userIdEscaped, USER_ID_SIZE);
\r
680 static char query[JSON_QUERY_SIZE];
\r
681 snprintf(query, JSON_QUERY_SIZE,
\r
682 "$.one_time_keys.%s.%s",
\r
686 const char * keyObject;
\r
688 mjson_find(responseBuffer, strlen(responseBuffer),
\r
690 &keyObject, &keyObjectSize);
\r
692 int koff, klen, voff, vlen, vtype;
\r
693 mjson_next(keyObject, keyObjectSize, 0,
\r
694 &koff, &klen, &voff, &vlen, &vtype);
\r
696 mjson_get_string(keyObject + voff, vlen,
\r
697 "$.key", outOnetimeKey, outOnetimeKeyCap);
\r
699 // TODO:verify signature
\r
704 // https://spec.matrix.org/v1.6/client-server-api/#post_matrixclientv3login
\r
706 MatrixClientLoginPassword(
\r
707 MatrixClient * client,
\r
708 const char * username,
\r
709 const char * password,
\r
710 const char * displayName)
\r
712 static char requestBuffer[LOGIN_REQUEST_SIZE];
\r
714 mjson_snprintf(requestBuffer, LOGIN_REQUEST_SIZE,
\r
716 "\"type\":\"m.login.password\","
\r
718 "\"type\":\"m.id.user\","
\r
721 "\"password\":\"%s\","
\r
722 "\"initial_device_display_name\":\"%s\""
\r
728 static char responseBuffer[LOGIN_RESPONSE_SIZE];
\r
730 MatrixHttpPost(client,
\r
733 responseBuffer, LOGIN_RESPONSE_SIZE,
\r
739 int responseLen = strlen(responseBuffer);
\r
741 mjson_get_string(responseBuffer, responseLen,
\r
743 client->accessToken, ACCESS_TOKEN_SIZE);
\r
744 mjson_get_string(responseBuffer, responseLen,
\r
746 client->deviceId, DEVICE_ID_SIZE);
\r
747 mjson_get_string(responseBuffer, responseLen,
\r
749 client->expireMs, EXPIRE_MS_SIZE);
\r
750 mjson_get_string(responseBuffer, responseLen,
\r
752 client->refreshToken, REFRESH_TOKEN_SIZE);
\r
757 // https://spec.matrix.org/v1.6/client-server-api/#put_matrixclientv3roomsroomidsendeventtypetxnid
\r
759 MatrixClientSendEvent(
\r
760 MatrixClient * client,
\r
761 const char * roomId,
\r
762 const char * msgType,
\r
763 const char * msgBody)
\r
765 static char requestUrl[MAX_URL_LEN];
\r
766 sprintf(requestUrl,
\r
767 ROOM_SEND_URL, roomId, msgType, (int)time(NULL));
\r
769 static char responseBuffer[ROOM_SEND_RESPONSE_SIZE];
\r
771 MatrixHttpPut(client,
\r
774 responseBuffer, ROOM_SEND_RESPONSE_SIZE,
\r
780 // https://spec.matrix.org/v1.6/client-server-api/#mroomencrypted
\r
781 // https://matrix.org/docs/guides/end-to-end-encryption-implementation-guide#sending-an-encrypted-message-event
\r
783 MatrixClientSendEventEncrypted(
\r
784 MatrixClient * client,
\r
785 const char * roomId,
\r
786 const char * msgType,
\r
787 const char * msgBody)
\r
790 static char requestBuffer[ROOM_SEND_REQUEST_SIZE];
\r
791 sprintf(requestBuffer,
\r
795 "\"room_id\":\"%s\""
\r
801 // get megolm session
\r
802 MatrixMegolmOutSession * outSession;
\r
803 MatrixClientGetMegolmOutSession(client, roomId, &outSession);
\r
806 static char encryptedBuffer[ENCRYPTED_REQUEST_SIZE];
\r
807 MatrixMegolmOutSessionEncrypt(outSession,
\r
809 encryptedBuffer, ENCRYPTED_REQUEST_SIZE);
\r
811 char thisDeviceKey[DEVICE_KEY_SIZE];
\r
812 MatrixOlmAccountGetDeviceKey(&client->olmAccount, thisDeviceKey, DEVICE_KEY_SIZE);
\r
815 // encrypted event json
\r
816 const char * senderKey = thisDeviceKey;
\r
817 const char * sessionId = outSession->id;
\r
818 const char * deviceId = client->deviceId;
\r
820 static char encryptedEventBuffer[ENCRYPTED_EVENT_SIZE];
\r
821 sprintf(encryptedEventBuffer,
\r
823 "\"algorithm\":\"m.megolm.v1.aes-sha2\","
\r
824 "\"ciphertext\":\"%s\","
\r
825 "\"device_id\":\"%s\","
\r
826 "\"sender_key\":\"%s\","
\r
827 "\"session_id\":\"%s\""
\r
835 return MatrixClientSendEvent(client,
\r
837 "m.room.encrypted",
\r
838 encryptedEventBuffer);
\r
841 // https://spec.matrix.org/v1.6/client-server-api/#get_matrixclientv3sync
\r
844 MatrixClient * client,
\r
845 char * outSyncBuffer, int outSyncCap,
\r
846 const char * nextBatch)
\r
848 // filter={\"event_fields\":[\"to_device\"]}
\r
849 static char url[MAX_URL_LEN];
\r
850 snprintf(url, MAX_URL_LEN,
\r
851 "/_matrix/client/v3/sync%s",
\r
852 strlen(nextBatch) > 0 ? "?since=" : "");
\r
854 int index = strlen(url);
\r
856 for (int i = 0; i < strlen(nextBatch); i++) {
\r
857 char c = nextBatch[i];
\r
860 url[index++] = '%';
\r
861 url[index++] = '7';
\r
862 url[index++] = 'E';
\r
871 MatrixHttpGet(client,
\r
873 outSyncBuffer, outSyncCap,
\r
877 // https://spec.matrix.org/v1.7/client-server-api/#get_matrixclientv3roomsroomideventeventid
\r
879 MatrixClientGetRoomEvent(
\r
880 MatrixClient * client,
\r
881 const char * roomId,
\r
882 const char * eventId,
\r
883 char * outEvent, int outEventCap)
\r
885 static char url[MAX_URL_LEN];
\r
886 snprintf(url, MAX_URL_LEN,
\r
887 "/_matrix/client/v3/rooms/%s/event/%s",
\r
892 MatrixHttpGet(client,
\r
894 outEvent, outEventCap,
\r
899 MatrixClientShareMegolmOutSession(
\r
900 MatrixClient * client,
\r
901 const char * userId,
\r
902 const char * deviceId,
\r
903 MatrixMegolmOutSession * session)
\r
905 // generate room key event
\r
906 static char eventBuffer[KEY_SHARE_EVENT_LEN];
\r
907 sprintf(eventBuffer,
\r
909 "\"algorithm\":\"m.megolm.v1.aes-sha2\","
\r
910 "\"room_id\":\"%s\","
\r
911 "\"session_id\":\"%s\","
\r
912 "\"session_key\":\"%s\""
\r
919 // // get olm session
\r
920 // MatrixOlmSession * olmSession;
\r
921 // MatrixClientGetOlmSession(client, userId, deviceId, &olmSession);
\r
924 // char encryptedBuffer[KEY_SHARE_EVENT_LEN];
\r
925 // MatrixOlmSessionEncrypt(olmSession,
\r
927 // encryptedBuffer, KEY_SHARE_EVENT_LEN);
\r
930 MatrixClientSendToDeviceEncrypted(client,
\r
940 MatrixClientShareMegolmOutSessionTest(
\r
941 MatrixClient * client,
\r
942 const char * userId,
\r
943 const char * deviceId,
\r
944 MatrixMegolmOutSession * session)
\r
946 // generate room key event
\r
947 char eventBuffer[KEY_SHARE_EVENT_LEN];
\r
948 sprintf(eventBuffer,
\r
950 "\"algorithm\":\"m.megolm.v1.aes-sha2\","
\r
951 "\"room_id\":\"%s\","
\r
952 "\"session_id\":\"%s\","
\r
953 "\"session_key\":\"%s\""
\r
961 MatrixClientSendToDevice(client,
\r
971 // MatrixClientSetMegolmOutSession(
\r
972 // MatrixClient * client,
\r
973 // const char * roomId,
\r
974 // MatrixMegolmOutSession session)
\r
976 // if (client->numMegolmOutSessions < 10)
\r
978 // session.roomId = roomId;
\r
979 // client->megolmOutSessions[client->numMegolmOutSessions] = session;
\r
980 // client->numMegolmOutSessions++;
\r
988 MatrixClientGetMegolmOutSession(
\r
989 MatrixClient * client,
\r
990 const char * roomId,
\r
991 MatrixMegolmOutSession ** outSession)
\r
993 for (int i = 0; i < client->numMegolmOutSessions; i++)
\r
995 if (strcmp(client->megolmOutSessions[i].roomId, roomId) == 0)
\r
997 *outSession = &client->megolmOutSessions[i];
\r
1002 if (MatrixClientInitMegolmOutSession(client, roomId)) {
\r
1003 *outSession = &client->megolmOutSessions[client->numMegolmOutSessions-1];
\r
1011 MatrixClientInitMegolmOutSession(
\r
1012 MatrixClient * client,
\r
1013 const char * roomId)
\r
1015 if (client->numMegolmOutSessions < NUM_MEGOLM_SESSIONS)
\r
1017 MatrixMegolmOutSessionInit(
\r
1018 &client->megolmOutSessions[client->numMegolmOutSessions],
\r
1021 client->numMegolmOutSessions++;
\r
1029 MatrixClientRequestMegolmInSession(
\r
1030 MatrixClient * client,
\r
1031 const char * roomId,
\r
1032 const char * sessionId,
\r
1033 const char * senderKey,
\r
1034 const char * userId,
\r
1035 const char * deviceId,
\r
1036 MatrixMegolmInSession * outMegolmInSession)
\r
1038 // TODO: cancel requests
\r
1039 MatrixClientSendDummy(client, userId, deviceId);
\r
1041 static char event[ROOMKEY_REQUEST_SIZE];
\r
1042 snprintf(event, ROOMKEY_REQUEST_SIZE,
\r
1044 "\"action\":\"request\","
\r
1046 "\"algorithm\":\"m.megolm.v1.aes-sha2\","
\r
1047 "\"room_id\":\"%s\","
\r
1048 "\"sender_key\":\"%s\","
\r
1049 "\"session_id\":\"%s\""
\r
1051 "\"request_id\":\"%d\","
\r
1052 "\"requesting_device_id\":\"%s\""
\r
1058 client->deviceId);
\r
1061 MatrixClientSendToDevice(client,
\r
1065 "m.room_key_request");
\r
1071 MatrixClientGetOlmSession(
\r
1072 MatrixClient * client,
\r
1073 const char * userId,
\r
1074 const char * deviceId,
\r
1075 MatrixOlmSession ** outSession)
\r
1077 for (int i = 0; i < client->numOlmSessions; i++)
\r
1079 if (strcmp(client->olmSessions[i].deviceId, deviceId) == 0)
\r
1081 *outSession = &client->olmSessions[i];
\r
1086 if (client->numOlmSessions < NUM_OLM_SESSIONS)
\r
1088 static char deviceKey[DEVICE_KEY_SIZE];
\r
1089 MatrixClientRequestDeviceKey(client,
\r
1091 deviceKey, DEVICE_KEY_SIZE);
\r
1093 char onetimeKey[ONETIME_KEY_SIZE];
\r
1094 MatrixClientClaimOnetimeKey(client,
\r
1097 onetimeKey, ONETIME_KEY_SIZE);
\r
1099 MatrixOlmSessionTo(
\r
1100 &client->olmSessions[client->numOlmSessions],
\r
1101 client->olmAccount.account,
\r
1106 *outSession = &client->olmSessions[client->numOlmSessions];
\r
1108 client->numOlmSessions++;
\r
1116 // https://spec.matrix.org/v1.6/client-server-api/#put_matrixclientv3sendtodeviceeventtypetxnid
\r
1118 MatrixClientSendToDevice(
\r
1119 MatrixClient * client,
\r
1120 const char * userId,
\r
1121 const char * deviceId,
\r
1122 const char * message,
\r
1123 const char * msgType)
\r
1125 static char requestUrl[MAX_URL_LEN];
\r
1126 sprintf(requestUrl,
\r
1127 TODEVICE_URL, msgType, (int)time(NULL));
\r
1129 static char eventBuffer[TODEVICE_EVENT_SIZE];
\r
1130 snprintf(eventBuffer, TODEVICE_EVENT_SIZE,
\r
1142 static char responseBuffer[ROOM_SEND_RESPONSE_SIZE];
\r
1144 MatrixHttpPut(client,
\r
1147 responseBuffer, ROOM_SEND_RESPONSE_SIZE,
\r
1150 printf("%s\n", responseBuffer);
\r
1156 MatrixClientSendToDeviceEncrypted(
\r
1157 MatrixClient * client,
\r
1158 const char * userId,
\r
1159 const char * deviceId,
\r
1160 const char * message,
\r
1161 const char * msgType)
\r
1163 // get olm session
\r
1164 MatrixOlmSession * olmSession;
\r
1165 MatrixClientGetOlmSession(client, userId, deviceId, &olmSession);
\r
1167 // create event json
\r
1168 char targetDeviceKey[DEVICE_KEY_SIZE];
\r
1169 MatrixClientRequestDeviceKey(client, deviceId, targetDeviceKey, DEVICE_KEY_SIZE);
\r
1170 char targetSigningKey[SIGNING_KEY_SIZE];
\r
1171 MatrixClientRequestSigningKey(client, deviceId, targetSigningKey, SIGNING_KEY_SIZE);
\r
1173 char thisSigningKey[DEVICE_KEY_SIZE];
\r
1174 MatrixOlmAccountGetSigningKey(&client->olmAccount, thisSigningKey, DEVICE_KEY_SIZE);
\r
1176 static char eventBuffer[TODEVICE_EVENT_SIZE];
\r
1177 sprintf(eventBuffer,
\r
1179 "\"type\":\"%s\","
\r
1181 "\"sender\":\"%s\","
\r
1182 "\"recipient\":\"%s\","
\r
1183 "\"recipient_keys\":{"
\r
1184 "\"ed25519\":\"%s\""
\r
1187 "\"ed25519\":\"%s\""
\r
1193 userId, // recipient user id
\r
1194 targetSigningKey, // recipient device key
\r
1197 printf("%s\n", eventBuffer);
\r
1200 static char encryptedBuffer[ENCRYPTED_REQUEST_SIZE];
\r
1201 MatrixOlmSessionEncrypt(olmSession,
\r
1203 encryptedBuffer, ENCRYPTED_REQUEST_SIZE);
\r
1205 char thisDeviceKey[DEVICE_KEY_SIZE];
\r
1206 MatrixOlmAccountGetDeviceKey(&client->olmAccount, thisDeviceKey, DEVICE_KEY_SIZE);
\r
1209 static char encryptedEventBuffer[ENCRYPTED_EVENT_SIZE];
\r
1210 sprintf(encryptedEventBuffer,
\r
1212 "\"algorithm\":\"m.olm.v1.curve25519-aes-sha2\","
\r
1213 "\"ciphertext\":{"
\r
1215 "\"body\":\"%s\","
\r
1219 "\"device_id\":\"%s\","
\r
1220 "\"sender_key\":\"%s\""
\r
1224 olm_session_has_received_message(olmSession->session),
\r
1229 return MatrixClientSendToDevice(
\r
1233 encryptedEventBuffer,
\r
1234 "m.room.encrypted");
\r
1238 MatrixClientSendDummy(
\r
1239 MatrixClient * client,
\r
1240 const char * userId,
\r
1241 const char * deviceId)
\r
1243 return MatrixClientSendToDeviceEncrypted(
\r
1252 MatrixClientFindDevice(
\r
1253 MatrixClient * client,
\r
1254 const char * deviceId,
\r
1255 MatrixDevice ** outDevice)
\r
1257 MatrixClientRequestDeviceKeys(client);
\r
1259 for (int i = 0; i < client->numDevices; i++)
\r
1261 if (strcmp(client->devices[i].deviceId, deviceId) == 0)
\r
1263 *outDevice = &client->devices[i];
\r
1268 *outDevice = NULL;
\r
1273 MatrixClientRequestDeviceKey(
\r
1274 MatrixClient * client,
\r
1275 const char * deviceId,
\r
1276 char * outDeviceKey, int outDeviceKeyCap)
\r
1278 MatrixDevice * device;
\r
1280 if (MatrixClientFindDevice(client, deviceId, &device))
\r
1282 strncpy(outDeviceKey, device->deviceKey, outDeviceKeyCap);
\r
1286 MatrixClientRequestDeviceKeys(client);
\r
1288 if (MatrixClientFindDevice(client, deviceId, &device))
\r
1290 strncpy(outDeviceKey, device->deviceKey, outDeviceKeyCap);
\r
1298 MatrixClientRequestSigningKey(
\r
1299 MatrixClient * client,
\r
1300 const char * deviceId,
\r
1301 char * outSigningKey, int outSigningKeyCap)
\r
1303 MatrixDevice * device;
\r
1305 if (MatrixClientFindDevice(client, deviceId, &device))
\r
1307 strncpy(outSigningKey, device->signingKey, outSigningKeyCap);
\r
1311 MatrixClientRequestDeviceKeys(client);
\r
1313 if (MatrixClientFindDevice(client, deviceId, &device))
\r
1315 strncpy(outSigningKey, device->signingKey, outSigningKeyCap);
\r
1322 // https://spec.matrix.org/v1.6/client-server-api/#post_matrixclientv3keysquery
\r
1324 MatrixClientRequestDeviceKeys(
\r
1325 MatrixClient * client)
\r
1327 static char userIdEscaped[USER_ID_SIZE];
\r
1328 JsonEscape(client->userId, strlen(client->userId),
\r
1329 userIdEscaped, USER_ID_SIZE);
\r
1331 static char request[KEYS_QUERY_REQUEST_SIZE];
\r
1332 snprintf(request, KEYS_QUERY_REQUEST_SIZE,
\r
1333 "{\"device_keys\":{\"%s\":[]}}", client->userId);
\r
1335 static char responseBuffer[KEYS_QUERY_RESPONSE_SIZE];
\r
1336 bool requestResult = MatrixHttpPost(client,
\r
1339 responseBuffer, KEYS_QUERY_RESPONSE_SIZE,
\r
1342 if (! requestResult)
\r
1345 printf("keys:\n%s\n", responseBuffer);
\r
1347 // query for retrieving device keys for user id
\r
1348 static char query[JSON_QUERY_SIZE];
\r
1349 snprintf(query, JSON_QUERY_SIZE,
\r
1350 "$.device_keys.%s", userIdEscaped);
\r
1354 mjson_find(responseBuffer, strlen(responseBuffer),
\r
1355 query, &s, &slen);
\r
1359 int koff, klen, voff, vlen, vtype, off = 0;
\r
1360 for (off = 0; (off = mjson_next(s, slen, off, &koff, &klen,
\r
1361 &voff, &vlen, &vtype)) != 0; ) {
\r
1362 const char * key = s + koff;
\r
1363 const char * val = s + voff;
\r
1365 // set device id, "key" is the JSON key
\r
1367 snprintf(d.deviceId, DEVICE_ID_SIZE,
\r
1368 "%.*s", klen-2, key+1);
\r
1370 // look for device key in value
\r
1371 static char deviceKeyQuery[JSON_QUERY_SIZE];
\r
1372 snprintf(deviceKeyQuery, JSON_QUERY_SIZE,
\r
1373 "$.keys.curve25519:%s", d.deviceId);
\r
1374 mjson_get_string(val, vlen,
\r
1375 deviceKeyQuery, d.deviceKey, DEVICE_KEY_SIZE);
\r
1377 // look for signing key in value
\r
1378 static char signingKeyQuery[JSON_QUERY_SIZE];
\r
1379 snprintf(signingKeyQuery, JSON_QUERY_SIZE,
\r
1380 "$.keys.ed25519:%s", d.deviceId);
\r
1381 mjson_get_string(val, vlen,
\r
1382 signingKeyQuery, d.signingKey, SIGNING_KEY_SIZE);
\r
1385 if (client->numDevices < NUM_DEVICES)
\r
1387 bool foundDevice = false;
\r
1388 for (int i = 0; i < client->numDevices; i++)
\r
1389 if (strcmp(client->devices[i].deviceId, d.deviceId) == 0)
\r
1390 foundDevice = true;
\r
1392 if (! foundDevice) {
\r
1393 printf("new device: %s %s %s\n", d.deviceId, d.deviceKey, d.signingKey);
\r
1394 client->devices[client->numDevices] = d;
\r
1395 client->numDevices++;
\r
1408 MatrixClientDeleteDevice(
\r
1409 MatrixClient * client)
\r
1411 static char deleteRequest[1024];
\r
1412 snprintf(deleteRequest, 1024,
\r
1413 "{\"devices\":[\"%s\"]}",
\r
1414 client->deviceId);
\r
1415 static char deleteResponse[1024];
\r
1416 bool res = MatrixHttpPost(client, "/_matrix/client/v3/delete_devices",
\r
1417 deleteRequest, deleteResponse, 1024, true);
\r