]> gitweb.ps.run Git - matrix_esp_thesis/blob - src/matrix.c
18b700a2e412b1d3699b1781bb1a9cb40e7d0930
[matrix_esp_thesis] / src / matrix.c
1 #include "matrix.h"\r
2 \r
3 #include <time.h>\r
4 #include <stdio.h>\r
5 #include <mjson.h>\r
6 \r
7 \r
8 #define LOGIN_REQUEST_SIZE 1024\r
9 #define LOGIN_RESPONSE_SIZE 1024\r
10 #define LOGIN_URL "/_matrix/client/v3/login"\r
11 \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
17 \r
18 #define ROOMKEY_REQUEST_SIZE (1024*4)\r
19 \r
20 #define TODEVICE_EVENT_SIZE (1024*5)\r
21 #define TODEVICE_URL "/_matrix/client/v3/sendToDevice/%s/%d"\r
22 \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
26 \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
31 \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
35 \r
36 #define JSON_QUERY_SIZE 128\r
37 \r
38 \r
39 \r
40 void\r
41 Randomize(\r
42     uint8_t * random,\r
43     int randomLen)\r
44 {\r
45     static bool first = true;\r
46     if (first) { srand(time(0)); first = false; }\r
47 \r
48     for (int i = 0; i < randomLen; i++)\r
49     {\r
50         random[i] = rand() % 256;\r
51     }\r
52 }\r
53 \r
54 bool\r
55 JsonEscape(\r
56     const char * sIn, int sInLen,\r
57     char * sOut, int sOutCap)\r
58 {\r
59     int sOutIndex = 0;\r
60 \r
61     for (int i = 0; i < sInLen; i++)\r
62     {\r
63         if (i >= sOutCap)\r
64             return false;\r
65         \r
66         if (sIn[i] == '.' ||\r
67             sIn[i] == '[' ||\r
68             sIn[i] == ']'\r
69         ) {\r
70             sOut[sOutIndex++] = '\\';\r
71         }\r
72         sOut[sOutIndex++] = sIn[i];\r
73     }\r
74 \r
75     if (sOutIndex < sOutCap)\r
76         sOut[sOutIndex] = '\0';\r
77 \r
78     return true;\r
79 }\r
80 \r
81 bool JsonSign(\r
82     MatrixClient * client,\r
83     const char * sIn, int sInLen,\r
84     char * sOut, int sOutCap)\r
85 {\r
86     static char signature[OLM_SIGNATURE_SIZE];\r
87     size_t res =\r
88         olm_account_sign(client->olmAccount.account,\r
89             sIn, sInLen,\r
90             signature, OLM_SIGNATURE_SIZE);\r
91     \r
92     int signatureLen = res;\r
93     \r
94     static char thisSigningKey[SIGNING_KEY_SIZE];\r
95     MatrixOlmAccountGetSigningKey(&client->olmAccount, thisSigningKey, SIGNING_KEY_SIZE);\r
96 \r
97     static char signatureJson[JSON_SIGNATURE_SIZE];\r
98     int signatureJsonLen =\r
99         mjson_snprintf(signatureJson, JSON_SIGNATURE_SIZE,\r
100             "{"\r
101                 "\"signatures\":{"\r
102                     "\"%s\":{"\r
103                         "\"ed25519:%s\":\"%.*s\""\r
104                     "}"\r
105                 "}"\r
106             "}",\r
107             client->userId,\r
108             //"1",\r
109             client->deviceId,\r
110             signatureLen, signature);\r
111 \r
112     struct mjson_fixedbuf result = { sOut, sOutCap, 0 };\r
113     mjson_merge(\r
114         sIn, sInLen,\r
115         signatureJson, signatureJsonLen,\r
116         mjson_print_fixed_buf,\r
117         &result);\r
118 \r
119     return true;\r
120 }\r
121 \r
122 \r
123 bool\r
124 MatrixOlmAccountInit(\r
125     MatrixOlmAccount * account)\r
126 {\r
127     account->account = olm_account(account->memory);\r
128 \r
129     static uint8_t random[OLM_ACCOUNT_RANDOM_SIZE];\r
130     Randomize(random, OLM_ACCOUNT_RANDOM_SIZE);\r
131 \r
132     size_t res = olm_create_account(\r
133         account->account,\r
134         random,\r
135         OLM_ACCOUNT_RANDOM_SIZE);\r
136 \r
137     return res != olm_error();\r
138 }\r
139 \r
140 bool\r
141 MatrixOlmAccountUnpickle(\r
142     MatrixOlmAccount * account,\r
143     void * pickled, int pickledLen,\r
144     const void * key, int keyLen)\r
145 {\r
146     size_t res;\r
147     res = olm_unpickle_account(account->account,\r
148         key, keyLen,\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
153     }\r
154     return res != olm_error();\r
155 }\r
156 \r
157 bool\r
158 MatrixOlmAccountGetDeviceKey(\r
159     MatrixOlmAccount * account,\r
160     char * key, int keyCap)\r
161 {\r
162     static char deviceKeysJson[OLM_IDENTITY_KEYS_JSON_SIZE];\r
163     size_t res =\r
164         olm_account_identity_keys(account->account,\r
165             deviceKeysJson, OLM_IDENTITY_KEYS_JSON_SIZE);\r
166     mjson_get_string(deviceKeysJson, res,\r
167         "$.curve25519",\r
168         key, keyCap);\r
169     return true;\r
170 }\r
171 \r
172 bool\r
173 MatrixOlmAccountGetSigningKey(\r
174     MatrixOlmAccount * account,\r
175     char * key, int keyCap)\r
176 {\r
177     static char deviceKeysJson[OLM_IDENTITY_KEYS_JSON_SIZE];\r
178     size_t res =\r
179         olm_account_identity_keys(account->account,\r
180             deviceKeysJson, OLM_IDENTITY_KEYS_JSON_SIZE);\r
181     mjson_get_string(deviceKeysJson, res,\r
182         "$.ed25519",\r
183         key, keyCap);\r
184     return true;\r
185 }\r
186 \r
187 // TODO:in/outbound sessions\r
188 bool\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
195 {\r
196     memset(session, 0, sizeof(MatrixOlmSession));\r
197 \r
198     session->deviceId = deviceId;\r
199 \r
200     session->session =\r
201         olm_session(session->memory);\r
202 \r
203     static uint8_t random[OLM_OUTBOUND_SESSION_RANDOM_SIZE];\r
204     Randomize(random, OLM_OUTBOUND_SESSION_RANDOM_SIZE);\r
205 \r
206     size_t res =\r
207         olm_create_outbound_session(session->session,\r
208             olmAccount,\r
209             deviceKey, strlen(deviceKey),\r
210             deviceOnetimeKey, strlen(deviceOnetimeKey),\r
211             random, OLM_OUTBOUND_SESSION_RANDOM_SIZE);\r
212     \r
213     if (res == olm_error()) {\r
214         printf("error olm:%s\n", olm_session_last_error(session->session));\r
215     }\r
216 \r
217     return session->session != NULL;\r
218 }\r
219 \r
220 bool\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
226 {\r
227     memset(session, 0, sizeof(MatrixOlmSession));\r
228 \r
229     session->deviceId = deviceId;\r
230 \r
231     session->session =\r
232         olm_session(session->memory);\r
233     \r
234     size_t res;\r
235     res = olm_unpickle_session(session->session,\r
236         key, keyLen,\r
237         pickled, pickledLen);\r
238     \r
239     if (res == olm_error()) {\r
240         printf("error unpickling olm session:%s\n", olm_session_last_error(session->session));\r
241     }\r
242 \r
243     return res != olm_error();\r
244 }\r
245 \r
246 bool\r
247 MatrixOlmSessionEncrypt(\r
248     MatrixOlmSession * session,\r
249     const char * plaintext,\r
250     char * outBuffer, int outBufferCap)\r
251 {\r
252     static uint8_t random[OLM_ENCRYPT_RANDOM_SIZE];\r
253     Randomize(random, OLM_ENCRYPT_RANDOM_SIZE);\r
254 \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
259 \r
260     return res != olm_error();\r
261 }\r
262 \r
263 bool\r
264 MatrixOlmSessionDecrypt(\r
265     MatrixOlmSession * session,\r
266     size_t messageType,\r
267     char * encrypted,\r
268     char * outBuffer, int outBufferCap)\r
269 {\r
270     static uint8_t random[OLM_ENCRYPT_RANDOM_SIZE];\r
271     Randomize(random, OLM_ENCRYPT_RANDOM_SIZE);\r
272 \r
273     size_t res =\r
274         olm_decrypt(session->session,\r
275             messageType,\r
276             encrypted, strlen(encrypted),\r
277             outBuffer, outBufferCap);\r
278     \r
279     if (res != olm_error() && res < outBufferCap)\r
280         outBuffer[outBufferCap] = '\0';\r
281 \r
282     return res != olm_error();\r
283 }\r
284 \r
285 // https://matrix.org/docs/guides/end-to-end-encryption-implementation-guide#starting-a-megolm-session\r
286 bool\r
287 MatrixMegolmOutSessionInit(\r
288     MatrixMegolmOutSession * session,\r
289     const char * roomId)\r
290 {\r
291     memset(session, 0, sizeof(MatrixMegolmOutSession));\r
292 \r
293     static uint8_t random[MEGOLM_INIT_RANDOM_SIZE];\r
294     Randomize(random, MEGOLM_INIT_RANDOM_SIZE);\r
295 \r
296     strncpy(session->roomId, roomId, ROOM_ID_SIZE);\r
297 \r
298     session->session =\r
299         olm_outbound_group_session(session->memory);\r
300 \r
301     olm_init_outbound_group_session(\r
302         session->session,\r
303         random,\r
304         MEGOLM_INIT_RANDOM_SIZE);\r
305 \r
306     olm_outbound_group_session_id(session->session,\r
307         (uint8_t *)session->id,\r
308         MEGOLM_SESSION_ID_SIZE);\r
309         \r
310     olm_outbound_group_session_key(session->session,\r
311         (uint8_t *)session->key,\r
312         MEGOLM_SESSION_KEY_SIZE);\r
313     \r
314     return true;\r
315 }\r
316 \r
317 bool\r
318 MatrixMegolmOutSessionEncrypt(\r
319     MatrixMegolmOutSession * session,\r
320     const char * plaintext,\r
321     char * outBuffer, int outBufferCap)\r
322 {\r
323     size_t res = olm_group_encrypt(session->session,\r
324         (uint8_t *)plaintext, strlen(plaintext),\r
325         (uint8_t *)outBuffer, outBufferCap);\r
326 \r
327     return res != olm_error();\r
328 }\r
329 \r
330 bool\r
331 MatrixMegolmOutSessionSave(\r
332     MatrixMegolmOutSession * session,\r
333     const char * filename,\r
334     const char * key)\r
335 {\r
336     FILE * f = fopen(filename, "w");\r
337 \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
341 \r
342     size_t pickleBufferLen =\r
343         olm_pickle_outbound_group_session_length(\r
344             session->session);\r
345     void * pickleBuffer = malloc(pickleBufferLen);\r
346 \r
347     olm_pickle_outbound_group_session(\r
348         session->session,\r
349         key, strlen(key),\r
350         pickleBuffer, pickleBufferLen);\r
351     \r
352     fwrite(&pickleBufferLen, sizeof(size_t), 1, f);\r
353     fwrite(pickleBuffer, 1, pickleBufferLen, f);\r
354     free(pickleBuffer);\r
355 \r
356     fclose(f);\r
357 \r
358     return true;\r
359 }\r
360 \r
361 bool\r
362 MatrixMegolmOutSessionLoad(\r
363     MatrixMegolmOutSession * session,\r
364     const char * filename,\r
365     const char * key)\r
366 {\r
367     FILE * f = fopen(filename, "r");\r
368 \r
369     size_t roomIdLen;\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
374 \r
375     size_t pickleBufferLen;\r
376     fread(&pickleBufferLen, sizeof(size_t), 1, f);\r
377 \r
378     void * pickleBuffer = malloc(pickleBufferLen);\r
379     fread(pickleBuffer, 1, pickleBufferLen, f);\r
380 \r
381     olm_unpickle_outbound_group_session(\r
382         session->session,\r
383         key, strlen(key),\r
384         pickleBuffer, pickleBufferLen);\r
385     \r
386     free(pickleBuffer);\r
387 \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
390 \r
391     fclose(f);\r
392 \r
393     return true;\r
394 }\r
395 \r
396 \r
397 \r
398 bool\r
399 MatrixClientInit(\r
400     MatrixClient * client,\r
401     const char * server)\r
402 {\r
403     memset(client, 0, sizeof(MatrixClient));\r
404 \r
405     strcpy(client->server, server);\r
406 \r
407     // init olm account\r
408     MatrixOlmAccountInit(&client->olmAccount);\r
409 \r
410     return true;\r
411 }\r
412 \r
413 bool\r
414 MatrixClientSave(\r
415     MatrixClient * client,\r
416     const char * filename)\r
417 {\r
418     FILE * f = fopen(filename, "w");\r
419     \r
420     \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
425 \r
426 \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
435 \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
440     }\r
441 \r
442     fclose(f);\r
443     return true;\r
444 }\r
445 \r
446 bool\r
447 MatrixClientLoad(\r
448     MatrixClient * client,\r
449     const char * filename)\r
450 {\r
451     FILE * f = fopen(filename, "r");\r
452     \r
453     \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
458 \r
459 \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
468 \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
473     }\r
474 \r
475     fclose(f);\r
476     return true;\r
477 }\r
478 \r
479 bool\r
480 MatrixClientSetAccessToken(\r
481     MatrixClient * client,\r
482     const char * accessToken)\r
483 {\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
487 \r
488     return true;\r
489 }\r
490 \r
491 bool\r
492 MatrixClientSetDeviceId(\r
493     MatrixClient * client,\r
494     const char * deviceId)\r
495 {\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
499 \r
500     return true;\r
501 }\r
502 \r
503 bool\r
504 MatrixClientSetUserId(\r
505     MatrixClient * client,\r
506     const char * userId)\r
507 {\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
511 \r
512     return true;\r
513 }\r
514 \r
515 bool\r
516 MatrixClientGenerateOnetimeKeys(\r
517     MatrixClient * client,\r
518     int numberOfKeys)\r
519 {\r
520     static uint8_t random[OLM_ONETIME_KEYS_RANDOM_SIZE];\r
521     Randomize(random, OLM_ONETIME_KEYS_RANDOM_SIZE);\r
522 \r
523     size_t res =\r
524         olm_account_generate_one_time_keys(client->olmAccount.account,\r
525             numberOfKeys, random, OLM_ONETIME_KEYS_RANDOM_SIZE);\r
526 \r
527     return res != olm_error();\r
528 }\r
529 \r
530 // https://spec.matrix.org/v1.7/client-server-api/#post_matrixclientv3keysupload\r
531 bool\r
532 MatrixClientUploadOnetimeKeys(\r
533     MatrixClient * client)\r
534 {\r
535     static char requestBuffer[KEYS_UPLOAD_REQUEST_SIZE];\r
536 \r
537     mjson_snprintf(requestBuffer, KEYS_UPLOAD_REQUEST_SIZE,\r
538         "{");\r
539 \r
540     static char onetimeKeysBuffer[1024];\r
541     olm_account_one_time_keys(client->olmAccount.account,\r
542         onetimeKeysBuffer, 1024);\r
543 \r
544     const char *keys;\r
545     int keysLen;\r
546     mjson_find(onetimeKeysBuffer, strlen(onetimeKeysBuffer), "$.curve25519", &keys, &keysLen);\r
547 \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
551         \r
552         int keyJsonLen =\r
553             snprintf(keyJson, JSON_ONETIME_KEY_SIZE,\r
554                 "{\"key\":\"%.*s\"}",\r
555                 vlen-2, keys + voff+1);\r
556 \r
557         static char keyJsonSigned[JSON_ONETIME_KEY_SIGNED_SIZE];\r
558 \r
559         JsonSign(client,\r
560             keyJson, keyJsonLen,\r
561             keyJsonSigned, JSON_ONETIME_KEY_SIGNED_SIZE);\r
562         \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
566             keyJsonSigned);\r
567     }\r
568 \r
569     if (requestBuffer[strlen(requestBuffer)-1] == ',')\r
570         requestBuffer[strlen(requestBuffer)-1] = '\0';\r
571 \r
572     mjson_snprintf(requestBuffer+strlen(requestBuffer), KEYS_UPLOAD_REQUEST_SIZE-strlen(requestBuffer),\r
573         "}");\r
574         \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
579         \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
586 \r
587     static char responseBuffer[KEYS_UPLOAD_RESPONSE_SIZE];\r
588     MatrixHttpPost(client,\r
589         KEYS_UPLOAD_URL,\r
590         finalEvent,\r
591         responseBuffer, KEYS_UPLOAD_RESPONSE_SIZE,\r
592         true);\r
593 \r
594     printf("%s\n", responseBuffer);\r
595 \r
596     return true;\r
597 }\r
598 \r
599 // https://spec.matrix.org/v1.7/client-server-api/#post_matrixclientv3keysupload\r
600 bool\r
601 MatrixClientUploadDeviceKey(\r
602     MatrixClient * client)\r
603 {\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
608 \r
609     static char deviceKeysBuffer[KEYS_UPLOAD_REQUEST_SIZE];\r
610 \r
611     int deviceKeysBufferLen =\r
612         mjson_snprintf(deviceKeysBuffer, KEYS_UPLOAD_REQUEST_SIZE,\r
613             "{"\r
614                 "\"algorithms\":[\"m.olm.v1.curve25519-aes-sha2\",\"m.megolm.v1.aes-sha2\"],"\r
615                 "\"device_id\":\"%s\","\r
616                 "\"keys\":{"\r
617                     "\"curve25519:%s\":\"%s\","\r
618                     "\"ed25519:%s\":\"%s\""\r
619                 "},"\r
620                 "\"user_id\":\"%s\""\r
621             "}",\r
622             client->deviceId,\r
623             client->deviceId, thisDeviceKey,\r
624             client->deviceId, thisSigningKey,\r
625             client->userId);\r
626 \r
627     static char deviceKeysSignedBuffer[KEYS_UPLOAD_REQUEST_SIGNED_SIZE];\r
628     JsonSign(client,\r
629         deviceKeysBuffer, deviceKeysBufferLen,\r
630         deviceKeysSignedBuffer, KEYS_UPLOAD_REQUEST_SIZE);\r
631     \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
635 \r
636     static char responseBuffer[KEYS_UPLOAD_RESPONSE_SIZE];\r
637     MatrixHttpPost(client,\r
638         KEYS_UPLOAD_URL,\r
639         finalEvent,\r
640         responseBuffer, KEYS_UPLOAD_RESPONSE_SIZE,\r
641         true);\r
642         \r
643     printf("%s\n", responseBuffer);\r
644 \r
645     return true;\r
646 }\r
647 \r
648 // https://spec.matrix.org/v1.7/client-server-api/#post_matrixclientv3keysclaim\r
649 bool\r
650 MatrixClientClaimOnetimeKey(\r
651     MatrixClient * client,\r
652     const char * userId,\r
653     const char * deviceId,\r
654     char * outOnetimeKey, int outOnetimeKeyCap)\r
655 {\r
656     static char requestBuffer[KEYS_CLAIM_REQUEST_SIZE];\r
657     mjson_snprintf(requestBuffer, KEYS_CLAIM_REQUEST_SIZE,\r
658     "{"\r
659       "\"one_time_keys\":{"\r
660         "\"%s\":{"\r
661           "\"%s\":\"signed_curve25519\""\r
662         "}"\r
663       "},"\r
664       "\"timeout\":10000"\r
665     "}",\r
666     userId,\r
667     deviceId);\r
668 \r
669     static char responseBuffer[KEYS_CLAIM_RESPONSE_SIZE];\r
670     MatrixHttpPost(client,\r
671         KEYS_CLAIM_URL,\r
672         requestBuffer,\r
673         responseBuffer, KEYS_CLAIM_RESPONSE_SIZE,\r
674         true);\r
675     \r
676     char userIdEscaped[USER_ID_SIZE];\r
677     JsonEscape(userId, strlen(userId),\r
678         userIdEscaped, USER_ID_SIZE);\r
679     \r
680     static char query[JSON_QUERY_SIZE];\r
681     snprintf(query, JSON_QUERY_SIZE,\r
682         "$.one_time_keys.%s.%s",\r
683         userIdEscaped,\r
684         deviceId);\r
685     \r
686     const char * keyObject;\r
687     int keyObjectSize;\r
688     mjson_find(responseBuffer, strlen(responseBuffer),\r
689         query,\r
690         &keyObject, &keyObjectSize);\r
691     \r
692     int koff, klen, voff, vlen, vtype;\r
693     mjson_next(keyObject, keyObjectSize, 0,\r
694         &koff, &klen, &voff, &vlen, &vtype);\r
695     \r
696     mjson_get_string(keyObject + voff, vlen,\r
697         "$.key", outOnetimeKey, outOnetimeKeyCap);\r
698     \r
699     // TODO:verify signature\r
700     \r
701     return true;\r
702 }\r
703 \r
704 // https://spec.matrix.org/v1.6/client-server-api/#post_matrixclientv3login\r
705 bool\r
706 MatrixClientLoginPassword(\r
707     MatrixClient * client,\r
708     const char * username,\r
709     const char * password,\r
710     const char * displayName)\r
711 {\r
712     static char requestBuffer[LOGIN_REQUEST_SIZE];\r
713 \r
714     mjson_snprintf(requestBuffer, LOGIN_REQUEST_SIZE,\r
715         "{"\r
716             "\"type\":\"m.login.password\","\r
717             "\"identifier\":{"\r
718                 "\"type\":\"m.id.user\","\r
719                 "\"user\":\"%s\""\r
720             "},"\r
721             "\"password\":\"%s\","\r
722             "\"initial_device_display_name\":\"%s\""\r
723         "}",\r
724         username,\r
725         password,\r
726         displayName);\r
727     \r
728     static char responseBuffer[LOGIN_RESPONSE_SIZE];\r
729     bool result =\r
730         MatrixHttpPost(client,\r
731             LOGIN_URL,\r
732             requestBuffer,\r
733             responseBuffer, LOGIN_RESPONSE_SIZE,\r
734             false);\r
735     \r
736     if (!result)\r
737         return false;\r
738     \r
739     int responseLen = strlen(responseBuffer);\r
740 \r
741     mjson_get_string(responseBuffer, responseLen,\r
742         "$.access_token",\r
743         client->accessToken, ACCESS_TOKEN_SIZE);\r
744     mjson_get_string(responseBuffer, responseLen,\r
745         "$.device_id",\r
746         client->deviceId, DEVICE_ID_SIZE);\r
747     mjson_get_string(responseBuffer, responseLen,\r
748         "$.expires_in_ms",\r
749         client->expireMs, EXPIRE_MS_SIZE);\r
750     mjson_get_string(responseBuffer, responseLen,\r
751         "$.refresh_token",\r
752         client->refreshToken, REFRESH_TOKEN_SIZE);\r
753 \r
754     return true;\r
755 }\r
756 \r
757 // https://spec.matrix.org/v1.6/client-server-api/#put_matrixclientv3roomsroomidsendeventtypetxnid\r
758 bool\r
759 MatrixClientSendEvent(\r
760     MatrixClient * client,\r
761     const char * roomId,\r
762     const char * msgType,\r
763     const char * msgBody)\r
764 {    \r
765     static char requestUrl[MAX_URL_LEN];\r
766     sprintf(requestUrl,\r
767         ROOM_SEND_URL, roomId, msgType, (int)time(NULL));\r
768 \r
769     static char responseBuffer[ROOM_SEND_RESPONSE_SIZE];\r
770     bool result =\r
771         MatrixHttpPut(client,\r
772             requestUrl,\r
773             msgBody,\r
774             responseBuffer, ROOM_SEND_RESPONSE_SIZE,\r
775             true);\r
776     \r
777     return result;\r
778 }\r
779 \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
782 bool\r
783 MatrixClientSendEventEncrypted(\r
784     MatrixClient * client,\r
785     const char * roomId,\r
786     const char * msgType,\r
787     const char * msgBody)\r
788 {\r
789     // event json\r
790     static char requestBuffer[ROOM_SEND_REQUEST_SIZE];\r
791     sprintf(requestBuffer,\r
792         "{"\r
793         "\"type\":\"%s\","\r
794         "\"content\":%s,"\r
795         "\"room_id\":\"%s\""\r
796         "}",\r
797         msgType,\r
798         msgBody,\r
799         roomId);\r
800 \r
801     // get megolm session\r
802     MatrixMegolmOutSession * outSession;\r
803     MatrixClientGetMegolmOutSession(client, roomId, &outSession);\r
804         \r
805     // encrypt\r
806     static char encryptedBuffer[ENCRYPTED_REQUEST_SIZE];\r
807     MatrixMegolmOutSessionEncrypt(outSession,\r
808         requestBuffer,\r
809         encryptedBuffer, ENCRYPTED_REQUEST_SIZE);\r
810 \r
811     char thisDeviceKey[DEVICE_KEY_SIZE];\r
812     MatrixOlmAccountGetDeviceKey(&client->olmAccount, thisDeviceKey, DEVICE_KEY_SIZE);\r
813     \r
814 \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
819 \r
820     static char encryptedEventBuffer[ENCRYPTED_EVENT_SIZE];\r
821     sprintf(encryptedEventBuffer,\r
822         "{"\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
828         "}",\r
829         encryptedBuffer,\r
830         deviceId,\r
831         senderKey,\r
832         sessionId);\r
833 \r
834     // send\r
835     return MatrixClientSendEvent(client,\r
836         roomId,\r
837         "m.room.encrypted",\r
838         encryptedEventBuffer);\r
839 }\r
840 \r
841 // https://spec.matrix.org/v1.6/client-server-api/#get_matrixclientv3sync\r
842 bool\r
843 MatrixClientSync(\r
844     MatrixClient * client,\r
845     char * outSyncBuffer, int outSyncCap)\r
846 {\r
847     return\r
848         MatrixHttpGet(client,\r
849             "/_matrix/client/v3/sync",\r
850             outSyncBuffer, outSyncCap,\r
851             true);\r
852 }\r
853 \r
854 // https://spec.matrix.org/v1.7/client-server-api/#get_matrixclientv3roomsroomideventeventid\r
855 bool\r
856 MatrixClientGetRoomEvent(\r
857     MatrixClient * client,\r
858     const char * roomId,\r
859     const char * eventId,\r
860     char * outEvent, int outEventCap)\r
861 {\r
862     static char url[MAX_URL_LEN];\r
863     snprintf(url, MAX_URL_LEN,\r
864         "/_matrix/client/v3/rooms/%s/event/%s",\r
865             roomId,\r
866             eventId);\r
867 \r
868     return\r
869         MatrixHttpGet(client,\r
870             url,\r
871             outEvent, outEventCap,\r
872             true);\r
873 }\r
874 \r
875 bool\r
876 MatrixClientShareMegolmOutSession(\r
877     MatrixClient * client,\r
878     const char * userId,\r
879     const char * deviceId,\r
880     MatrixMegolmOutSession * session)\r
881 {\r
882     // generate room key event\r
883     static char eventBuffer[KEY_SHARE_EVENT_LEN];\r
884     sprintf(eventBuffer,\r
885         "{"\r
886             "\"algorithm\":\"m.megolm.v1.aes-sha2\","\r
887             "\"room_id\":\"%s\","\r
888             "\"session_id\":\"%s\","\r
889             "\"session_key\":\"%s\""\r
890         "}",\r
891         session->roomId,\r
892         session->id,\r
893         session->key\r
894     );\r
895 \r
896     // // get olm session\r
897     // MatrixOlmSession * olmSession;\r
898     // MatrixClientGetOlmSession(client, userId, deviceId, &olmSession);\r
899 \r
900     // // encrypt\r
901     // char encryptedBuffer[KEY_SHARE_EVENT_LEN];\r
902     // MatrixOlmSessionEncrypt(olmSession,\r
903     //     eventBuffer,\r
904     //     encryptedBuffer, KEY_SHARE_EVENT_LEN);\r
905 \r
906     // send\r
907     MatrixClientSendToDeviceEncrypted(client,\r
908         userId,\r
909         deviceId,\r
910         eventBuffer,\r
911         "m.room_key");\r
912 \r
913     return true;\r
914 }\r
915 \r
916 bool\r
917 MatrixClientShareMegolmOutSessionTest(\r
918     MatrixClient * client,\r
919     const char * userId,\r
920     const char * deviceId,\r
921     MatrixMegolmOutSession * session)\r
922 {\r
923     // generate room key event\r
924     char eventBuffer[KEY_SHARE_EVENT_LEN];\r
925     sprintf(eventBuffer,\r
926         "{"\r
927             "\"algorithm\":\"m.megolm.v1.aes-sha2\","\r
928             "\"room_id\":\"%s\","\r
929             "\"session_id\":\"%s\","\r
930             "\"session_key\":\"%s\""\r
931         "}",\r
932         session->roomId,\r
933         session->id,\r
934         session->key\r
935     );\r
936 \r
937     // send\r
938     MatrixClientSendToDevice(client,\r
939         userId,\r
940         deviceId,\r
941         eventBuffer,\r
942         "m.room_key");\r
943 \r
944     return true;\r
945 }\r
946 \r
947 // bool\r
948 // MatrixClientSetMegolmOutSession(\r
949 //     MatrixClient * client,\r
950 //     const char * roomId,\r
951 //     MatrixMegolmOutSession session)\r
952 // {\r
953 //     if (client->numMegolmOutSessions < 10)\r
954 //     {\r
955 //         session.roomId = roomId;\r
956 //         client->megolmOutSessions[client->numMegolmOutSessions] = session;\r
957 //         client->numMegolmOutSessions++;\r
958 \r
959 //         return true;\r
960 //     }\r
961 //     return false;\r
962 // }\r
963 \r
964 bool\r
965 MatrixClientGetMegolmOutSession(\r
966     MatrixClient * client,\r
967     const char * roomId,\r
968     MatrixMegolmOutSession ** outSession)\r
969 {\r
970     for (int i = 0; i < client->numMegolmOutSessions; i++)\r
971     {\r
972         if (strcmp(client->megolmOutSessions[i].roomId, roomId) == 0)\r
973         {\r
974             *outSession = &client->megolmOutSessions[i];\r
975             return true;\r
976         }\r
977     }\r
978 \r
979     if (MatrixClientInitMegolmOutSession(client, roomId)) {\r
980         *outSession = &client->megolmOutSessions[client->numMegolmOutSessions-1];\r
981         return true;\r
982     }\r
983 \r
984     return false;\r
985 }\r
986 \r
987 bool\r
988 MatrixClientInitMegolmOutSession(\r
989     MatrixClient * client,\r
990     const char * roomId)\r
991 {\r
992     if (client->numMegolmOutSessions < NUM_MEGOLM_SESSIONS)\r
993     {\r
994         MatrixMegolmOutSessionInit(\r
995             &client->megolmOutSessions[client->numMegolmOutSessions],\r
996             roomId);\r
997         \r
998         client->numMegolmOutSessions++;\r
999 \r
1000         return true;\r
1001     }\r
1002     return false;\r
1003 }\r
1004 \r
1005 bool\r
1006 MatrixClientRequestMegolmInSession(\r
1007     MatrixClient * client,\r
1008     const char * roomId,\r
1009     const char * sessionId,\r
1010     const char * senderKey,\r
1011     const char * userId,\r
1012     const char * deviceId,\r
1013     MatrixMegolmInSession * outMegolmInSession)\r
1014 {\r
1015     // TODO: cancel requests\r
1016     MatrixClientSendDummy(client, userId, deviceId);\r
1017 \r
1018     static char event[ROOMKEY_REQUEST_SIZE];\r
1019     snprintf(event, ROOMKEY_REQUEST_SIZE,\r
1020         "{"\r
1021             "\"action\":\"request\","\r
1022             "\"body\":{"\r
1023                 "\"algorithm\":\"m.megolm.v1.aes-sha2\","\r
1024                 "\"room_id\":\"%s\","\r
1025                 "\"sender_key\":\"%s\","\r
1026                 "\"session_id\":\"%s\""\r
1027             "},"\r
1028             "\"request_id\":\"%d\","\r
1029             "\"requesting_device_id\":\"%s\""\r
1030         "}",\r
1031         roomId,\r
1032         senderKey,\r
1033         sessionId,\r
1034         time(NULL),\r
1035         client->deviceId);\r
1036 \r
1037     \r
1038     MatrixClientSendToDevice(client,\r
1039         userId,\r
1040         deviceId,\r
1041         event,\r
1042         "m.room_key_request");\r
1043 \r
1044     return true;\r
1045 }\r
1046 \r
1047 bool\r
1048 MatrixClientGetOlmSession(\r
1049     MatrixClient * client,\r
1050     const char * userId,\r
1051     const char * deviceId,\r
1052     MatrixOlmSession ** outSession)\r
1053 {\r
1054     for (int i = 0; i < client->numOlmSessions; i++)\r
1055     {\r
1056         if (strcmp(client->olmSessions[i].deviceId, deviceId) == 0)\r
1057         {\r
1058             *outSession = &client->olmSessions[i];\r
1059             return true;\r
1060         }\r
1061     }\r
1062 \r
1063     if (client->numOlmSessions < NUM_OLM_SESSIONS)\r
1064     {\r
1065         static char deviceKey[DEVICE_KEY_SIZE];\r
1066         MatrixClientRequestDeviceKey(client,\r
1067             deviceId,\r
1068             deviceKey, DEVICE_KEY_SIZE);\r
1069 \r
1070         char onetimeKey[ONETIME_KEY_SIZE];\r
1071         MatrixClientClaimOnetimeKey(client,\r
1072             userId,\r
1073             deviceId,\r
1074             onetimeKey, ONETIME_KEY_SIZE);\r
1075 \r
1076         MatrixOlmSessionTo(\r
1077             &client->olmSessions[client->numOlmSessions],\r
1078             client->olmAccount.account,\r
1079             deviceId,\r
1080             deviceKey,\r
1081             onetimeKey);\r
1082 \r
1083         *outSession = &client->olmSessions[client->numOlmSessions];\r
1084         \r
1085         client->numOlmSessions++;\r
1086 \r
1087         return true;\r
1088     }\r
1089 \r
1090     return false;\r
1091 }\r
1092 \r
1093 // https://spec.matrix.org/v1.6/client-server-api/#put_matrixclientv3sendtodeviceeventtypetxnid\r
1094 bool\r
1095 MatrixClientSendToDevice(\r
1096     MatrixClient * client,\r
1097     const char * userId,\r
1098     const char * deviceId,\r
1099     const char * message,\r
1100     const char * msgType)\r
1101 {\r
1102     static char requestUrl[MAX_URL_LEN];\r
1103     sprintf(requestUrl,\r
1104         TODEVICE_URL, msgType, (int)time(NULL));\r
1105 \r
1106     static char eventBuffer[TODEVICE_EVENT_SIZE];\r
1107     snprintf(eventBuffer, TODEVICE_EVENT_SIZE,\r
1108         "{"\r
1109             "\"messages\":{"\r
1110                 "\"%s\":{"\r
1111                     "\"%s\":%s"\r
1112                 "}"\r
1113             "}"\r
1114         "}",\r
1115         userId,\r
1116         deviceId,\r
1117         message);\r
1118 \r
1119     static char responseBuffer[ROOM_SEND_RESPONSE_SIZE];\r
1120     bool result =\r
1121         MatrixHttpPut(client,\r
1122             requestUrl,\r
1123             eventBuffer,\r
1124             responseBuffer, ROOM_SEND_RESPONSE_SIZE,\r
1125             true);\r
1126     \r
1127     printf("%s\n", responseBuffer);\r
1128     \r
1129     return result;\r
1130 }\r
1131 \r
1132 bool\r
1133 MatrixClientSendToDeviceEncrypted(\r
1134     MatrixClient * client,\r
1135     const char * userId,\r
1136     const char * deviceId,\r
1137     const char * message,\r
1138     const char * msgType)\r
1139 {\r
1140     // get olm session\r
1141     MatrixOlmSession * olmSession;\r
1142     MatrixClientGetOlmSession(client, userId, deviceId, &olmSession);\r
1143 \r
1144     // create event json\r
1145     char targetDeviceKey[DEVICE_KEY_SIZE];\r
1146     MatrixClientRequestDeviceKey(client, deviceId, targetDeviceKey, DEVICE_KEY_SIZE);\r
1147     char targetSigningKey[SIGNING_KEY_SIZE];\r
1148     MatrixClientRequestSigningKey(client, deviceId, targetSigningKey, SIGNING_KEY_SIZE);\r
1149     \r
1150     char thisSigningKey[DEVICE_KEY_SIZE];\r
1151     MatrixOlmAccountGetSigningKey(&client->olmAccount, thisSigningKey, DEVICE_KEY_SIZE);\r
1152 \r
1153     static char eventBuffer[TODEVICE_EVENT_SIZE];\r
1154     sprintf(eventBuffer,\r
1155         "{"\r
1156         "\"type\":\"%s\","\r
1157         "\"content\":%s,"\r
1158         "\"sender\":\"%s\","\r
1159         "\"recipient\":\"%s\","\r
1160         "\"recipient_keys\":{"\r
1161           "\"ed25519\":\"%s\""\r
1162         "},"\r
1163         "\"keys\":{"\r
1164           "\"ed25519\":\"%s\""\r
1165         "}"\r
1166         "}",\r
1167         msgType,\r
1168         message,\r
1169         client->userId,\r
1170         userId, // recipient user id\r
1171         targetSigningKey, // recipient device key\r
1172         thisSigningKey);\r
1173     \r
1174     printf("%s\n", eventBuffer);\r
1175 \r
1176     // encrypt\r
1177     static char encryptedBuffer[ENCRYPTED_REQUEST_SIZE];\r
1178     MatrixOlmSessionEncrypt(olmSession,\r
1179         eventBuffer,\r
1180         encryptedBuffer, ENCRYPTED_REQUEST_SIZE);\r
1181 \r
1182     char thisDeviceKey[DEVICE_KEY_SIZE];\r
1183     MatrixOlmAccountGetDeviceKey(&client->olmAccount, thisDeviceKey, DEVICE_KEY_SIZE);\r
1184 \r
1185 \r
1186     static char encryptedEventBuffer[ENCRYPTED_EVENT_SIZE];\r
1187     sprintf(encryptedEventBuffer,\r
1188         "{"\r
1189         "\"algorithm\":\"m.olm.v1.curve25519-aes-sha2\","\r
1190         "\"ciphertext\":{"\r
1191           "\"%s\":{"\r
1192             "\"body\":\"%s\","\r
1193             "\"type\":%d"\r
1194           "}"\r
1195         "},"\r
1196         "\"device_id\":\"%s\","\r
1197         "\"sender_key\":\"%s\""\r
1198         "}",\r
1199         targetDeviceKey,\r
1200         encryptedBuffer,\r
1201         olm_session_has_received_message(olmSession->session),\r
1202         client->deviceId,\r
1203         thisDeviceKey);\r
1204 \r
1205     // send\r
1206     return MatrixClientSendToDevice(\r
1207         client,\r
1208         userId,\r
1209         deviceId,\r
1210         encryptedEventBuffer,\r
1211         "m.room.encrypted");\r
1212 }\r
1213 \r
1214 bool\r
1215 MatrixClientSendDummy(\r
1216     MatrixClient * client,\r
1217     const char * userId,\r
1218     const char * deviceId)\r
1219 {\r
1220     return MatrixClientSendToDeviceEncrypted(\r
1221         client,\r
1222         userId,\r
1223         deviceId,\r
1224         "{}",\r
1225         "m.dummy");\r
1226 }\r
1227 \r
1228 bool\r
1229 MatrixClientFindDevice(\r
1230     MatrixClient * client,\r
1231     const char * deviceId,\r
1232     MatrixDevice ** outDevice)\r
1233 {\r
1234     MatrixClientRequestDeviceKeys(client);\r
1235 \r
1236     for (int i = 0; i < client->numDevices; i++)\r
1237     {\r
1238         if (strcmp(client->devices[i].deviceId, deviceId) == 0)\r
1239         {\r
1240             *outDevice = &client->devices[i];\r
1241             return true;\r
1242         }\r
1243     }\r
1244 \r
1245     *outDevice = NULL;\r
1246     return false;\r
1247 }\r
1248 \r
1249 bool\r
1250 MatrixClientRequestDeviceKey(\r
1251     MatrixClient * client,\r
1252     const char * deviceId,\r
1253     char * outDeviceKey, int outDeviceKeyCap)\r
1254 {\r
1255     MatrixDevice * device;\r
1256     \r
1257     if (MatrixClientFindDevice(client, deviceId, &device))\r
1258     {\r
1259         strncpy(outDeviceKey, device->deviceKey, outDeviceKeyCap);\r
1260         return true;\r
1261     }\r
1262 \r
1263     MatrixClientRequestDeviceKeys(client);\r
1264     \r
1265     if (MatrixClientFindDevice(client, deviceId, &device))\r
1266     {\r
1267         strncpy(outDeviceKey, device->deviceKey, outDeviceKeyCap);\r
1268         return true;\r
1269     }\r
1270 \r
1271     return false;\r
1272 }\r
1273 \r
1274 bool\r
1275 MatrixClientRequestSigningKey(\r
1276     MatrixClient * client,\r
1277     const char * deviceId,\r
1278     char * outSigningKey, int outSigningKeyCap)\r
1279 {\r
1280     MatrixDevice * device;\r
1281     \r
1282     if (MatrixClientFindDevice(client, deviceId, &device))\r
1283     {\r
1284         strncpy(outSigningKey, device->signingKey, outSigningKeyCap);\r
1285         return true;\r
1286     }\r
1287 \r
1288     MatrixClientRequestDeviceKeys(client);\r
1289     \r
1290     if (MatrixClientFindDevice(client, deviceId, &device))\r
1291     {\r
1292         strncpy(outSigningKey, device->signingKey, outSigningKeyCap);\r
1293         return true;\r
1294     }\r
1295 \r
1296     return false;\r
1297 }\r
1298 \r
1299 // https://spec.matrix.org/v1.6/client-server-api/#post_matrixclientv3keysquery\r
1300 bool\r
1301 MatrixClientRequestDeviceKeys(\r
1302     MatrixClient * client)\r
1303 {\r
1304     static char userIdEscaped[USER_ID_SIZE];\r
1305     JsonEscape(client->userId, strlen(client->userId),\r
1306         userIdEscaped, USER_ID_SIZE);\r
1307 \r
1308     static char request[KEYS_QUERY_REQUEST_SIZE];\r
1309     snprintf(request, KEYS_QUERY_REQUEST_SIZE,\r
1310         "{\"device_keys\":{\"%s\":[]}}", client->userId);\r
1311 \r
1312     static char responseBuffer[KEYS_QUERY_RESPONSE_SIZE];\r
1313     bool requestResult = MatrixHttpPost(client,\r
1314         KEYS_QUERY_URL,\r
1315         request,\r
1316         responseBuffer, KEYS_QUERY_RESPONSE_SIZE,\r
1317         true);\r
1318 \r
1319     if (! requestResult)\r
1320         return false;\r
1321 \r
1322     printf("keys:\n%s\n", responseBuffer);\r
1323 \r
1324     // query for retrieving device keys for user id\r
1325     static char query[JSON_QUERY_SIZE];\r
1326     snprintf(query, JSON_QUERY_SIZE,\r
1327         "$.device_keys.%s", userIdEscaped);\r
1328     \r
1329     const char * s;\r
1330     int slen;\r
1331     mjson_find(responseBuffer, strlen(responseBuffer),\r
1332         query, &s, &slen);\r
1333     \r
1334     // loop over keys\r
1335     \r
1336     int koff, klen, voff, vlen, vtype, off = 0;\r
1337     for (off = 0; (off = mjson_next(s, slen, off, &koff, &klen,\r
1338                                     &voff, &vlen, &vtype)) != 0; ) {\r
1339         const char * key = s + koff;\r
1340         const char * val = s + voff;\r
1341 \r
1342         // set device id, "key" is the JSON key\r
1343         MatrixDevice d;\r
1344         snprintf(d.deviceId, DEVICE_ID_SIZE,\r
1345             "%.*s", klen-2, key+1);\r
1346 \r
1347         // look for device key in value\r
1348         static char deviceKeyQuery[JSON_QUERY_SIZE];\r
1349         snprintf(deviceKeyQuery, JSON_QUERY_SIZE,\r
1350             "$.keys.curve25519:%s", d.deviceId);\r
1351         mjson_get_string(val, vlen,\r
1352             deviceKeyQuery, d.deviceKey, DEVICE_KEY_SIZE);\r
1353 \r
1354         // look for signing key in value\r
1355         static char signingKeyQuery[JSON_QUERY_SIZE];\r
1356         snprintf(signingKeyQuery, JSON_QUERY_SIZE,\r
1357             "$.keys.ed25519:%s", d.deviceId);\r
1358         mjson_get_string(val, vlen,\r
1359             signingKeyQuery, d.signingKey, SIGNING_KEY_SIZE);\r
1360 \r
1361         // add device\r
1362         if (client->numDevices < NUM_DEVICES)\r
1363         {\r
1364             bool foundDevice = false;\r
1365             for (int i = 0; i < client->numDevices; i++)\r
1366                 if (strcmp(client->devices[i].deviceId, d.deviceId) == 0)\r
1367                     foundDevice = true;\r
1368 \r
1369             if (! foundDevice) {\r
1370                 printf("new device: %s %s %s\n", d.deviceId, d.deviceKey, d.signingKey);\r
1371                 client->devices[client->numDevices] = d;\r
1372                 client->numDevices++;\r
1373             }\r
1374         }\r
1375         else\r
1376         {\r
1377             return false;\r
1378         }\r
1379     }\r
1380 \r
1381     return true;\r
1382 }\r
1383 \r
1384 bool\r
1385 MatrixClientDeleteDevice(\r
1386     MatrixClient * client)\r
1387 {\r
1388     static char deleteRequest[1024];\r
1389     snprintf(deleteRequest, 1024,\r
1390         "{\"devices\":[\"%s\"]}",\r
1391         client->deviceId);\r
1392     static char deleteResponse[1024];\r
1393     bool res = MatrixHttpPost(client, "/_matrix/client/v3/delete_devices",\r
1394         deleteRequest, deleteResponse, 1024, true);\r
1395     return res;\r
1396 }