diff --git a/src/dm/dm_crypto.c b/src/dm/dm_crypto.c index 12deafe6..50226472 100644 --- a/src/dm/dm_crypto.c +++ b/src/dm/dm_crypto.c @@ -14,6 +14,19 @@ #define DM_ID "dm_crypto" +/* Медиа использует тот же ключ target, но отдельное пространство nonce. */ +void dm_build_media_nonce(uint64_t author, const uint8_t media_id[16], uint64_t part, uint8_t nonce[DM_NONCE_SIZE]) { + SHA256_CTX ctx; + uint8_t hash[32]; + SHA256_Init(&ctx); + SHA256_Update(&ctx, "utun_dm_media", 13); + SHA256_Update(&ctx, &author, sizeof(author)); + SHA256_Update(&ctx, media_id, 16); + SHA256_Update(&ctx, &part, sizeof(part)); + SHA256_Final(hash, &ctx); + memcpy(nonce, hash, DM_NONCE_SIZE); +} + 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; @@ -80,10 +93,17 @@ void dm_build_nonce(uint64_t conv_id, uint64_t author, uint64_t seq, uint8_t non 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; + if (!key || !nonce || (!in && in_len) || !out || !out_len || + (encrypt ? in_len > DM_CCM_PLAIN_MAX : in_len < DM_TAG_SIZE || in_len - DM_TAG_SIZE > DM_CCM_PLAIN_MAX)) { + DEBUG_ERROR(DEBUG_CATEGORY_DM, "%s: invalid CCM arguments encrypt=%d bytes=%zu", DM_ID, encrypt, in_len); + return -1; + } + *out_len = 0; + static const uint8_t empty = 0; + if (!in) in = ∅ EVP_CIPHER_CTX* ectx = EVP_CIPHER_CTX_new(); - if (!ectx) return -1; + if (!ectx) { DEBUG_ERROR(DEBUG_CATEGORY_DM, "%s: CCM context allocation failed", DM_ID); return -1; } const EVP_CIPHER* cipher = EVP_aes_256_ccm(); int rc = -1; @@ -93,6 +113,7 @@ static int dm_ccm_crypt(const uint8_t key[DM_CONTENT_KEY_SIZE], const uint8_t no || 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, NULL, &outl, NULL, (int)in_len) != 1) goto done; 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; @@ -106,14 +127,14 @@ static int dm_ccm_crypt(const uint8_t key[DM_CONTENT_KEY_SIZE], const uint8_t no || 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, NULL, &outl, NULL, (int)body) != 1) goto done; 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); + if (rc) DEBUG_ERROR(DEBUG_CATEGORY_DM, "%s: CCM %s failed bytes=%zu", DM_ID, encrypt ? "encrypt" : "decrypt", in_len); return rc; } diff --git a/src/dm/dm_crypto.h b/src/dm/dm_crypto.h index 6ca1ae3c..f59f6f42 100644 --- a/src/dm/dm_crypto.h +++ b/src/dm/dm_crypto.h @@ -20,6 +20,8 @@ extern "C" { #define DM_CONTENT_KEY_SIZE 32 #define DM_NONCE_SIZE 13 /* AES-CCM требует ровно 13 байт */ #define DM_TAG_SIZE 16 +#define DM_CCM_PLAIN_MAX 65535u +#define DM_MEDIA_PLAIN_SIZE 32768u /* conv_id = SHA256("utun_dm_v1" || min(a,b) || max(a,b))[..8] & 0x7FFF..., * 63-битное число, одинаковое на обеих сторонах (одна ветка на пару). */ @@ -35,6 +37,9 @@ int dm_derive_content_key(const uint8_t my_priv[32], const uint8_t peer_pub[32], * не пересекается между направлениями (author различается). */ void dm_build_nonce(uint64_t conv_id, uint64_t author, uint64_t seq, uint8_t nonce[DM_NONCE_SIZE]); +/* Nonce порции медиа; media_id неизменен на всех повторах одной отправки. */ +void dm_build_media_nonce(uint64_t author, const uint8_t media_id[16], uint64_t part, uint8_t nonce[DM_NONCE_SIZE]); + /* AES-256-CCM. ciphertext = ciphertext(plain_len) || tag(DM_TAG_SIZE). * out должен вмещать plain_len + DM_TAG_SIZE. 0=ок, <0=ошибка. */ int dm_encrypt(const uint8_t key[DM_CONTENT_KEY_SIZE], const uint8_t nonce[DM_NONCE_SIZE],