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*4
\r
27 #define KEYS_UPLOAD_REQUEST_SIGNED_SIZE 2048*4
\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 char thisSigningKey[DEVICE_KEY_SIZE];
\r
93 MatrixOlmAccountGetSigningKey(&client->olmAccount, thisSigningKey, DEVICE_KEY_SIZE);
\r
95 static char signatureJson[JSON_SIGNATURE_SIZE];
\r
96 int signatureJsonLen =
\r
97 mjson_snprintf(signatureJson, JSON_SIGNATURE_SIZE,
\r
101 "\"ed25519:%s\":\"%.*s\""
\r
107 signatureLen, signature);
\r
109 struct mjson_fixedbuf result = { sOut, sOutCap, 0 };
\r
112 signatureJson, signatureJsonLen,
\r
113 mjson_print_fixed_buf,
\r
121 MatrixOlmAccountInit(
\r
122 MatrixOlmAccount * account)
\r
124 account->account = olm_account(account->memory);
\r
126 static uint8_t random[OLM_ACCOUNT_RANDOM_SIZE];
\r
127 Randomize(random, OLM_ACCOUNT_RANDOM_SIZE);
\r
129 size_t res = olm_create_account(
\r
132 OLM_ACCOUNT_RANDOM_SIZE);
\r
134 return res != olm_error();
\r
138 MatrixOlmAccountUnpickle(
\r
139 MatrixOlmAccount * account,
\r
140 void * pickled, int pickledLen,
\r
141 const void * key, int keyLen)
\r
144 res = olm_unpickle_account(account->account,
\r
146 pickled, pickledLen);
\r
147 if (res == olm_error()) {
\r
148 printf("error unpickling olm account:%s\n",
\r
149 olm_account_last_error(account->account));
\r
151 return res != olm_error();
\r
155 MatrixOlmAccountGetDeviceKey(
\r
156 MatrixOlmAccount * account,
\r
157 char * key, int keyCap)
\r
159 static char deviceKeysJson[OLM_IDENTITY_KEYS_JSON_SIZE];
\r
161 olm_account_identity_keys(account->account,
\r
162 deviceKeysJson, OLM_IDENTITY_KEYS_JSON_SIZE);
\r
163 mjson_get_string(deviceKeysJson, res,
\r
170 MatrixOlmAccountGetSigningKey(
\r
171 MatrixOlmAccount * account,
\r
172 char * key, int keyCap)
\r
174 static char deviceKeysJson[OLM_IDENTITY_KEYS_JSON_SIZE];
\r
176 olm_account_identity_keys(account->account,
\r
177 deviceKeysJson, OLM_IDENTITY_KEYS_JSON_SIZE);
\r
178 mjson_get_string(deviceKeysJson, res,
\r
184 // TODO:in/outbound sessions
\r
186 MatrixOlmSessionTo(
\r
187 MatrixOlmSession * session,
\r
188 OlmAccount * olmAccount,
\r
189 const char * deviceId,
\r
190 const char * deviceKey,
\r
191 const char * deviceOnetimeKey)
\r
193 memset(session, 0, sizeof(MatrixOlmSession));
\r
195 session->deviceId = deviceId;
\r
198 olm_session(session->memory);
\r
200 static uint8_t random[OLM_OUTBOUND_SESSION_RANDOM_SIZE];
\r
201 Randomize(random, OLM_OUTBOUND_SESSION_RANDOM_SIZE);
\r
204 olm_create_outbound_session(session->session,
\r
206 deviceKey, strlen(deviceKey),
\r
207 deviceOnetimeKey, strlen(deviceOnetimeKey),
\r
208 random, OLM_OUTBOUND_SESSION_RANDOM_SIZE);
\r
210 if (res == olm_error()) {
\r
211 printf("error olm:%s\n", olm_session_last_error(session->session));
\r
214 return session->session != NULL;
\r
218 MatrixOlmSessionUnpickle(
\r
219 MatrixOlmSession * session,
\r
220 const char * deviceId,
\r
221 void * pickled, int pickledLen,
\r
222 const void * key, int keyLen)
\r
224 memset(session, 0, sizeof(MatrixOlmSession));
\r
226 session->deviceId = deviceId;
\r
229 olm_session(session->memory);
\r
232 res = olm_unpickle_session(session->session,
\r
234 pickled, pickledLen);
\r
236 if (res == olm_error()) {
\r
237 printf("error unpickling olm session:%s\n", olm_session_last_error(session->session));
\r
240 return res != olm_error();
\r
244 MatrixOlmSessionEncrypt(
\r
245 MatrixOlmSession * session,
\r
246 const char * plaintext,
\r
247 char * outBuffer, int outBufferCap)
\r
249 static uint8_t random[OLM_ENCRYPT_RANDOM_SIZE];
\r
250 Randomize(random, OLM_ENCRYPT_RANDOM_SIZE);
\r
252 size_t res = olm_encrypt(session->session,
\r
253 plaintext, strlen(plaintext),
\r
254 random, OLM_ENCRYPT_RANDOM_SIZE,
\r
255 outBuffer, outBufferCap);
\r
257 return res != olm_error();
\r
260 // https://matrix.org/docs/guides/end-to-end-encryption-implementation-guide#starting-a-megolm-session
\r
262 MatrixMegolmOutSessionInit(
\r
263 MatrixMegolmOutSession * session,
\r
264 const char * roomId)
\r
266 memset(session, 0, sizeof(MatrixMegolmOutSession));
\r
268 static uint8_t random[MEGOLM_INIT_RANDOM_SIZE];
\r
269 Randomize(random, MEGOLM_INIT_RANDOM_SIZE);
\r
271 strncpy(session->roomId, roomId, ROOM_ID_SIZE);
\r
274 olm_outbound_group_session(session->memory);
\r
276 olm_init_outbound_group_session(
\r
279 MEGOLM_INIT_RANDOM_SIZE);
\r
281 olm_outbound_group_session_id(session->session,
\r
282 (uint8_t *)session->id,
\r
283 MEGOLM_SESSION_ID_SIZE);
\r
285 olm_outbound_group_session_key(session->session,
\r
286 (uint8_t *)session->key,
\r
287 MEGOLM_SESSION_KEY_SIZE);
\r
293 MatrixMegolmOutSessionEncrypt(
\r
294 MatrixMegolmOutSession * session,
\r
295 const char * plaintext,
\r
296 char * outBuffer, int outBufferCap)
\r
298 size_t res = olm_group_encrypt(session->session,
\r
299 (uint8_t *)plaintext, strlen(plaintext),
\r
300 (uint8_t *)outBuffer, outBufferCap);
\r
302 return res != olm_error();
\r
306 MatrixMegolmOutSessionSave(
\r
307 MatrixMegolmOutSession * session,
\r
308 const char * filename,
\r
311 FILE * f = fopen(filename, "w");
\r
313 size_t roomIdLen = strlen(session->roomId);
\r
314 fwrite(&roomIdLen, sizeof(size_t), 1, f);
\r
315 fwrite(session->roomId, 1, roomIdLen, f);
\r
317 size_t pickleBufferLen =
\r
318 olm_pickle_outbound_group_session_length(
\r
320 void * pickleBuffer = malloc(pickleBufferLen);
\r
322 olm_pickle_outbound_group_session(
\r
325 pickleBuffer, pickleBufferLen);
\r
327 fwrite(&pickleBufferLen, sizeof(size_t), 1, f);
\r
328 fwrite(pickleBuffer, 1, pickleBufferLen, f);
\r
329 free(pickleBuffer);
\r
337 MatrixMegolmOutSessionLoad(
\r
338 MatrixMegolmOutSession * session,
\r
339 const char * filename,
\r
342 FILE * f = fopen(filename, "r");
\r
345 fread(&roomIdLen, sizeof(size_t), 1, f);
\r
346 fread(session->roomId, 1, roomIdLen, f);
\r
347 for (int i = roomIdLen; i < ROOM_ID_SIZE; i++)
\r
348 session->roomId[i] = '\0';
\r
350 size_t pickleBufferLen;
\r
351 fread(&pickleBufferLen, sizeof(size_t), 1, f);
\r
353 void * pickleBuffer = malloc(pickleBufferLen);
\r
354 fread(pickleBuffer, 1, pickleBufferLen, f);
\r
356 olm_unpickle_outbound_group_session(
\r
359 pickleBuffer, pickleBufferLen);
\r
361 free(pickleBuffer);
\r
363 olm_outbound_group_session_id(session->session, (uint8_t *)session->id, MEGOLM_SESSION_ID_SIZE);
\r
364 olm_outbound_group_session_key(session->session, (uint8_t *)session->key, MEGOLM_SESSION_KEY_SIZE);
\r
375 MatrixClient * client,
\r
376 const char * server)
\r
378 memset(client, 0, sizeof(MatrixClient));
\r
380 strcpy(client->server, server);
\r
382 // init olm account
\r
383 MatrixOlmAccountInit(&client->olmAccount);
\r
390 MatrixClient * client,
\r
391 const char * filename)
\r
393 FILE * f = fopen(filename, "w");
\r
396 char thisDeviceKey[DEVICE_KEY_SIZE];
\r
397 MatrixOlmAccountGetDeviceKey(&client->olmAccount, thisDeviceKey, DEVICE_KEY_SIZE);
\r
398 char thisSigningKey[DEVICE_KEY_SIZE];
\r
399 MatrixOlmAccountGetSigningKey(&client->olmAccount, thisSigningKey, DEVICE_KEY_SIZE);
\r
402 fwrite(thisDeviceKey, 1, DEVICE_KEY_SIZE, f);
\r
403 fwrite(thisSigningKey, 1, DEVICE_KEY_SIZE, f);
\r
404 fwrite(client->userId, 1, USER_ID_SIZE, f);
\r
405 fwrite(client->server, 1, SERVER_SIZE, f);
\r
406 fwrite(client->accessToken, 1, ACCESS_TOKEN_SIZE, f);
\r
407 fwrite(client->deviceId, 1, DEVICE_ID_SIZE, f);
\r
408 fwrite(client->expireMs, 1, EXPIRE_MS_SIZE, f);
\r
409 fwrite(client->refreshToken, 1, REFRESH_TOKEN_SIZE, f);
\r
411 fwrite(&client->numDevices, sizeof(int), 1, f);
\r
412 for (int i = 0; i < client->numDevices; i++) {
\r
413 fwrite(client->devices[i].deviceId, 1, DEVICE_ID_SIZE, f);
\r
414 fwrite(client->devices[i].deviceKey, 1, DEVICE_KEY_SIZE, f);
\r
423 MatrixClient * client,
\r
424 const char * filename)
\r
426 FILE * f = fopen(filename, "r");
\r
429 char thisDeviceKey[DEVICE_KEY_SIZE];
\r
430 MatrixOlmAccountGetDeviceKey(&client->olmAccount, thisDeviceKey, DEVICE_KEY_SIZE);
\r
431 char thisSigningKey[DEVICE_KEY_SIZE];
\r
432 MatrixOlmAccountGetSigningKey(&client->olmAccount, thisSigningKey, DEVICE_KEY_SIZE);
\r
435 fread(thisDeviceKey, 1, DEVICE_KEY_SIZE, f);
\r
436 fread(thisSigningKey, 1, DEVICE_KEY_SIZE, f);
\r
437 fread(client->userId, 1, USER_ID_SIZE, f);
\r
438 fread(client->server, 1, SERVER_SIZE, f);
\r
439 fread(client->accessToken, 1, ACCESS_TOKEN_SIZE, f);
\r
440 fread(client->deviceId, 1, DEVICE_ID_SIZE, f);
\r
441 fread(client->expireMs, 1, EXPIRE_MS_SIZE, f);
\r
442 fread(client->refreshToken, 1, REFRESH_TOKEN_SIZE, f);
\r
444 fread(&client->numDevices, sizeof(int), 1, f);
\r
445 for (int i = 0; i < client->numDevices; i++) {
\r
446 fread(client->devices[i].deviceId, 1, DEVICE_ID_SIZE, f);
\r
447 fread(client->devices[i].deviceKey, 1, DEVICE_KEY_SIZE, f);
\r
455 MatrixClientSetAccessToken(
\r
456 MatrixClient * client,
\r
457 const char * accessToken)
\r
459 for (int i = 0; i < ACCESS_TOKEN_SIZE-1; i++)
\r
460 client->accessToken[i] = accessToken[i];
\r
461 client->accessToken[ACCESS_TOKEN_SIZE-1] = '\0';
\r
467 MatrixClientSetDeviceId(
\r
468 MatrixClient * client,
\r
469 const char * deviceId)
\r
471 for (int i = 0; i < DEVICE_ID_SIZE-1; i++)
\r
472 client->deviceId[i] = deviceId[i];
\r
473 client->deviceId[DEVICE_ID_SIZE-1] = '\0';
\r
479 MatrixClientSetUserId(
\r
480 MatrixClient * client,
\r
481 const char * userId)
\r
483 for (int i = 0; i < USER_ID_SIZE-1; i++)
\r
484 client->userId[i] = userId[i];
\r
485 client->userId[USER_ID_SIZE-1] = '\0';
\r
491 MatrixClientGenerateOnetimeKeys(
\r
492 MatrixClient * client,
\r
495 static uint8_t random[OLM_ONETIME_KEYS_RANDOM_SIZE];
\r
496 Randomize(random, OLM_ONETIME_KEYS_RANDOM_SIZE);
\r
499 olm_account_generate_one_time_keys(client->olmAccount.account,
\r
500 numberOfKeys, random, OLM_ONETIME_KEYS_RANDOM_SIZE);
\r
502 return res != olm_error();
\r
505 // https://spec.matrix.org/v1.7/client-server-api/#post_matrixclientv3keysupload
\r
507 MatrixClientUploadOnetimeKeys(
\r
508 MatrixClient * client)
\r
510 static char requestBuffer[KEYS_UPLOAD_REQUEST_SIZE];
\r
512 mjson_snprintf(requestBuffer, KEYS_UPLOAD_REQUEST_SIZE,
\r
513 "{\"one_time_keys\":{");
\r
515 static char onetimeKeysBuffer[1024];
\r
516 olm_account_one_time_keys(client->olmAccount.account,
\r
517 onetimeKeysBuffer, 1024);
\r
521 mjson_find(onetimeKeysBuffer, strlen(onetimeKeysBuffer), "$.curve25519", &keys, &keysLen);
\r
523 int koff, klen, voff, vlen, vtype, off = 0;
\r
524 while ((off = mjson_next(keys, keysLen, off, &koff, &klen, &voff, &vlen, &vtype)) != 0) {
\r
525 static char keyJson[JSON_ONETIME_KEY_SIZE];
\r
527 snprintf(keyJson, JSON_ONETIME_KEY_SIZE,
\r
528 "{\"key\":\"%.*s\"}",
\r
529 vlen-2, keys + voff+1);
\r
531 static char keyJsonSigned[JSON_ONETIME_KEY_SIGNED_SIZE];
\r
534 keyJson, JSON_ONETIME_KEY_SIZE,
\r
535 keyJsonSigned, JSON_ONETIME_KEY_SIGNED_SIZE);
\r
537 mjson_snprintf(requestBuffer+strlen(requestBuffer), KEYS_UPLOAD_REQUEST_SIZE-strlen(requestBuffer),
\r
538 "\"signed_curve25519:%.*s\":%s,",
\r
539 klen-2, keys + koff+1,
\r
543 mjson_snprintf(requestBuffer+strlen(requestBuffer)-1, KEYS_UPLOAD_REQUEST_SIZE-strlen(requestBuffer),
\r
546 static char responseBuffer[KEYS_UPLOAD_RESPONSE_SIZE];
\r
547 MatrixHttpPost(client,
\r
550 responseBuffer, KEYS_UPLOAD_RESPONSE_SIZE,
\r
556 // https://spec.matrix.org/v1.7/client-server-api/#post_matrixclientv3keysupload
\r
558 MatrixClientUploadDeviceKey(
\r
559 MatrixClient * client)
\r
561 char thisDeviceKey[DEVICE_KEY_SIZE];
\r
562 MatrixOlmAccountGetDeviceKey(&client->olmAccount, thisDeviceKey, DEVICE_KEY_SIZE);
\r
563 char thisSigningKey[DEVICE_KEY_SIZE];
\r
564 MatrixOlmAccountGetSigningKey(&client->olmAccount, thisSigningKey, DEVICE_KEY_SIZE);
\r
566 static char deviceKeysBuffer[KEYS_UPLOAD_REQUEST_SIZE];
\r
568 mjson_snprintf(deviceKeysBuffer, KEYS_UPLOAD_REQUEST_SIZE,
\r
569 "{\"device_keys\":{"
\r
570 "\"algorithms\":[\"m.olm.v1.curve25519-aes-sha2\",\"m.megolm.v1.aes-sha2\"],"
\r
571 "\"device_id\":\"%s\","
\r
573 "\"curve25519:%s\":\"%s\","
\r
574 "\"ed25519:%s\":\"%s\""
\r
576 "\"user_id\":\"%s\""
\r
579 client->deviceId, thisDeviceKey,
\r
580 client->deviceId, thisSigningKey,
\r
583 static char deviceKeysSignedBuffer[KEYS_UPLOAD_REQUEST_SIGNED_SIZE];
\r
585 deviceKeysBuffer, KEYS_UPLOAD_REQUEST_SIZE,
\r
586 deviceKeysSignedBuffer, KEYS_UPLOAD_REQUEST_SIZE);
\r
589 static char responseBuffer[KEYS_UPLOAD_RESPONSE_SIZE];
\r
590 MatrixHttpPost(client,
\r
592 deviceKeysSignedBuffer,
\r
593 responseBuffer, KEYS_UPLOAD_RESPONSE_SIZE,
\r
599 // https://spec.matrix.org/v1.7/client-server-api/#post_matrixclientv3keysclaim
\r
601 MatrixClientClaimOnetimeKey(
\r
602 MatrixClient * client,
\r
603 const char * userId,
\r
604 const char * deviceId,
\r
605 char * outOnetimeKey, int outOnetimeKeyCap)
\r
607 static char requestBuffer[KEYS_CLAIM_REQUEST_SIZE];
\r
608 mjson_snprintf(requestBuffer, KEYS_CLAIM_REQUEST_SIZE,
\r
610 "\"one_time_keys\":{"
\r
612 "\"%s\":\"signed_curve25519\""
\r
615 "\"timeout\":10000"
\r
620 static char responseBuffer[KEYS_CLAIM_RESPONSE_SIZE];
\r
621 MatrixHttpPost(client,
\r
624 responseBuffer, KEYS_CLAIM_RESPONSE_SIZE,
\r
627 char userIdEscaped[USER_ID_SIZE];
\r
628 JsonEscape(userId, strlen(userId),
\r
629 userIdEscaped, USER_ID_SIZE);
\r
631 static char query[JSON_QUERY_SIZE];
\r
632 snprintf(query, JSON_QUERY_SIZE,
\r
633 "$.one_time_keys.%s.%s",
\r
637 const char * keyObject;
\r
639 mjson_find(responseBuffer, strlen(responseBuffer),
\r
641 &keyObject, &keyObjectSize);
\r
643 int koff, klen, voff, vlen, vtype;
\r
644 mjson_next(keyObject, keyObjectSize, 0,
\r
645 &koff, &klen, &voff, &vlen, &vtype);
\r
647 mjson_get_string(keyObject + voff, vlen,
\r
648 "$.key", outOnetimeKey, outOnetimeKeyCap);
\r
650 // TODO:verify signature
\r
655 // https://spec.matrix.org/v1.6/client-server-api/#post_matrixclientv3login
\r
657 MatrixClientLoginPassword(
\r
658 MatrixClient * client,
\r
659 const char * username,
\r
660 const char * password,
\r
661 const char * displayName)
\r
663 static char requestBuffer[LOGIN_REQUEST_SIZE];
\r
665 mjson_snprintf(requestBuffer, LOGIN_REQUEST_SIZE,
\r
667 "\"type\":\"m.login.password\","
\r
669 "\"type\":\"m.id.user\","
\r
672 "\"password\":\"%s\","
\r
673 "\"initial_device_display_name\":\"%s\""
\r
679 static char responseBuffer[LOGIN_RESPONSE_SIZE];
\r
681 MatrixHttpPost(client,
\r
684 responseBuffer, LOGIN_RESPONSE_SIZE,
\r
690 int responseLen = strlen(responseBuffer);
\r
692 mjson_get_string(responseBuffer, responseLen,
\r
694 client->accessToken, ACCESS_TOKEN_SIZE);
\r
695 mjson_get_string(responseBuffer, responseLen,
\r
697 client->deviceId, DEVICE_ID_SIZE);
\r
698 mjson_get_string(responseBuffer, responseLen,
\r
700 client->expireMs, EXPIRE_MS_SIZE);
\r
701 mjson_get_string(responseBuffer, responseLen,
\r
703 client->refreshToken, REFRESH_TOKEN_SIZE);
\r
708 // https://spec.matrix.org/v1.6/client-server-api/#put_matrixclientv3roomsroomidsendeventtypetxnid
\r
710 MatrixClientSendEvent(
\r
711 MatrixClient * client,
\r
712 const char * roomId,
\r
713 const char * msgType,
\r
714 const char * msgBody)
\r
716 static char requestUrl[MAX_URL_LEN];
\r
717 sprintf(requestUrl,
\r
718 ROOMEVENT_URL, roomId, msgType, (int)time(NULL));
\r
720 static char responseBuffer[ROOMEVENT_RESPONSE_SIZE];
\r
722 MatrixHttpPut(client,
\r
725 responseBuffer, ROOMEVENT_RESPONSE_SIZE,
\r
731 // https://spec.matrix.org/v1.6/client-server-api/#mroomencrypted
\r
732 // https://matrix.org/docs/guides/end-to-end-encryption-implementation-guide#sending-an-encrypted-message-event
\r
734 MatrixClientSendEventEncrypted(
\r
735 MatrixClient * client,
\r
736 const char * roomId,
\r
737 const char * msgType,
\r
738 const char * msgBody)
\r
741 static char requestBuffer[ROOMEVENT_REQUEST_SIZE];
\r
742 sprintf(requestBuffer,
\r
746 "\"room_id\":\"%s\""
\r
752 // get megolm session
\r
753 MatrixMegolmOutSession * outSession;
\r
754 MatrixClientGetMegolmOutSession(client, roomId, &outSession);
\r
757 static char encryptedBuffer[ENCRYPTED_REQUEST_SIZE];
\r
758 MatrixMegolmOutSessionEncrypt(outSession,
\r
760 encryptedBuffer, ENCRYPTED_REQUEST_SIZE);
\r
762 char thisDeviceKey[DEVICE_KEY_SIZE];
\r
763 MatrixOlmAccountGetDeviceKey(&client->olmAccount, thisDeviceKey, DEVICE_KEY_SIZE);
\r
766 // encrypted event json
\r
767 const char * senderKey = thisDeviceKey;
\r
768 const char * sessionId = outSession->id;
\r
769 const char * deviceId = client->deviceId;
\r
771 static char encryptedEventBuffer[ENCRYPTED_EVENT_SIZE];
\r
772 sprintf(encryptedEventBuffer,
\r
774 "\"algorithm\":\"m.megolm.v1.aes-sha2\","
\r
775 "\"ciphertext\":\"%s\","
\r
776 "\"device_id\":\"%s\","
\r
777 "\"sender_key\":\"%s\","
\r
778 "\"session_id\":\"%s\""
\r
786 return MatrixClientSendEvent(client,
\r
788 "m.room.encrypted",
\r
789 encryptedEventBuffer);
\r
792 // https://spec.matrix.org/v1.6/client-server-api/#get_matrixclientv3sync
\r
795 MatrixClient * client,
\r
796 char * outSyncBuffer, int outSyncCap)
\r
799 MatrixHttpGet(client,
\r
800 "/_matrix/client/v3/sync",
\r
801 outSyncBuffer, outSyncCap,
\r
806 MatrixClientShareMegolmOutSession(
\r
807 MatrixClient * client,
\r
808 const char * userId,
\r
809 const char * deviceId,
\r
810 MatrixMegolmOutSession * session)
\r
812 // generate room key event
\r
813 static char eventBuffer[KEY_SHARE_EVENT_LEN];
\r
814 sprintf(eventBuffer,
\r
816 "\"algorithm\":\"m.megolm.v1.aes-sha2\","
\r
817 "\"room_id\":\"%s\","
\r
818 "\"session_id\":\"%s\","
\r
819 "\"session_key\":\"%s\""
\r
826 // // get olm session
\r
827 // MatrixOlmSession * olmSession;
\r
828 // MatrixClientGetOlmSession(client, userId, deviceId, &olmSession);
\r
831 // char encryptedBuffer[KEY_SHARE_EVENT_LEN];
\r
832 // MatrixOlmSessionEncrypt(olmSession,
\r
834 // encryptedBuffer, KEY_SHARE_EVENT_LEN);
\r
837 MatrixClientSendToDeviceEncrypted(client,
\r
847 MatrixClientShareMegolmOutSessionTest(
\r
848 MatrixClient * client,
\r
849 const char * userId,
\r
850 const char * deviceId,
\r
851 MatrixMegolmOutSession * session)
\r
853 // generate room key event
\r
854 char eventBuffer[KEY_SHARE_EVENT_LEN];
\r
855 sprintf(eventBuffer,
\r
857 "\"algorithm\":\"m.megolm.v1.aes-sha2\","
\r
858 "\"room_id\":\"%s\","
\r
859 "\"session_id\":\"%s\","
\r
860 "\"session_key\":\"%s\""
\r
868 MatrixClientSendToDevice(client,
\r
878 // MatrixClientSetMegolmOutSession(
\r
879 // MatrixClient * client,
\r
880 // const char * roomId,
\r
881 // MatrixMegolmOutSession session)
\r
883 // if (client->numMegolmOutSessions < 10)
\r
885 // session.roomId = roomId;
\r
886 // client->megolmOutSessions[client->numMegolmOutSessions] = session;
\r
887 // client->numMegolmOutSessions++;
\r
895 MatrixClientGetMegolmOutSession(
\r
896 MatrixClient * client,
\r
897 const char * roomId,
\r
898 MatrixMegolmOutSession ** outSession)
\r
900 for (int i = 0; i < client->numMegolmOutSessions; i++)
\r
902 if (strcmp(client->megolmOutSessions[i].roomId, roomId) == 0)
\r
904 *outSession = &client->megolmOutSessions[i];
\r
909 if (MatrixClientInitMegolmOutSession(client, roomId)) {
\r
910 *outSession = &client->megolmOutSessions[client->numMegolmOutSessions-1];
\r
918 MatrixClientInitMegolmOutSession(
\r
919 MatrixClient * client,
\r
920 const char * roomId)
\r
922 if (client->numMegolmOutSessions < NUM_MEGOLM_SESSIONS)
\r
924 MatrixMegolmOutSessionInit(
\r
925 &client->megolmOutSessions[client->numMegolmOutSessions],
\r
928 client->numMegolmOutSessions++;
\r
936 MatrixClientGetOlmSession(
\r
937 MatrixClient * client,
\r
938 const char * userId,
\r
939 const char * deviceId,
\r
940 MatrixOlmSession ** outSession)
\r
942 for (int i = 0; i < client->numOlmSessions; i++)
\r
944 if (strcmp(client->olmSessions[i].deviceId, deviceId) == 0)
\r
946 *outSession = &client->olmSessions[i];
\r
951 if (client->numOlmSessions < NUM_OLM_SESSIONS)
\r
953 static char deviceKey[DEVICE_KEY_SIZE];
\r
954 MatrixClientRequestDeviceKey(client,
\r
956 deviceKey, DEVICE_KEY_SIZE);
\r
958 char onetimeKey[ONETIME_KEY_SIZE];
\r
959 MatrixClientClaimOnetimeKey(client,
\r
962 onetimeKey, ONETIME_KEY_SIZE);
\r
964 MatrixOlmSessionTo(
\r
965 &client->olmSessions[client->numOlmSessions],
\r
966 client->olmAccount.account,
\r
971 *outSession = &client->olmSessions[client->numOlmSessions];
\r
973 client->numOlmSessions++;
\r
981 // https://spec.matrix.org/v1.6/client-server-api/#put_matrixclientv3sendtodeviceeventtypetxnid
\r
983 MatrixClientSendToDevice(
\r
984 MatrixClient * client,
\r
985 const char * userId,
\r
986 const char * deviceId,
\r
987 const char * message,
\r
988 const char * msgType)
\r
990 static char requestUrl[MAX_URL_LEN];
\r
991 sprintf(requestUrl,
\r
992 TODEVICE_URL, msgType, (int)time(NULL));
\r
994 static char eventBuffer[TODEVICE_EVENT_SIZE];
\r
995 snprintf(eventBuffer, TODEVICE_EVENT_SIZE,
\r
1007 static char responseBuffer[ROOMEVENT_RESPONSE_SIZE];
\r
1009 MatrixHttpPut(client,
\r
1012 responseBuffer, ROOMEVENT_RESPONSE_SIZE,
\r
1019 MatrixClientSendToDeviceEncrypted(
\r
1020 MatrixClient * client,
\r
1021 const char * userId,
\r
1022 const char * deviceId,
\r
1023 const char * message,
\r
1024 const char * msgType)
\r
1026 // get olm session
\r
1027 MatrixOlmSession * olmSession;
\r
1028 MatrixClientGetOlmSession(client, userId, deviceId, &olmSession);
\r
1030 // create event json
\r
1031 char targetDeviceKey[DEVICE_KEY_SIZE];
\r
1032 MatrixClientRequestDeviceKey(client, deviceId, targetDeviceKey, DEVICE_KEY_SIZE);
\r
1033 char targetSigningKey[SIGNING_KEY_SIZE];
\r
1034 MatrixClientRequestSigningKey(client, deviceId, targetSigningKey, SIGNING_KEY_SIZE);
\r
1036 char thisSigningKey[DEVICE_KEY_SIZE];
\r
1037 MatrixOlmAccountGetSigningKey(&client->olmAccount, thisSigningKey, DEVICE_KEY_SIZE);
\r
1039 static char eventBuffer[TODEVICE_EVENT_SIZE];
\r
1040 sprintf(eventBuffer,
\r
1042 "\"type\":\"%s\","
\r
1044 "\"sender\":\"%s\","
\r
1045 "\"recipient\":\"%s\","
\r
1046 "\"recipient_keys\":{"
\r
1047 "\"ed25519\":\"%s\""
\r
1050 "\"ed25519\":\"%s\""
\r
1056 userId, // recipient user id
\r
1057 targetSigningKey, // recipient device key
\r
1060 printf("%s\n", eventBuffer);
\r
1063 static char encryptedBuffer[ENCRYPTED_REQUEST_SIZE];
\r
1064 MatrixOlmSessionEncrypt(olmSession,
\r
1066 encryptedBuffer, ENCRYPTED_REQUEST_SIZE);
\r
1068 char thisDeviceKey[DEVICE_KEY_SIZE];
\r
1069 MatrixOlmAccountGetDeviceKey(&client->olmAccount, thisDeviceKey, DEVICE_KEY_SIZE);
\r
1072 static char encryptedEventBuffer[ENCRYPTED_EVENT_SIZE];
\r
1073 sprintf(encryptedEventBuffer,
\r
1075 "\"algorithm\":\"m.olm.v1.curve25519-aes-sha2\","
\r
1076 "\"ciphertext\":{"
\r
1078 "\"body\":\"%s\","
\r
1082 "\"device_id\":\"%s\","
\r
1083 "\"sender_key\":\"%s\""
\r
1087 olm_session_has_received_message(olmSession->session),
\r
1092 return MatrixClientSendToDevice(
\r
1096 encryptedEventBuffer,
\r
1097 "m.room.encrypted");
\r
1101 MatrixClientFindDevice(
\r
1102 MatrixClient * client,
\r
1103 const char * deviceId,
\r
1104 MatrixDevice ** outDevice)
\r
1106 MatrixClientRequestDeviceKeys(client);
\r
1108 for (int i = 0; i < client->numDevices; i++)
\r
1110 if (strcmp(client->devices[i].deviceId, deviceId) == 0)
\r
1112 *outDevice = &client->devices[i];
\r
1117 *outDevice = NULL;
\r
1122 MatrixClientRequestDeviceKey(
\r
1123 MatrixClient * client,
\r
1124 const char * deviceId,
\r
1125 char * outDeviceKey, int outDeviceKeyCap)
\r
1127 MatrixDevice * device;
\r
1129 if (MatrixClientFindDevice(client, deviceId, &device))
\r
1131 strncpy(outDeviceKey, device->deviceKey, outDeviceKeyCap);
\r
1135 MatrixClientRequestDeviceKeys(client);
\r
1137 if (MatrixClientFindDevice(client, deviceId, &device))
\r
1139 strncpy(outDeviceKey, device->deviceKey, outDeviceKeyCap);
\r
1147 MatrixClientRequestSigningKey(
\r
1148 MatrixClient * client,
\r
1149 const char * deviceId,
\r
1150 char * outSigningKey, int outSigningKeyCap)
\r
1152 MatrixDevice * device;
\r
1154 if (MatrixClientFindDevice(client, deviceId, &device))
\r
1156 strncpy(outSigningKey, device->signingKey, outSigningKeyCap);
\r
1160 MatrixClientRequestDeviceKeys(client);
\r
1162 if (MatrixClientFindDevice(client, deviceId, &device))
\r
1164 strncpy(outSigningKey, device->signingKey, outSigningKeyCap);
\r
1171 // https://spec.matrix.org/v1.6/client-server-api/#post_matrixclientv3keysquery
\r
1173 MatrixClientRequestDeviceKeys(
\r
1174 MatrixClient * client)
\r
1176 static char userIdEscaped[USER_ID_SIZE];
\r
1177 JsonEscape(client->userId, strlen(client->userId),
\r
1178 userIdEscaped, USER_ID_SIZE);
\r
1180 static char request[KEYS_QUERY_REQUEST_SIZE];
\r
1181 snprintf(request, KEYS_QUERY_REQUEST_SIZE,
\r
1182 "{\"device_keys\":{\"%s\":[]}}", client->userId);
\r
1184 static char responseBuffer[KEYS_QUERY_RESPONSE_SIZE];
\r
1185 bool requestResult = MatrixHttpPost(client,
\r
1188 responseBuffer, KEYS_QUERY_RESPONSE_SIZE,
\r
1191 if (! requestResult)
\r
1194 // query for retrieving device keys for user id
\r
1195 static char query[JSON_QUERY_SIZE];
\r
1196 snprintf(query, JSON_QUERY_SIZE,
\r
1197 "$.device_keys.%s", userIdEscaped);
\r
1201 mjson_find(responseBuffer, strlen(responseBuffer),
\r
1202 query, &s, &slen);
\r
1206 int koff, klen, voff, vlen, vtype, off = 0;
\r
1207 for (off = 0; (off = mjson_next(s, slen, off, &koff, &klen,
\r
1208 &voff, &vlen, &vtype)) != 0; ) {
\r
1209 const char * key = s + koff;
\r
1210 const char * val = s + voff;
\r
1212 // set device id, "key" is the JSON key
\r
1214 snprintf(d.deviceId, DEVICE_ID_SIZE,
\r
1215 "%.*s", klen-2, key+1);
\r
1217 // look for device key in value
\r
1218 static char deviceKeyQuery[JSON_QUERY_SIZE];
\r
1219 snprintf(deviceKeyQuery, JSON_QUERY_SIZE,
\r
1220 "$.keys.curve25519:%s", d.deviceId);
\r
1221 mjson_get_string(val, vlen,
\r
1222 deviceKeyQuery, d.deviceKey, DEVICE_KEY_SIZE);
\r
1224 // look for signing key in value
\r
1225 static char signingKeyQuery[JSON_QUERY_SIZE];
\r
1226 snprintf(signingKeyQuery, JSON_QUERY_SIZE,
\r
1227 "$.keys.ed25519:%s", d.deviceId);
\r
1228 mjson_get_string(val, vlen,
\r
1229 signingKeyQuery, d.signingKey, SIGNING_KEY_SIZE);
\r
1232 if (client->numDevices < NUM_DEVICES)
\r
1234 bool foundDevice = false;
\r
1235 for (int i = 0; i < client->numDevices; i++)
\r
1236 if (strcmp(client->devices[i].deviceId, d.deviceId) == 0)
\r
1237 foundDevice = true;
\r
1239 if (! foundDevice) {
\r
1240 printf("new device: %s %s %s\n", d.deviceId, d.deviceKey, d.signingKey);
\r
1241 client->devices[client->numDevices] = d;
\r
1242 client->numDevices++;
\r
1255 MatrixClientDeleteDevice(
\r
1256 MatrixClient * client)
\r
1258 static char deleteRequest[1024];
\r
1259 snprintf(deleteRequest, 1024,
\r
1260 "{\"devices\":[\"%s\"]}",
\r
1261 client->deviceId);
\r
1262 static char deleteResponse[1024];
\r
1263 bool res = MatrixHttpPost(client, "/_matrix/client/v3/delete_devices",
\r
1264 deleteRequest, deleteResponse, 1024, true);
\r