diff --git a/src/stream/stream_buffer.c b/src/stream/stream_buffer.c index 2c371315..ec4acb74 100644 --- a/src/stream/stream_buffer.c +++ b/src/stream/stream_buffer.c @@ -27,6 +27,8 @@ struct stream_cached_batch { uint16_t count; uint8_t flags; int rejected; + int verified; + uint8_t verified_parts[(RS_MAX_BLOCKS + 7) / 8]; }; struct stream_buffer { @@ -419,8 +421,11 @@ int stream_buffer_decode(struct stream_buffer *b, uint64_t seq, uint8_t *output, return STREAM_ERR_ARGUMENT; } const uint8_t *blocks[RS_MAX_BLOCKS] = {0}; - for (uint32_t slot = batch->first_slot; slot != STREAM_SLOT_NONE; slot = b->slots[slot].next) - blocks[b->slots[slot].subblock] = b->data + slot * b->block_bytes + STREAM_BLOCK_RS_OFF; + for (uint32_t slot = batch->first_slot; slot != STREAM_SLOT_NONE; slot = b->slots[slot].next) { + unsigned sub = b->slots[slot].subblock; + if (!batch->verified || (batch->verified_parts[sub / 8] & (1u << (sub % 8)))) + blocks[sub] = b->data + slot * b->block_bytes + STREAM_BLOCK_RS_OFF; + } size_t decoded_size = (size_t)(b->descriptor.n - b->descriptor.r) * (batch->block_size - RS_HEADER_SIZE); if (rs_decode(b->rs, blocks, batch->block_size, b->decode_output, rs_input_capacity(b->rs)) != RS_OK) { DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "buffer decode RS failed seq=%llu present=%u", (unsigned long long)seq, batch->count); @@ -428,7 +433,8 @@ int stream_buffer_decode(struct stream_buffer *b, uint64_t seq, uint8_t *output, } struct stream_batch_info expected = {.seq = batch->seq, .data_length = batch->data_length, .timestamp_ms = batch->timestamp_ms, .block_size = batch->block_size, .flags = batch->flags}; - int result = stream_batch_verify(&b->descriptor, b->public_key, &expected, b->decode_output, decoded_size); + int result = batch->verified ? STREAM_OK + : stream_batch_verify(&b->descriptor, b->public_key, &expected, b->decode_output, decoded_size); if (result != STREAM_OK) { if (result != STREAM_ERR_SIGNATURE && result != STREAM_ERR_FORMAT) return result; batch->rejected = 1; b->stats.rejected_batches++; @@ -448,6 +454,16 @@ int stream_buffer_decode(struct stream_buffer *b, uint64_t seq, uint8_t *output, } return STREAM_REJECTED; } + if (!batch->verified) { + unsigned selected = 0; + for (unsigned sub = 0; sub < b->descriptor.n && selected < (unsigned)(b->descriptor.n - b->descriptor.r); sub++) { + if (!blocks[sub]) continue; + batch->verified_parts[sub / 8] |= (uint8_t)(1u << (sub % 8)); selected++; + } + batch->verified = 1; + DEBUG_TRACE(DEBUG_CATEGORY_STREAM, "buffer authenticated immutable RS set source=%llx seq=%llu parts=%u", + (unsigned long long)b->descriptor.source_node_id, (unsigned long long)seq, selected); + } memcpy(output, b->decode_output + STREAM_BATCH_HEADER, batch->data_length); *length = batch->data_length; DEBUG_TRACE(DEBUG_CATEGORY_STREAM, "buffer decoded seq=%llu present=%u length=%u", (unsigned long long)seq, batch->count, batch->data_length); diff --git a/src/stream/stream_buffer.h b/src/stream/stream_buffer.h index c5f0e07c..3ed684da 100644 --- a/src/stream/stream_buffer.h +++ b/src/stream/stream_buffer.h @@ -59,6 +59,8 @@ int stream_buffer_read(struct stream_buffer *buffer, uint64_t seq, uint8_t subbl int stream_buffer_range(struct stream_buffer *buffer, uint8_t subblock, uint64_t *first, uint64_t *last); /* Восстановить пачку и проверить одну подпись; достаточно capacity >= действительной длины. + * После успеха фиксируется использованный набор k неизменяемых частей: повторное чтение не проверяет подпись заново + * и не доверяет поздним частям, которые не участвовали в подтверждённом восстановлении. * STREAM_REJECTED означает неверную подпись/подписанные метаданные; такая пачка повторно не декодируется. * Выход не меняется при WAIT/GAP/EXPIRED/REJECTED/ошибке; отсутствующие части в кольцо не добавляются. */ int stream_buffer_decode(struct stream_buffer *buffer, uint64_t seq, uint8_t *output, size_t capacity, size_t *length);