diff --git a/src/dm/dm_core.c b/src/dm/dm_core.c index 47dd400c5..d204cd3e6 100644 --- a/src/dm/dm_core.c +++ b/src/dm/dm_core.c @@ -430,7 +430,7 @@ static int dm_build_body(struct dm_state* dm, const struct dm_conv* c, uint64_t u_free(enc); return -1; } size_t body_len = DM_MSG_FIXED_HDR + ct_len + 2 + enc_len + (media ? DM_MEDIA_DESC_SIZE : 0) + DM_MSG_SIG_SIZE; - if (context_size) body_len += context_size + 1; + body_len += context_size; uint8_t* body = u_malloc(body_len); if (!body) { u_free(enc); return -1; } size_t off = 0; @@ -438,11 +438,11 @@ static int dm_build_body(struct dm_state* dm, const struct dm_conv* c, uint64_t memcpy(body + off, &seq, 8); off += 8; memcpy(body + off, &ts, 8); off += 8; memcpy(body + off, &author, 8); off += 8; - if (context_size) { body[off++] = 0; memcpy(body + off, context, context_size); off += context_size; } body[off++] = ct_len; memcpy(body + off, ct, ct_len); off += ct_len; - uint16_t el = (uint16_t)enc_len; + uint16_t el = (uint16_t)(enc_len | (context_size ? DM_CONTEXT_FLAG : 0)); memcpy(body + off, &el, 2); off += 2; + if (context_size) { memcpy(body + off, context, context_size); off += context_size; } memcpy(body + off, enc, enc_len); off += enc_len; if (media) { memcpy(body + off, media, DM_MEDIA_DESC_SIZE); off += DM_MEDIA_DESC_SIZE; } /* подпись по всем полям до sig */ diff --git a/src/dm/dm_protocol.c b/src/dm/dm_protocol.c index d4e01aa9b..67ff52239 100644 --- a/src/dm/dm_protocol.c +++ b/src/dm/dm_protocol.c @@ -23,8 +23,12 @@ int dm_message_parse(const uint8_t* body, size_t available, struct dm_message_vi memcpy(&view.conv, body, 8); memcpy(&view.seq, body + 8, 8); memcpy(&view.timestamp, body + 16, 8); memcpy(&view.author, body + 24, 8); view.content_type_size = body[32]; - if (!view.content_type_size) { + if (available - pos < (size_t)view.content_type_size + 2) return protocol_error("content type length"); + view.content_type = body + pos; pos += view.content_type_size; + memcpy(&view.ciphertext_size, body + pos, 2); pos += 2; + if (view.ciphertext_size & DM_CONTEXT_FLAG) { view.mode = DM_MODE_GROUPS; + view.ciphertext_size &= ~DM_CONTEXT_FLAG; if (available - pos < 1 + 2 * DM_PARTY_WIRE_SIZE + 2 || body[pos++] != 1) return protocol_error("context version/size"); if (dm_party_decode(body + pos, view.parties) || dm_party_decode(body + pos + DM_PARTY_WIRE_SIZE, view.parties + 1)) return protocol_error("context parties"); @@ -32,14 +36,9 @@ int dm_message_parse(const uint8_t* body, size_t available, struct dm_message_vi view.target_count = (uint16_t)(body[pos] | ((uint16_t)body[pos + 1] << 8)); pos += 2; if (view.target_count < 2 || view.target_count > DM_TARGET_MAX) return protocol_error("target count"); size_t targets_size = (size_t)view.target_count * DM_TARGET_PROOF_SIZE; - if (available - pos < targets_size + 1) return protocol_error("target list length"); + if (available - pos < targets_size) return protocol_error("target list length"); view.targets = body + pos; pos += targets_size; - view.content_type_size = body[pos++]; - if (!view.content_type_size) return protocol_error("empty content type"); } - if (available - pos < (size_t)view.content_type_size + 2) return protocol_error("content type length"); - view.content_type = body + pos; pos += view.content_type_size; - memcpy(&view.ciphertext_size, body + pos, 2); pos += 2; if (view.ciphertext_size < 16 || view.ciphertext_size > 1024 + 16 || available - pos < view.ciphertext_size) return protocol_error("ciphertext length"); view.ciphertext = body + pos; pos += view.ciphertext_size; diff --git a/src/dm/dm_protocol.h b/src/dm/dm_protocol.h index 1b7fbb609..c26cc84cd 100644 --- a/src/dm/dm_protocol.h +++ b/src/dm/dm_protocol.h @@ -1,6 +1,6 @@ /* dm_protocol — единый разбор тела DEVICE/GROUPS и сертификатов адресатов. - * DEVICE сохраняет существующий формат. GROUPS занимает зарезервированный - * ct_len=0 и помещает типизированные стороны/адресатов перед настоящим ct. + * DEVICE сохраняет существующий формат, включая пустой content type. GROUPS + * ставит старший бит data_len и помещает контекст перед ciphertext. * Тело подписывается целиком, включая снимок; parser не проверяет подпись автора. * Указатели view заимствуют входной буфер. SQL-функция encode вызывается в uasync. */ #ifndef UTUN_DM_PROTOCOL_H @@ -13,8 +13,9 @@ 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 + DM_CONTEXT_MAX + 1 + 255 + 2 + 1024 + 16 + 56 + 64) +#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. */ @@ -46,7 +47,7 @@ int dm_message_target(const struct dm_message_view* view, uint64_t node, uint8_t /* 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 (без префикса сообщения/нулевого ct_len). +/* 0/-1: сформировать контекст GROUPS (между data_len с флагом и ciphertext). * VERSION=1:u8, parties:2*73,count:LE16,sorted certificates:count*137. * Сторона 0 должна включать текущего отправителя. out меняется только при успехе; * DM_CONTEXT_MAX достаточно. Источник включается, неизвестные устройства не ищутся. */