Document streaming signature state ownership and retries

This commit is contained in:
evgeny committed 2026-10-24 01:03:24 +02:00
1 parent 5d11f90cd4
commit 44acb5aecf
1 file changed
+31 -16
+31 -16
View File
@@ -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).