/* dm_protocol — единый разбор тела DEVICE/GROUPS и сертификатов адресатов. * DEVICE сохраняет существующий формат, включая пустой content type. GROUPS * ставит старший бит data_len и помещает контекст перед ciphertext. * Тело подписывается целиком, включая снимок; parser не проверяет подпись автора. * Указатели view заимствуют входной буфер. SQL-функция encode вызывается в uasync. */ #ifndef UTUN_DM_PROTOCOL_H #define UTUN_DM_PROTOCOL_H #include "dm_identity.h" #ifdef __cplusplus extern "C" { #endif #define DM_TARGET_PROOF_SIZE 137 /* node:LE64,side:u8,x:32,ed:32,member_sig:64. */ #define DM_CONTEXT_FLAG 0x8000u /* Старший бит uint16 data_len; фактическая длина не более1040. */ #define DM_CONTEXT_MAX (1 + 2 * DM_PARTY_WIRE_SIZE + 2 + DM_TARGET_MAX * DM_TARGET_PROOF_SIZE) #define DM_BODY_MAX (33 + 255 + 2 + DM_CONTEXT_MAX + 1024 + 16 + 56 + 64) struct dm_message_view { uint64_t conv, seq, timestamp, author; /* Общий префикс, native-endian. */ enum dm_mode mode; struct dm_party parties[2]; /* Только GROUPS: сторона автора, другая сторона. */ uint16_t target_count; /* GROUPS: снимок включает автора. DEVICE: 0. */ const uint8_t* targets; /* target_count сертификатов по DM_TARGET_PROOF_SIZE. */ const uint8_t* content_type; /* Не NUL-строка, ровно content_type_size байт. */ uint8_t content_type_size; const uint8_t* ciphertext; /* ciphertext_size байт, включая CCM tag. */ uint16_t ciphertext_size; const uint8_t* media; /* UUID/size/hash:56 либо NULL. */ const uint8_t* signature; /* Подпись автора:64. */ size_t size; /* Длина тела; parser допускает хвост после него. */ }; /* 0/-1: проверить границы/форму тела и вернуть view. Выход не меняется при ошибке. * Проверка точной длины, подписей и адресата — обязанность принимающего модуля. */ int dm_message_parse(const uint8_t* body, size_t available, struct dm_message_view* out); /* 0/-1: GROUPS проверяет ID беседы, упорядоченный уникальный snapshot, обе стороны, * наличие автора на стороне 0 и подписи удостоверений. DEVICE — пустая операция: * ID/подпись конкретной пары проверяет dm_core. Ключи самих устройств связаны с node_id. */ int dm_message_verify_targets(const struct dm_message_view* view); /* GROUPS: найти node в снимке, 0/1 — сторона, -1 — отсутствует. При успехе * x/ed (если не NULL) получают по 32 байта. Для DEVICE всегда -1. */ int dm_message_target(const struct dm_message_view* view, uint64_t node, uint8_t x[32], uint8_t ed[32]); /* 0/-1: прочитать node/side по индексу GROUPS-snapshot, без проверки подписи. */ int dm_message_target_at(const struct dm_message_view* view, unsigned index, struct dm_target* out); /* 0/-1: сформировать контекст GROUPS (между data_len с флагом и ciphertext). * VERSION=1:u8, parties:2*73,count:LE16,sorted certificates:count*137. * Сторона 0 должна включать текущего отправителя. out меняется только при успехе; * DM_CONTEXT_MAX достаточно. Источник включается, неизвестные устройства не ищутся. */ int dm_context_encode(struct UTUN_INSTANCE* inst, const struct dm_party parties[2], uint8_t* out, size_t capacity, size_t* length); #ifdef __cplusplus } #endif #endif