|
|
|
|
@ -19,7 +19,8 @@ struct stream_encoder {
|
|
|
|
|
/* Проверить ключ трансляции и параметры до расчёта размеров/выделения памяти. */ |
|
|
|
|
int stream_descriptor_validate(const struct stream_descriptor *d) { |
|
|
|
|
if (!d || !d->group_id || !d->source_node_id || !d->source_epoch || !d->n || d->r >= d->n |
|
|
|
|
|| d->block_size <= RS_HEADER_SIZE || d->block_size > RS_MAX_BLOCK_SIZE) { |
|
|
|
|
|| d->block_size <= RS_HEADER_SIZE || d->block_size > RS_MAX_BLOCK_SIZE |
|
|
|
|
|| (size_t)(d->n - d->r) * (d->block_size - RS_HEADER_SIZE) <= STREAM_BATCH_OVERHEAD) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "descriptor invalid: present=%d group=%llx source=%llx epoch=%llx n=%u r=%u size=%u", |
|
|
|
|
d != NULL, d ? (unsigned long long)d->group_id : 0, d ? (unsigned long long)d->source_node_id : 0, |
|
|
|
|
d ? (unsigned long long)d->source_epoch : 0, d ? d->n : 0, d ? d->r : 0, d ? d->block_size : 0); |
|
|
|
|
@ -28,12 +29,13 @@ int stream_descriptor_validate(const struct stream_descriptor *d) {
|
|
|
|
|
return STREAM_OK; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
size_t stream_block_size(const struct stream_descriptor *d) { |
|
|
|
|
size_t stream_block_capacity(const struct stream_descriptor *d) { |
|
|
|
|
return stream_descriptor_validate(d) == STREAM_OK ? d->block_size + STREAM_BLOCK_OVERHEAD : 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
size_t stream_input_size(const struct stream_descriptor *d) { |
|
|
|
|
return stream_descriptor_validate(d) == STREAM_OK ? (size_t)(d->n - d->r) * (d->block_size - RS_HEADER_SIZE) : 0; |
|
|
|
|
return stream_descriptor_validate(d) == STREAM_OK |
|
|
|
|
? (size_t)(d->n - d->r) * (d->block_size - RS_HEADER_SIZE) - STREAM_BATCH_OVERHEAD : 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Создать рабочую память RS и получить публичный ключ именно переданного Ed25519 seed. */ |
|
|
|
|
@ -64,8 +66,8 @@ int stream_encoder_create(struct stream_encoder **out, const struct stream_descr
|
|
|
|
|
stream_encoder_destroy(encoder); |
|
|
|
|
return STREAM_ERR_FEC; |
|
|
|
|
} |
|
|
|
|
encoder->input = u_malloc((uint32_t)stream_input_size(d)); |
|
|
|
|
encoder->wire = u_malloc((uint32_t)(d->n * stream_block_size(d))); |
|
|
|
|
encoder->input = u_malloc((uint32_t)rs_input_capacity(encoder->rs)); |
|
|
|
|
encoder->wire = u_malloc((uint32_t)(d->n * stream_block_capacity(d))); |
|
|
|
|
if (!encoder->input || !encoder->wire) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "encoder working memory allocation failed n=%u size=%u", d->n, d->block_size); |
|
|
|
|
stream_encoder_destroy(encoder); |
|
|
|
|
@ -74,7 +76,7 @@ int stream_encoder_create(struct stream_encoder **out, const struct stream_descr
|
|
|
|
|
*out = encoder; |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_STREAM, "encoder created group=%llx source=%llx epoch=%llx FEC=%u+%u block=%zu input=%zu", |
|
|
|
|
(unsigned long long)d->group_id, (unsigned long long)d->source_node_id, (unsigned long long)d->source_epoch, |
|
|
|
|
d->n - d->r, d->r, stream_block_size(d), stream_input_size(d)); |
|
|
|
|
d->n - d->r, d->r, stream_block_capacity(d), stream_input_size(d)); |
|
|
|
|
return STREAM_OK; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -84,7 +86,7 @@ void stream_encoder_destroy(struct stream_encoder *encoder) {
|
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_STREAM, "encoder destroy source=%llx epoch=%llx", |
|
|
|
|
(unsigned long long)encoder->descriptor.source_node_id, (unsigned long long)encoder->descriptor.source_epoch); |
|
|
|
|
OPENSSL_cleanse(encoder->private_key, sizeof(encoder->private_key)); |
|
|
|
|
if (encoder->input) OPENSSL_cleanse(encoder->input, stream_input_size(&encoder->descriptor)); |
|
|
|
|
if (encoder->input) OPENSSL_cleanse(encoder->input, rs_input_capacity(encoder->rs)); |
|
|
|
|
u_free(encoder->input); |
|
|
|
|
u_free(encoder->wire); |
|
|
|
|
rs_free(encoder->rs); |
|
|
|
|
@ -96,85 +98,137 @@ const uint8_t *stream_encoder_public_key(const struct stream_encoder *encoder) {
|
|
|
|
|
return encoder->public_key; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Сформировать канонический заголовок, RS-кодограммы и подпись каждого блока. */ |
|
|
|
|
/* Подписать метаданные и содержимое один раз, затем защитить всё сообщение кодированием RS. */ |
|
|
|
|
int stream_encoder_encode(struct stream_encoder *encoder, uint64_t seq, uint16_t timestamp_ms, uint8_t flags, |
|
|
|
|
const uint8_t *input, size_t length, const uint8_t **blocks) { |
|
|
|
|
if (!encoder || !input || !blocks || !length || seq == UINT64_MAX || (flags & ~STREAM_BLOCK_TIME_RESET) |
|
|
|
|
const uint8_t *input, size_t length, const uint8_t **blocks, size_t *block_length) { |
|
|
|
|
if (!encoder || !input || !blocks || !block_length || !length || seq == UINT64_MAX || (flags & ~STREAM_BLOCK_TIME_RESET) |
|
|
|
|
|| length > stream_input_size(&encoder->descriptor)) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "encode invalid arguments seq=%llu len=%zu flags=%u", (unsigned long long)seq, length, flags); |
|
|
|
|
return STREAM_ERR_ARGUMENT; |
|
|
|
|
} |
|
|
|
|
const struct stream_descriptor *d = &encoder->descriptor; |
|
|
|
|
size_t wire_size = stream_block_size(d); |
|
|
|
|
size_t signed_size = STREAM_BATCH_HEADER + length, batch_size = signed_size + SC_SIGN_SIZE; |
|
|
|
|
size_t rs_size = RS_HEADER_SIZE + (batch_size + d->n - d->r - 1) / (d->n - d->r); |
|
|
|
|
size_t wire_size = STREAM_BLOCK_OVERHEAD + rs_size, stride = stream_block_capacity(d); |
|
|
|
|
uint8_t *rs_blocks[RS_MAX_BLOCKS]; |
|
|
|
|
memcpy(encoder->input, input, length); |
|
|
|
|
memset(encoder->input + length, 0, stream_input_size(d) - length); |
|
|
|
|
uint8_t *batch = encoder->input; |
|
|
|
|
memcpy(batch, STREAM_BATCH_DOMAIN, STREAM_BLOCK_DOMAIN_SIZE); |
|
|
|
|
stream_write_be(batch + STREAM_BLOCK_GROUP_OFF, d->group_id, 8); |
|
|
|
|
stream_write_be(batch + STREAM_BLOCK_SOURCE_OFF, d->source_node_id, 8); |
|
|
|
|
stream_write_be(batch + STREAM_BLOCK_EPOCH_OFF, d->source_epoch, 8); |
|
|
|
|
batch[STREAM_BLOCK_N_OFF] = d->n; batch[STREAM_BLOCK_R_OFF] = d->r; |
|
|
|
|
stream_write_be(batch + STREAM_BLOCK_SIZE_OFF, rs_size, 2); |
|
|
|
|
stream_write_be(batch + STREAM_BLOCK_TIMESTAMP_OFF, timestamp_ms, 2); |
|
|
|
|
stream_write_be(batch + STREAM_BLOCK_LENGTH_OFF, length, 4); |
|
|
|
|
batch[STREAM_BLOCK_FLAGS_OFF] = flags; |
|
|
|
|
stream_write_be(batch + STREAM_BATCH_SEQ_OFF, seq, 8); |
|
|
|
|
memcpy(batch + STREAM_BATCH_HEADER, input, length); |
|
|
|
|
if (sc_ed25519_sign(encoder->private_key, batch, signed_size, batch + signed_size) != SC_OK) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "encode batch signature failed source=%llx seq=%llu length=%zu", |
|
|
|
|
(unsigned long long)d->source_node_id, (unsigned long long)seq, length); |
|
|
|
|
return STREAM_ERR_CRYPTO; |
|
|
|
|
} |
|
|
|
|
for (unsigned i = 0; i < d->n; i++) { |
|
|
|
|
uint8_t *wire = encoder->wire + i * wire_size; |
|
|
|
|
uint8_t *wire = encoder->wire + i * stride; |
|
|
|
|
memcpy(wire, batch, STREAM_BLOCK_RS_OFF); |
|
|
|
|
memcpy(wire, STREAM_BLOCK_DOMAIN, STREAM_BLOCK_DOMAIN_SIZE); |
|
|
|
|
stream_write_be(wire + STREAM_BLOCK_GROUP_OFF, d->group_id, 8); |
|
|
|
|
stream_write_be(wire + STREAM_BLOCK_SOURCE_OFF, d->source_node_id, 8); |
|
|
|
|
stream_write_be(wire + STREAM_BLOCK_EPOCH_OFF, d->source_epoch, 8); |
|
|
|
|
wire[STREAM_BLOCK_N_OFF] = d->n; |
|
|
|
|
wire[STREAM_BLOCK_R_OFF] = d->r; |
|
|
|
|
stream_write_be(wire + STREAM_BLOCK_SIZE_OFF, d->block_size, 2); |
|
|
|
|
stream_write_be(wire + STREAM_BLOCK_TIMESTAMP_OFF, timestamp_ms, 2); |
|
|
|
|
stream_write_be(wire + STREAM_BLOCK_LENGTH_OFF, length, 4); |
|
|
|
|
wire[STREAM_BLOCK_FLAGS_OFF] = flags; |
|
|
|
|
rs_blocks[i] = wire + STREAM_BLOCK_RS_OFF; |
|
|
|
|
} |
|
|
|
|
if (rs_encode(encoder->rs, seq, encoder->input, rs_blocks) != RS_OK) { |
|
|
|
|
size_t encoded_size = 0; |
|
|
|
|
if (rs_encode(encoder->rs, seq, batch, batch_size, rs_blocks, &encoded_size) != RS_OK || encoded_size != rs_size) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "encode RS failed seq=%llu", (unsigned long long)seq); |
|
|
|
|
return STREAM_ERR_FEC; |
|
|
|
|
} |
|
|
|
|
for (unsigned i = 0; i < d->n; i++) { |
|
|
|
|
uint8_t *wire = encoder->wire + i * wire_size; |
|
|
|
|
if (sc_ed25519_sign(encoder->private_key, wire, wire_size - SC_SIGN_SIZE, wire + wire_size - SC_SIGN_SIZE) != SC_OK) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "encode signature failed seq=%llu sub=%u", (unsigned long long)seq, i); |
|
|
|
|
return STREAM_ERR_CRYPTO; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
for (unsigned i = 0; i < d->n; i++) blocks[i] = encoder->wire + i * wire_size; |
|
|
|
|
DEBUG_TRACE(DEBUG_CATEGORY_STREAM, "encoded seq=%llu timestamp=%u len=%zu n=%u flags=%u", |
|
|
|
|
(unsigned long long)seq, timestamp_ms, length, d->n, flags); |
|
|
|
|
for (unsigned i = 0; i < d->n; i++) blocks[i] = encoder->wire + i * stride; |
|
|
|
|
*block_length = wire_size; |
|
|
|
|
DEBUG_TRACE(DEBUG_CATEGORY_STREAM, "encoded signed batch seq=%llu timestamp=%u len=%zu n=%u flags=%u part=%zu max=%zu padding=%zu", |
|
|
|
|
(unsigned long long)seq, timestamp_ms, length, d->n, flags, wire_size, stride, |
|
|
|
|
(size_t)(d->n - d->r) * (rs_size - RS_HEADER_SIZE) - batch_size); |
|
|
|
|
return STREAM_OK; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Проверить размеры, область подписи, дескриптор, метаданные и подпись до публикации view. */ |
|
|
|
|
int stream_block_verify(const struct stream_descriptor *d, const uint8_t public_key[32], const uint8_t *wire, |
|
|
|
|
size_t length, struct stream_block_view *view) { |
|
|
|
|
if (!public_key || !wire || !view || stream_descriptor_validate(d) != STREAM_OK) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "verify invalid arguments length=%zu", length); |
|
|
|
|
/* Только структурная проверка до хранения/пересылки; подлинность определяет восстановленная пачка. */ |
|
|
|
|
int stream_block_parse(const struct stream_descriptor *d, const uint8_t *wire, size_t length, struct stream_block_view *view) { |
|
|
|
|
if (!wire || !view || stream_descriptor_validate(d) != STREAM_OK) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "part parse invalid arguments length=%zu", length); |
|
|
|
|
return STREAM_ERR_ARGUMENT; |
|
|
|
|
} |
|
|
|
|
if (length != stream_block_size(d) || memcmp(wire, STREAM_BLOCK_DOMAIN, STREAM_BLOCK_DOMAIN_SIZE) |
|
|
|
|
if (length <= STREAM_BLOCK_OVERHEAD + RS_HEADER_SIZE || length > stream_block_capacity(d) |
|
|
|
|
|| memcmp(wire, STREAM_BLOCK_DOMAIN, STREAM_BLOCK_DOMAIN_SIZE) |
|
|
|
|
|| stream_read_be(wire + STREAM_BLOCK_GROUP_OFF, 8) != d->group_id |
|
|
|
|
|| stream_read_be(wire + STREAM_BLOCK_SOURCE_OFF, 8) != d->source_node_id |
|
|
|
|
|| stream_read_be(wire + STREAM_BLOCK_EPOCH_OFF, 8) != d->source_epoch |
|
|
|
|
|| wire[STREAM_BLOCK_N_OFF] != d->n || wire[STREAM_BLOCK_R_OFF] != d->r |
|
|
|
|
|| stream_read_be(wire + STREAM_BLOCK_SIZE_OFF, 2) != d->block_size) { |
|
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_STREAM, "verify format/descriptor mismatch source=%llx epoch=%llx length=%zu expected=%zu", |
|
|
|
|
(unsigned long long)d->source_node_id, (unsigned long long)d->source_epoch, length, stream_block_size(d)); |
|
|
|
|
|| stream_read_be(wire + STREAM_BLOCK_SIZE_OFF, 2) != length - STREAM_BLOCK_OVERHEAD) { |
|
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_STREAM, "part format/descriptor mismatch source=%llx epoch=%llx length=%zu max=%zu", |
|
|
|
|
(unsigned long long)d->source_node_id, (unsigned long long)d->source_epoch, length, stream_block_capacity(d)); |
|
|
|
|
return STREAM_ERR_FORMAT; |
|
|
|
|
} |
|
|
|
|
struct stream_block_view parsed = { |
|
|
|
|
.seq = stream_read_be(wire + STREAM_BLOCK_SEQ_OFF, 8), |
|
|
|
|
.data_length = (uint32_t)stream_read_be(wire + STREAM_BLOCK_LENGTH_OFF, 4), |
|
|
|
|
.timestamp_ms = (uint16_t)stream_read_be(wire + STREAM_BLOCK_TIMESTAMP_OFF, 2), |
|
|
|
|
.subblock = wire[STREAM_BLOCK_SUB_OFF], .flags = wire[STREAM_BLOCK_FLAGS_OFF], .rs = wire + STREAM_BLOCK_RS_OFF |
|
|
|
|
.batch = {.seq = stream_read_be(wire + STREAM_BLOCK_SEQ_OFF, 8), |
|
|
|
|
.data_length = (uint32_t)stream_read_be(wire + STREAM_BLOCK_LENGTH_OFF, 4), |
|
|
|
|
.timestamp_ms = (uint16_t)stream_read_be(wire + STREAM_BLOCK_TIMESTAMP_OFF, 2), |
|
|
|
|
.block_size = (uint16_t)(length - STREAM_BLOCK_OVERHEAD), .flags = wire[STREAM_BLOCK_FLAGS_OFF]}, |
|
|
|
|
.subblock = wire[STREAM_BLOCK_SUB_OFF], .rs = wire + STREAM_BLOCK_RS_OFF |
|
|
|
|
}; |
|
|
|
|
if (parsed.seq == UINT64_MAX || parsed.subblock >= d->n || !parsed.data_length |
|
|
|
|
|| parsed.data_length > stream_input_size(d) || (parsed.flags & ~STREAM_BLOCK_TIME_RESET)) { |
|
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_STREAM, "verify invalid metadata seq=%llu sub=%u length=%u flags=%u", |
|
|
|
|
(unsigned long long)parsed.seq, parsed.subblock, parsed.data_length, parsed.flags); |
|
|
|
|
const struct stream_batch_info *info = &parsed.batch; |
|
|
|
|
if (info->seq == UINT64_MAX || parsed.subblock >= d->n || !info->data_length || info->data_length > stream_input_size(d) |
|
|
|
|
|| (info->flags & ~STREAM_BLOCK_TIME_RESET) |
|
|
|
|
|| info->block_size != RS_HEADER_SIZE + (STREAM_BATCH_OVERHEAD + info->data_length + d->n - d->r - 1) / (d->n - d->r)) { |
|
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_STREAM, "part invalid metadata seq=%llu sub=%u length=%u flags=%u size=%u", |
|
|
|
|
(unsigned long long)info->seq, parsed.subblock, info->data_length, info->flags, info->block_size); |
|
|
|
|
return STREAM_ERR_FORMAT; |
|
|
|
|
} |
|
|
|
|
*view = parsed; |
|
|
|
|
return STREAM_OK; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Подпись проверяется после RS; внешние подсказки обязаны совпадать с подписанными полями пачки. */ |
|
|
|
|
int stream_batch_verify(const struct stream_descriptor *d, const uint8_t public_key[32], |
|
|
|
|
const struct stream_batch_info *expected, const uint8_t *decoded, size_t decoded_length) { |
|
|
|
|
if (!public_key || !expected || !decoded || stream_descriptor_validate(d) != STREAM_OK) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "batch verify invalid arguments length=%zu", decoded_length); |
|
|
|
|
return STREAM_ERR_ARGUMENT; |
|
|
|
|
} |
|
|
|
|
if (expected->seq == UINT64_MAX || !expected->data_length || expected->data_length > stream_input_size(d) |
|
|
|
|
|| (expected->flags & ~STREAM_BLOCK_TIME_RESET) || expected->block_size > d->block_size |
|
|
|
|
|| expected->block_size != RS_HEADER_SIZE + (STREAM_BATCH_OVERHEAD + expected->data_length + d->n - d->r - 1) / (d->n - d->r) |
|
|
|
|
|| decoded_length != (size_t)(d->n - d->r) * (expected->block_size - RS_HEADER_SIZE) |
|
|
|
|
|| decoded_length < STREAM_BATCH_OVERHEAD + expected->data_length) { |
|
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_STREAM, "batch invalid size source=%llx seq=%llu decoded=%zu data=%u part=%u", |
|
|
|
|
(unsigned long long)d->source_node_id, (unsigned long long)expected->seq, |
|
|
|
|
decoded_length, expected->data_length, expected->block_size); |
|
|
|
|
return STREAM_ERR_FORMAT; |
|
|
|
|
} |
|
|
|
|
if (sc_ed25519_verify(public_key, wire, length - SC_SIGN_SIZE, wire + length - SC_SIGN_SIZE) != SC_OK) { |
|
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_STREAM, "verify source signature failed source=%llx epoch=%llx seq=%llu sub=%u", |
|
|
|
|
size_t signed_size = STREAM_BATCH_HEADER + expected->data_length; |
|
|
|
|
if (sc_ed25519_verify(public_key, decoded, signed_size, decoded + signed_size) != SC_OK) { |
|
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_STREAM, "batch source signature rejected source=%llx epoch=%llx seq=%llu data=%u part=%u", |
|
|
|
|
(unsigned long long)d->source_node_id, (unsigned long long)d->source_epoch, |
|
|
|
|
(unsigned long long)parsed.seq, parsed.subblock); |
|
|
|
|
(unsigned long long)expected->seq, expected->data_length, expected->block_size); |
|
|
|
|
return STREAM_ERR_SIGNATURE; |
|
|
|
|
} |
|
|
|
|
*view = parsed; |
|
|
|
|
if (memcmp(decoded, STREAM_BATCH_DOMAIN, STREAM_BLOCK_DOMAIN_SIZE) |
|
|
|
|
|| stream_read_be(decoded + STREAM_BLOCK_GROUP_OFF, 8) != d->group_id |
|
|
|
|
|| stream_read_be(decoded + STREAM_BLOCK_SOURCE_OFF, 8) != d->source_node_id |
|
|
|
|
|| stream_read_be(decoded + STREAM_BLOCK_EPOCH_OFF, 8) != d->source_epoch |
|
|
|
|
|| decoded[STREAM_BLOCK_N_OFF] != d->n || decoded[STREAM_BLOCK_R_OFF] != d->r |
|
|
|
|
|| stream_read_be(decoded + STREAM_BLOCK_SIZE_OFF, 2) != expected->block_size |
|
|
|
|
|| stream_read_be(decoded + STREAM_BLOCK_TIMESTAMP_OFF, 2) != expected->timestamp_ms |
|
|
|
|
|| stream_read_be(decoded + STREAM_BLOCK_LENGTH_OFF, 4) != expected->data_length |
|
|
|
|
|| decoded[STREAM_BLOCK_FLAGS_OFF] != expected->flags |
|
|
|
|
|| stream_read_be(decoded + STREAM_BATCH_SEQ_OFF, 8) != expected->seq) { |
|
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_STREAM, "batch signed metadata mismatch source=%llx epoch=%llx seq=%llu", |
|
|
|
|
(unsigned long long)d->source_node_id, (unsigned long long)d->source_epoch, (unsigned long long)expected->seq); |
|
|
|
|
return STREAM_ERR_FORMAT; |
|
|
|
|
} |
|
|
|
|
for (size_t i = signed_size + SC_SIGN_SIZE; i < decoded_length; i++) { |
|
|
|
|
if (decoded[i]) { |
|
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_STREAM, "batch nonzero padding source=%llx seq=%llu offset=%zu", |
|
|
|
|
(unsigned long long)d->source_node_id, (unsigned long long)expected->seq, i); |
|
|
|
|
return STREAM_ERR_FORMAT; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
DEBUG_TRACE(DEBUG_CATEGORY_STREAM, "batch source signature verified source=%llx seq=%llu length=%u part=%u", |
|
|
|
|
(unsigned long long)d->source_node_id, (unsigned long long)expected->seq, expected->data_length, expected->block_size); |
|
|
|
|
return STREAM_OK; |
|
|
|
|
} |
|
|
|
|
|