From 44acb5aecf44be7f55ec57556943aa12e008ce0f Mon Sep 17 00:00:00 2001 From: evgeny Date: Sat, 24 Oct 2026 01:03:24 +0200 Subject: [PATCH] Document streaming signature state ownership and retries --- src/transport_layer/secure_channel_doc.md | 47 +++++++++++++++-------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/src/transport_layer/secure_channel_doc.md b/src/transport_layer/secure_channel_doc.md index ff6b4245..a08591a0 100644 --- a/src/transport_layer/secure_channel_doc.md +++ b/src/transport_layer/secure_channel_doc.md @@ -122,23 +122,38 @@ int ok = sc_ed25519_verify(pubkey, msg, msg_len, sig); // SC_OK при успе **Стриминговая (потоковая) подпись:** SHA-512 аккумулятор + one-shot Ed25519 в финале. Подходит для данных любого размера (память O(1)): ```c -struct sc_stream_sign_state state; +struct sc_stream_sign_state state = {0}; uint8_t stream_sig[SC_SIGN_SIZE]; size_t sig_len = sizeof(stream_sig); +int result; // Подпись -sc_stream_sign_init(&ctx, &state); -sc_stream_sign_update(&state, chunk1, len1); -sc_stream_sign_update(&state, chunk2, len2); -sc_stream_sign_final(&state, stream_sig, &sig_len); // sig_len — capacity на входе, длина подписи на выходе; cleanup +if ((result = sc_stream_sign_init(&ctx, &state)) != SC_OK) goto stream_done; +if ((result = sc_stream_sign_update(&state, chunk1, len1)) != SC_OK) goto stream_done; +if ((result = sc_stream_sign_update(&state, chunk2, len2)) != SC_OK) goto stream_done; +if ((result = sc_stream_sign_final(&state, stream_sig, &sig_len)) != SC_OK) goto stream_done; // Верификация -sc_stream_sign_verify_init(&state, ed25519_pubkey); -sc_stream_sign_update(&state, chunk1, len1); -sc_stream_sign_update(&state, chunk2, len2); -int result = sc_stream_sign_verify(&state, stream_sig, sig_len); // SC_OK при успехе, отрицательный SC_ERR_* при ошибке +if ((result = sc_stream_sign_verify_init(&state, ed25519_pubkey)) != SC_OK) goto stream_done; +if ((result = sc_stream_sign_update(&state, chunk1, len1)) != SC_OK) goto stream_done; +if ((result = sc_stream_sign_update(&state, chunk2, len2)) != SC_OK) goto stream_done; +result = sc_stream_sign_verify(&state, stream_sig, sig_len); + +stream_done: +sc_stream_sign_cleanup(&state); // Безопасно после ошибок, final/verify и предыдущей очистки. +// result: SC_OK при успехе, отрицательный SC_ERR_* при ошибке. ``` +Перед первым использованием состояние обнуляют. `init` требует пустого состояния и создаёт ресурсы +до его заполнения: отказ оставляет все поля пустыми. Повторный `init` активного состояния возвращает +`SC_ERR_INVALID_ARG`, сохраняя ресурсы и накопленный хеш; для новой операции сначала нужен `cleanup`. +Подписи достаточно локального ключа из инициализированного `ctx`; ECDH с пиром не требуется. + +`final` проверяет capacity не менее 64 до финализации. NULL аргументы, недостаточная capacity и неверный +режим не меняют состояние или выходной буфер. После начала финализации `final/verify` освобождают +состояние при любом результате, в том числе неверной подписи. При ошибке EVP update нужна явная очистка. +`cleanup` обнуляет все поля и допускает повторные вызовы и NULL. + **Деривация Ed25519-ключа из X25519:** `sc_derive_ed25519_pubkey(x25519_privkey, ed25519_pubkey)` берёт первые 32 байта `SHA512(x25519_privkey[32])` как Ed25519 seed и извлекает из него публичный ключ (32 байта). Потоковая подпись использует тот же производный ключ; one-shot API принимает непосредственно Ed25519-ключи. @@ -209,8 +224,8 @@ sc_sha_transcode(key, key_len, data, data_len); **`struct sc_stream_sign_state`** — состояние стриминговой подписи (Ed25519 + SHA-512): - `md_ctx` — `EVP_MD_CTX*` (SHA-512 аккумулятор, opaque) - `pkey` — `EVP_PKEY*` (Ed25519 ключ, opaque) -- `initialized` — флаг инициализации -- `is_sign` — 1 = режим подписи, 0 = режим верификации +- `initialized` — 1 после успешного init; 0 в пустом состоянии +- `is_sign` — в активном состоянии 1 = подпись, 0 = верификация; после cleanup равен 0 ### 3.3. Функции @@ -267,12 +282,12 @@ Nonce не зависит от часов, счётчиков контекста | Функция | Описание | |---|---| -| `sc_stream_sign_init(ctx, state)` | Инициализирует подпись. Ed25519 seed — первые 32 байта `SHA512(x25519_privkey[32])`. | -| `sc_stream_sign_verify_init(state, ed25519_pubkey)` | Инициализирует верификацию с переданным Ed25519 pubkey. | +| `sc_stream_sign_init(ctx, state)` | Инициализирует подпись в пустом state. Ed25519 seed — первые 32 байта `SHA512(x25519_privkey[32])`. Ошибка сохраняет state. | +| `sc_stream_sign_verify_init(state, ed25519_pubkey)` | Инициализирует верификацию с переданным Ed25519 pubkey[32] в пустом state; ошибка сохраняет state. | | `sc_stream_sign_update(state, data, len)` | SHA-512 update (инкрементальный). Нулевая длина — no-op. | -| `sc_stream_sign_final(state, sig_out, &sig_len)` | Завершает SHA-512 → one-shot Ed25519 подпись хэша → **автоматический cleanup**. На входе sig_len задаёт capacity sig_out (не менее 64), при успехе на выходе 64. | -| `sc_stream_sign_verify(state, sig, sig_len)` | Завершает SHA-512 → one-shot Ed25519 проверка → **автоматический cleanup**. Возвращает `SC_OK` при успехе, отрицательный `SC_ERR_*` при ошибке. | -| `sc_stream_sign_cleanup(state)` | Ручная очистка (если не вызван final/verify). | +| `sc_stream_sign_final(state, sig_out, &sig_len)` | SHA-512 → one-shot Ed25519 подпись хэша. На входе capacity sig_out ≥64, при успехе на выходе 64. После начала финализации выполняет cleanup при любом результате. | +| `sc_stream_sign_verify(state, sig, sig_len)` | SHA-512 → one-shot Ed25519 проверка. После начала финализации выполняет cleanup при любом результате. Возвращает `SC_OK` при успехе, отрицательный `SC_ERR_*` при ошибке. | +| `sc_stream_sign_cleanup(state)` | Освобождает ресурсы, обнуляет все поля; безопасен для NULL, пустого state и повторных вызовов. | **Потоковая схема:** Ed25519 внутри делает SHA-512 от данных, поэтому для стриминга нужно было бы грузить все данные в память. Вместо этого: `SHA-512(данные)` делается инкрементально через `EVP_DigestUpdate`, затем 64-байтный хэш подписывается one-shot `EVP_DigestSign`. Результат: `Ed25519(SHA-512(данные))` — консистентно с обеих сторон. Память O(1).