7 #define SERVER "https://matrix.org"
\r
8 #define USER_ID "@pscho:matrix.org"
\r
10 #define DEVICE_ID "ULZZOKJBYN"
\r
11 #define SENDER_KEY "cjP41XzRlY+pd8DoiBuKQJj9o15mrx6gkrpqTkAPZ2c"
\r
12 #define ROOM_ID "!XKFUjAsGrSSrpDFIxB:matrix.org"
\r
13 #define EVENT_ID "$vOS09eUaI0CduqAcaIU5ZVk6ljLQfLspz7UThP8vaUM"
\r
14 #define SESSION_ID "90UbGLue3ADVhvW7hFjoA2c6yg0JJKs/lPdMDZXnZAk"
\r
17 bool verified = false;
\r
18 char transactionId[256];
\r
19 OlmSAS * olmSas = NULL;
\r
23 MatrixClient * client,
\r
24 const char * event, int eventLen
\r
26 static char eventType[128];
\r
27 memset(eventType, 0, sizeof(eventType));
\r
28 mjson_get_string(event, eventLen, "$.type", eventType, 128);
\r
30 if (strcmp(eventType, "m.key.verification.request") == 0) {
\r
31 mjson_get_string(event, eventLen, "$.content.transaction_id", transactionId, 256);
\r
33 char verificationReadyBuffer[2048];
\r
34 snprintf(verificationReadyBuffer, 2048,
\r
36 "\"from_device\":\"%s\","
\r
37 "\"methods\":[\"m.sas.v1\"],"
\r
38 "\"transaction_id\":\"%s\""
\r
43 MatrixClientSendToDevice(client,
\r
46 verificationReadyBuffer,
\r
47 "m.key.verification.ready");
\r
49 else if (strcmp(eventType, "m.key.verification.start") == 0) {
\r
50 olmSas = olm_sas(malloc(olm_sas_size()));
\r
51 void * sasRandomBytes = malloc(olm_create_sas_random_length(olmSas));
\r
52 olm_create_sas(olmSas,
\r
54 olm_create_sas_random_length(olmSas));
\r
56 OlmUtility * olmUtil = olm_utility(malloc(olm_utility_size()));
\r
58 char publicKey[128];
\r
59 char keyStartJsonCanonical[1024];
\r
61 char commitment[256];
\r
62 olm_sas_get_pubkey(olmSas,
\r
65 printf("public key: %.*s\n", olm_sas_pubkey_length(olmSas), publicKey);
\r
67 const char * keyStartJson;
\r
68 int keyStartJsonLen;
\r
69 mjson_find(event, eventLen, "$.content", &keyStartJson, &keyStartJsonLen);
\r
70 JsonCanonicalize(keyStartJson, keyStartJsonLen, keyStartJsonCanonical, 1024);
\r
72 printf("json:\n%.*s\ncanonical json:\n%s\n", keyStartJsonLen, keyStartJson, keyStartJsonCanonical);
\r
75 snprintf(concat, 1024, "%.*s%s", olm_sas_pubkey_length(olmSas), publicKey, keyStartJsonCanonical);
\r
78 olm_sha256(olmUtil, concat, concatLen, commitment, 256);
\r
80 char verificationAcceptBuffer[2048];
\r
81 snprintf(verificationAcceptBuffer, 2048,
\r
83 "\"commitment\":\"%.*s\","
\r
84 "\"hash\":\"sha256\","
\r
85 "\"key_agreement_protocol\":\"curve25519\","
\r
86 "\"message_authentication_code\":\"hkdf-hmac-sha256.v2\","
\r
87 "\"method\":\"m.sas.v1\","
\r
88 "\"short_authentication_string\":[\"decimal\"],"
\r
89 "\"transaction_id\":\"%s\""
\r
91 commitmentLen, commitment,
\r
94 MatrixClientSendToDevice(client,
\r
97 verificationAcceptBuffer,
\r
98 "m.key.verification.accept");
\r
100 else if (strcmp(eventType, "m.key.verification.key") == 0) {
\r
101 char publicKey[128];
\r
102 olm_sas_get_pubkey(olmSas,
\r
106 char theirPublicKey[128];
\r
107 int theirPublicKeyLen =
\r
108 mjson_get_string(event, eventLen, "$.content.key", theirPublicKey, 128);
\r
110 printf("event: %.*s\n", eventLen, event);
\r
111 printf("theirPublicKey: %.*s\n", theirPublicKeyLen, theirPublicKey);
\r
112 printf("publicKey: %.*s\n", olm_sas_pubkey_length(olmSas), publicKey);
\r
114 olm_sas_set_their_key(olmSas, theirPublicKey, theirPublicKeyLen);
\r
116 char verificationKeyBuffer[2048];
\r
117 snprintf(verificationKeyBuffer, 2048,
\r
119 "\"key\":\"%.*s\","
\r
120 "\"transaction_id\":\"%s\""
\r
122 olm_sas_pubkey_length(olmSas), publicKey,
\r
125 MatrixClientSendToDevice(client,
\r
128 verificationKeyBuffer,
\r
129 "m.key.verification.key");
\r
132 char hkdfInfo[1024];
\r
134 snprintf(hkdfInfo, 1024,
\r
135 "MATRIX_KEY_VERIFICATION_SAS%s%s%s%s%s",
\r
142 unsigned char sasBytes[5];
\r
143 olm_sas_generate_bytes(olmSas,
\r
144 hkdfInfo, hkdfInfoLen,
\r
146 int b0 = sasBytes[0];
\r
147 int b1 = sasBytes[1];
\r
148 int b2 = sasBytes[2];
\r
149 int b3 = sasBytes[3];
\r
150 int b4 = sasBytes[4];
\r
152 printf("%d %d %d %d %d\n", b0, b1, b2, b3, b4);
\r
154 // https://spec.matrix.org/v1.7/client-server-api/#sas-method-decimal
\r
155 printf("%d | %d | %d\n",
\r
156 (b0 << 5 | b1 >> 3) + 1000,
\r
157 ((b1 & 0x7) << 10 | b2 << 2 | b3 >> 6) + 1000,
\r
158 ((b3 & 0x3F) << 7 | b4 >> 1) + 1000);
\r
159 printf("%d | %d | %d\n",
\r
160 ((b0 << 5) | (b1 >> 3)) + 1000,
\r
161 (((b1 & 0x7) << 10) | (b2 << 2) | (b3 >> 6)) + 1000,
\r
162 (((b3 & 0x3F) << 7) | (b4 >> 1)) + 1000);
\r
164 else if (strcmp(eventType, "m.key.verification.mac") == 0) {
\r
166 const char * masterKey = "vt8tJ5/SxqkvXS+XoGxr+4rJNe8fJfZT3/e/FTwlFsI";
\r
168 char keyList[1024];
\r
169 char keyListMac[1024];
\r
172 char key1Mac[1024];
\r
175 char key2Mac[1024];
\r
177 if (strcmp(masterKey, client->deviceId) < 0) {
\r
178 snprintf(key1Id, 1024, "ed25519:%s", masterKey);
\r
179 strcpy(key1, masterKey);
\r
180 snprintf(key2Id, 1024, "ed25519:%s", client->deviceId);
\r
181 MatrixOlmAccountGetSigningKey(&client->olmAccount, key2, 1024);
\r
184 snprintf(key1Id, 1024, "ed25519:%s", client->deviceId);
\r
185 MatrixOlmAccountGetSigningKey(&client->olmAccount, key1, 1024);
\r
186 snprintf(key2Id, 1024, "ed25519:%s", masterKey);
\r
187 strcpy(key2, masterKey);
\r
190 snprintf(keyList, 1024,
\r
191 "%s,%s", key1Id, key2Id);
\r
193 char macInfo[1024];
\r
197 snprintf(macInfo, 1024,
\r
198 "MATRIX_KEY_VERIFICATION_MAC%s%s%s%s%s%s",
\r
205 olm_sas_calculate_mac_fixed_base64(olmSas, keyList, strlen(keyList), macInfo, macInfoLen, keyListMac, 1024);
\r
209 snprintf(macInfo, 1024,
\r
210 "MATRIX_KEY_VERIFICATION_MAC%s%s%s%s%s%s",
\r
217 olm_sas_calculate_mac_fixed_base64(olmSas, key1, strlen(key1), macInfo, macInfoLen, key1Mac, 1024);
\r
221 snprintf(macInfo, 1024,
\r
222 "MATRIX_KEY_VERIFICATION_MAC%s%s%s%s%s%s",
\r
229 olm_sas_calculate_mac_fixed_base64(olmSas, key2, strlen(key2), macInfo, macInfoLen, key2Mac, 1024);
\r
232 char verificationMacBuffer[2048];
\r
233 snprintf(verificationMacBuffer, 2048,
\r
240 "\"transaction_id\":\"%s\""
\r
249 MatrixClientSendToDevice(client,
\r
252 verificationMacBuffer,
\r
253 "m.key.verification.mac");
\r
255 char verificationDoneBuffer[2048];
\r
256 snprintf(verificationDoneBuffer, 2048,
\r
258 "\"transaction_id\":\"%s\""
\r
262 MatrixClientSendToDevice(client,
\r
265 verificationDoneBuffer,
\r
266 "m.key.verification.done");
\r
270 else if (strcmp(eventType, "m.room.encrypted") == 0) {
\r
271 static char algorithm[128];
\r
272 mjson_get_string(event, eventLen, "$.content.algorithm", algorithm, 128);
\r
274 if (strcmp(algorithm, "m.olm.v1.curve25519-aes-sha2") == 0) {
\r
275 static char thisDeviceKey[DEVICE_KEY_SIZE];
\r
276 MatrixOlmAccountGetDeviceKey(&client->olmAccount, thisDeviceKey, DEVICE_KEY_SIZE);
\r
278 static char jp[128];
\r
279 snprintf(jp, 128, "$.content.ciphertext.%s.type", thisDeviceKey);
\r
281 double messageType;
\r
282 mjson_get_number(event, eventLen, jp, &messageType);
\r
283 int messageTypeInt = (int)messageType;
\r
285 static char encrypted[2048];
\r
286 static char decrypted[2048];
\r
288 snprintf(jp, 128, "$.content.ciphertext.%s.body", thisDeviceKey);
\r
290 mjson_get_string(event, eventLen, jp, encrypted, 2048);
\r
292 MatrixOlmSession * olmSession;
\r
293 if (messageTypeInt == 0) {
\r
294 MatrixClientNewOlmSessionIn(client,
\r
300 MatrixClientNewOlmSessionOut(client,
\r
306 printf("event: %.*s\n", eventLen, event);
\r
307 printf("encrypted: %s\n", encrypted);
\r
309 MatrixOlmSessionDecrypt(olmSession,
\r
310 messageTypeInt, encrypted, decrypted, 2048);
\r
312 printf("decrypted: %s\n", decrypted);
\r
314 HandleEvent(client, decrypted, strlen(decrypted));
\r
317 else if (strcmp(eventType, "m.room_key") == 0) {
\r
318 static char roomId[128];
\r
319 static char sessionId[128];
\r
320 static char sessionKey[1024];
\r
321 mjson_get_string(event, eventLen, "$.content.room_id", roomId, 128);
\r
322 mjson_get_string(event, eventLen, "$.content.session_id", sessionId, 128);
\r
323 mjson_get_string(event, eventLen, "$.content.session_key", sessionKey, 1024);
\r
325 printf("sessionId: %s\n", sessionId);
\r
326 printf("sessionKey: %s\n", sessionKey);
\r
328 MatrixMegolmInSession * megolmInSession;
\r
329 MatrixClientNewMegolmInSession(client, roomId, sessionId, sessionKey, &megolmInSession);
\r
331 else if (strcmp(eventType, "m.forwarded_room_key") == 0) {
\r
332 static char roomId[128];
\r
333 static char sessionId[128];
\r
334 static char sessionKey[1024];
\r
335 mjson_get_string(event, eventLen, "$.content.room_id", roomId, 128);
\r
336 mjson_get_string(event, eventLen, "$.content.session_id", sessionId, 128);
\r
337 mjson_get_string(event, eventLen, "$.content.session_key", sessionKey, 1024);
\r
339 printf("sessionId: %s\n", sessionId);
\r
340 printf("sessionKey: %s\n", sessionKey);
\r
342 MatrixMegolmInSession * megolmInSession;
\r
343 MatrixClientNewMegolmInSession(client, roomId, sessionId, sessionKey, &megolmInSession);
\r
349 MatrixClient * client,
\r
350 const char * room, int roomLen,
\r
351 const char * event, int eventLen)
\r
353 static char eventType[128];
\r
354 memset(eventType, 0, sizeof(eventType));
\r
355 mjson_get_string(event, eventLen, "$.type", eventType, 128);
\r
357 if (strcmp(eventType, "m.room.encrypted") == 0) {
\r
358 static char algorithm[128];
\r
359 mjson_get_string(event, eventLen, "$.content.algorithm", algorithm, 128);
\r
361 if (strcmp(algorithm, "m.megolm.v1.aes-sha2") == 0) {
\r
362 static char sessionId[128];
\r
364 mjson_get_string(event, eventLen, "$.content.session_id", sessionId, 128);
\r
368 MatrixMegolmInSession * megolmInSession;
\r
369 res = MatrixClientGetMegolmInSession(client,
\r
371 sessionId, sessionIdLen,
\r
375 static char encrypted[2048];
\r
376 static char decrypted[2048];
\r
377 mjson_get_string(event, eventLen, "$.content.ciphertext", encrypted, 2048);
\r
379 MatrixMegolmInSessionDecrypt(megolmInSession, encrypted, strlen(encrypted), decrypted, 2048);
\r
381 printf("decrypted: %s\n", decrypted);
\r
383 HandleEvent(client, decrypted, strlen(decrypted));
\r
386 printf("megolm session not known\n");
\r
390 HandleEvent(client, event, eventLen);
\r
395 MatrixClient * client
\r
397 static char nextBatch[1024];
\r
399 static char syncBuffer[1024*50];
\r
400 MatrixClientSync(client, syncBuffer, 1024*50, nextBatch);
\r
404 const char * s = syncBuffer;
\r
405 int slen = strlen(syncBuffer);
\r
408 // int koff, klen, voff, vlen, vtype, off = 0;
\r
409 // for (off = 0; (off = mjson_next(s, slen, off, &koff, &klen,
\r
410 // &voff, &vlen, &vtype)) != 0; ) {
\r
411 // const char * k = s + koff;
\r
412 // const char * v = s + voff;
\r
414 // printf("%.*s: %.100s\n", klen, k, v);
\r
418 mjson_get_string(s, slen, "$.next_batch", nextBatch, 1024);
\r
422 const char * events;
\r
425 mjson_find(s, slen, "$.to_device.events", &events, &eventsLen);
\r
427 if (res != MJSON_TOK_INVALID) {
\r
429 int koff, klen, voff, vlen, vtype, off = 0;
\r
430 for (off = 0; (off = mjson_next(events, eventsLen, off, &koff, &klen,
\r
431 &voff, &vlen, &vtype)) != 0; ) {
\r
432 const char * v = events + voff;
\r
434 HandleEvent(client, v, vlen);
\r
441 const char * rooms;
\r
444 mjson_find(s, slen, "$.rooms.join", &rooms, &roomsLen);
\r
446 if (res != MJSON_TOK_INVALID) {
\r
448 int koff, klen, voff, vlen, vtype, off = 0;
\r
449 for (off = 0; (off = mjson_next(rooms, roomsLen, off, &koff, &klen,
\r
450 &voff, &vlen, &vtype)) != 0; ) {
\r
451 const char * k = rooms + koff;
\r
452 const char * v = rooms + voff;
\r
454 const char * events;
\r
457 mjson_find(v, vlen, "$.timeline.events", &events, &eventsLen);
\r
459 if (res != MJSON_TOK_INVALID) {
\r
461 int koff2, klen2, voff2, vlen2, vtype2, off2 = 0;
\r
462 for (off2 = 0; (off2 = mjson_next(events, eventsLen, off2, &koff2, &klen2,
\r
463 &voff2, &vlen2, &vtype2)) != 0; ) {
\r
464 const char * v2 = events + voff2;
\r
466 HandleRoomEvent(client,
\r
481 MatrixClient client;
\r
482 MatrixClientInit(&client);
\r
483 MatrixHttpInit(&client.hc, SERVER);
\r
484 MatrixClientSetUserId(&client, USER_ID);
\r
486 MatrixClientLoginPassword(&client,
\r
490 printf("deviceId: %s\n", client.deviceId);
\r
491 MatrixClientGenerateOnetimeKeys(&client, 10);
\r
492 MatrixClientUploadOnetimeKeys(&client);
\r
493 MatrixClientUploadDeviceKeys(&client);
\r
495 static char eventBuffer[1024];
\r
496 MatrixClientGetRoomEvent(&client,
\r
499 eventBuffer, 1024);
\r
500 printf("event: %s\n", eventBuffer);
\r
505 // while (getchar() != 'q')
\r
508 MatrixClientRequestMegolmInSession(&client,
\r
515 MatrixMegolmInSession * megolmInSession;
\r
516 while (! MatrixClientGetMegolmInSession(&client,
\r
517 ROOM_ID, strlen(ROOM_ID),
\r
518 SESSION_ID, strlen(SESSION_ID),
\r
522 static char encryptedBuffer[1024];
\r
523 int encryptedBufferLen =
\r
524 mjson_get_string(eventBuffer, strlen(eventBuffer), "$.content.ciphertext", encryptedBuffer, 1024);
\r
526 printf("encryptedBuffer: [%.*s]\n", encryptedBufferLen, encryptedBuffer);
\r
528 static char decryptedBuffer[1024];
\r
529 MatrixMegolmInSessionDecrypt(megolmInSession,
\r
530 encryptedBuffer, encryptedBufferLen,
\r
531 decryptedBuffer, 1024);
\r
533 printf("decrypted: %s\n", decryptedBuffer);
\r
535 MatrixClientDeleteDevice(&client);
\r
537 MatrixHttpDeinit(&client.hc);
\r