dm: прямой обмен сообщениями (DM) + soundtouch + архитектура звонков
- src/dm/: dm_core/dm_crypto/dm_mailbox — прямой обмен сообщениями (DM) - lib/soundtouch-master/: vendored SoundTouch (изменение темпа/высоты аудио) - doc/dm_arch.md, doc/chat_call_arch.md: архитектура DM и звонков - tests: test_dm.c, test_dm_e2e.c - chat: рефакторинг chat_* модулей, member_sync, invite_build - chatgui/chatgui-android: синхронизация с новым API чата - lib/u_async, debug_config, utun_instance: сопутствующие изменения
This commit is contained in:
1 parent
761f64cf76
commit
73fe99b33f
123 files changed
+14095
-1270
No files matched your search
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* dm_crypto.c — криптография прямого чата (DM)
|
||||
*/
|
||||
|
||||
#include "dm_crypto.h"
|
||||
#include "../../lib/debug_config.h"
|
||||
|
||||
#define OPENSSL_API_COMPAT 0x10100000L
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/sha.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define DM_ID "dm_crypto"
|
||||
|
||||
uint64_t dm_derive_conv_id(uint64_t a, uint64_t b) {
|
||||
uint64_t lo = a < b ? a : b;
|
||||
uint64_t hi = a < b ? b : a;
|
||||
|
||||
SHA256_CTX ctx;
|
||||
uint8_t hash[32];
|
||||
SHA256_Init(&ctx);
|
||||
SHA256_Update(&ctx, "utun_dm_v1", 9);
|
||||
SHA256_Update(&ctx, &lo, 8);
|
||||
SHA256_Update(&ctx, &hi, 8);
|
||||
SHA256_Final(hash, &ctx);
|
||||
|
||||
uint64_t id;
|
||||
memcpy(&id, hash, 8);
|
||||
id &= 0x7FFFFFFFFFFFFFFFULL;
|
||||
if (id == 0) id = 1;
|
||||
return id;
|
||||
}
|
||||
|
||||
int dm_derive_content_key(const uint8_t my_priv[32], const uint8_t peer_pub[32],
|
||||
uint8_t out[DM_CONTENT_KEY_SIZE]) {
|
||||
if (!my_priv || !peer_pub || !out) return -1;
|
||||
|
||||
EVP_PKEY* my_pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_X25519, NULL, my_priv, 32);
|
||||
if (!my_pkey) { DEBUG_ERROR(DEBUG_CATEGORY_DM, "%s: EVP_PKEY_new_raw_private_key failed", DM_ID); return -1; }
|
||||
EVP_PKEY* peer_pkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_X25519, NULL, peer_pub, 32);
|
||||
if (!peer_pkey) { EVP_PKEY_free(my_pkey); DEBUG_ERROR(DEBUG_CATEGORY_DM, "%s: EVP_PKEY_new_raw_public_key failed", DM_ID); return -1; }
|
||||
|
||||
EVP_PKEY_CTX* dctx = EVP_PKEY_CTX_new(my_pkey, NULL);
|
||||
uint8_t shared[32];
|
||||
size_t shared_len = sizeof(shared);
|
||||
int rc = -1;
|
||||
if (!dctx) goto out;
|
||||
if (EVP_PKEY_derive_init(dctx) <= 0) goto out;
|
||||
if (EVP_PKEY_derive_set_peer(dctx, peer_pkey) <= 0) goto out;
|
||||
if (EVP_PKEY_derive(dctx, shared, &shared_len) <= 0 || shared_len != sizeof(shared)) goto out;
|
||||
|
||||
SHA256_CTX sctx;
|
||||
SHA256_Init(&sctx);
|
||||
SHA256_Update(&sctx, shared, sizeof(shared));
|
||||
SHA256_Update(&sctx, "utun_dm_content", 15);
|
||||
SHA256_Final(out, &sctx);
|
||||
rc = 0;
|
||||
|
||||
out:
|
||||
if (dctx) EVP_PKEY_CTX_free(dctx);
|
||||
EVP_PKEY_free(my_pkey);
|
||||
EVP_PKEY_free(peer_pkey);
|
||||
if (rc != 0) DEBUG_ERROR(DEBUG_CATEGORY_DM, "%s: X25519 derive failed", DM_ID);
|
||||
return rc;
|
||||
}
|
||||
|
||||
void dm_build_nonce(uint64_t conv_id, uint64_t author, uint64_t seq, uint8_t nonce[DM_NONCE_SIZE]) {
|
||||
SHA256_CTX ctx;
|
||||
uint8_t hash[32];
|
||||
SHA256_Init(&ctx);
|
||||
SHA256_Update(&ctx, &conv_id, 8);
|
||||
SHA256_Update(&ctx, &author, 8);
|
||||
SHA256_Update(&ctx, &seq, 8);
|
||||
SHA256_Final(hash, &ctx);
|
||||
memcpy(nonce, hash, DM_NONCE_SIZE);
|
||||
}
|
||||
|
||||
static int dm_ccm_crypt(const uint8_t key[DM_CONTENT_KEY_SIZE], const uint8_t nonce[DM_NONCE_SIZE],
|
||||
const uint8_t* in, size_t in_len, uint8_t* out, size_t* out_len,
|
||||
int encrypt, const uint8_t* tag_in) {
|
||||
if (!key || !nonce || !in || !out || !out_len) return -1;
|
||||
|
||||
EVP_CIPHER_CTX* ectx = EVP_CIPHER_CTX_new();
|
||||
if (!ectx) return -1;
|
||||
const EVP_CIPHER* cipher = EVP_aes_256_ccm();
|
||||
|
||||
int rc = -1;
|
||||
if (encrypt) {
|
||||
if (EVP_EncryptInit_ex(ectx, cipher, NULL, NULL, NULL) != 1
|
||||
|| EVP_CIPHER_CTX_ctrl(ectx, EVP_CTRL_AEAD_SET_IVLEN, DM_NONCE_SIZE, NULL) != 1
|
||||
|| EVP_CIPHER_CTX_ctrl(ectx, EVP_CTRL_AEAD_SET_TAG, DM_TAG_SIZE, NULL) != 1
|
||||
|| EVP_EncryptInit_ex(ectx, NULL, NULL, key, nonce) != 1) goto done;
|
||||
int outl;
|
||||
if (EVP_EncryptUpdate(ectx, out, &outl, in, (int)in_len) != 1 || outl != (int)in_len) goto done;
|
||||
int tmpl;
|
||||
if (EVP_EncryptFinal_ex(ectx, out + outl, &tmpl) != 1) goto done;
|
||||
if (EVP_CIPHER_CTX_ctrl(ectx, EVP_CTRL_AEAD_GET_TAG, DM_TAG_SIZE, out + in_len) != 1) goto done;
|
||||
*out_len = in_len + DM_TAG_SIZE;
|
||||
} else {
|
||||
if (in_len < DM_TAG_SIZE) goto done;
|
||||
size_t body = in_len - DM_TAG_SIZE;
|
||||
if (EVP_DecryptInit_ex(ectx, cipher, NULL, NULL, NULL) != 1
|
||||
|| EVP_CIPHER_CTX_ctrl(ectx, EVP_CTRL_AEAD_SET_IVLEN, DM_NONCE_SIZE, NULL) != 1
|
||||
|| EVP_CIPHER_CTX_ctrl(ectx, EVP_CTRL_AEAD_SET_TAG, DM_TAG_SIZE, (void*)(tag_in ? tag_in : in + body)) != 1
|
||||
|| EVP_DecryptInit_ex(ectx, NULL, NULL, key, nonce) != 1) goto done;
|
||||
int outl;
|
||||
if (EVP_DecryptUpdate(ectx, out, &outl, in, (int)body) != 1 || outl != (int)body) goto done;
|
||||
int tmpl;
|
||||
if (EVP_DecryptFinal_ex(ectx, out + outl, &tmpl) != 1) goto done;
|
||||
*out_len = body;
|
||||
}
|
||||
rc = 0;
|
||||
done:
|
||||
EVP_CIPHER_CTX_free(ectx);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int dm_encrypt(const uint8_t key[DM_CONTENT_KEY_SIZE], const uint8_t nonce[DM_NONCE_SIZE],
|
||||
const uint8_t* plain, size_t plain_len, uint8_t* out, size_t* out_len) {
|
||||
return dm_ccm_crypt(key, nonce, plain, plain_len, out, out_len, 1, NULL);
|
||||
}
|
||||
|
||||
int dm_decrypt(const uint8_t key[DM_CONTENT_KEY_SIZE], const uint8_t nonce[DM_NONCE_SIZE],
|
||||
const uint8_t* ct, size_t ct_len, uint8_t* out, size_t* out_len) {
|
||||
return dm_ccm_crypt(key, nonce, ct, ct_len, out, out_len, 0, NULL);
|
||||
}
|
||||
Reference in new issue
Block a user