diff --git a/doc/tasks.md b/doc/tasks.md index b40afeb6..eddf560e 100644 --- a/doc/tasks.md +++ b/doc/tasks.md @@ -67,16 +67,18 @@ Логи: /tmp/etcp-attempts-matrix.log, /tmp/etcp-attempts-asan-test_*.log, /tmp/etcp-oom-reinit-asan-test_*.log, /tmp/etcp-abort-encrypted-recovery.log. -[ ] **Crypto: повторное освобождение ключа после отказа инициализации потоковой подписи** — подтверждено 24.10.2026: - sc_stream_sign_init и sc_stream_sign_verify_init при отказе EVP_MD_CTX_new освобождают state->pkey, - но оставляют указатель в state; последующий sc_stream_sign_cleanup освобождает его повторно. - Та же незавершённая очистка есть при отказе EVP_DigestInit_ex. Активное состояние перед повторным init - также требует явного контракта: текущий memset теряет ссылки на его ресурсы. - Привести состояния после отказов к безопасному cleanup, определить контракт повторного init в .h, - добавить инъекции отказов выделения/инициализации и проверки cleanup/final/verify с ASAN/UBSan. - Временное воспроизведение на штатной libutun.a: два отказа EVP_MD_CTX_new, два повторных free; - диагностический wrapper фиксирует и подавляет второй free. Лог: /tmp/sc-sign-init-oom-repro.log, - исходник: /tmp/sc-sign-init-oom-repro.c. Исполняемый код проекта в этой проверке не менялся. +[+] **Crypto: повторное освобождение ключа после отказа инициализации потоковой подписи** — исправлено 24.10.2026: + Оба init создают ключ и SHA-512-контекст локально, заполняя state только после полного успеха. + Перед первым использованием state={0}; отказ оставляет его пустым. Повторный init активного state + отклоняется без потери ресурсов и накопленного хеша. Cleanup обнуляет все поля и допускает повторы. + Final проверяет capacity до финализации; недостаточный буфер позволяет повторить вызов с сохранённым хешем. + Контракты и обработка ошибок отражены в .h и примере документации; формат подписи прежний. + Регрессия до исправления падает на непустом state после отказа EVP_MD_CTX_new. + Проверены 6 отказов init, 8 отказов final/verify, 4 сочетания повторного init, capacity0/63/64, + повторная очистка/использование и совпадение с независимой one-shot подписью SHA512 сообщения. + Тесты не перехватывают free. Восемь профильных тестов ASAN/UBSan с detect_leaks=1 PASS; + check.sh184/184, proxy9/9, burst1/1, load8/8 PASS. Пример документации компилируется с -Werror. + Логи: /tmp/sc-sign-regression-before.log, /tmp/sc-sign-fix-full-check.log, /tmp/sc-sign-fix-asan-*.log. [+] **Media: очистка кеша удаляла временный файл активной загрузки** — исправлено 24.10.2026: file_io хранит общий защищённый mutex реестр владельцев temp по фактическим нормализованным путям. diff --git a/src/transport_layer/secure_channel.h b/src/transport_layer/secure_channel.h index 49732fe0..6388c5c1 100644 --- a/src/transport_layer/secure_channel.h +++ b/src/transport_layer/secure_channel.h @@ -115,8 +115,8 @@ struct sc_stream_sign_state { uint8_t is_sign; // В активном состоянии 1=sign, 0=verify; после cleanup обнуляется }; -/* init — только для пустого state. Ошибка оставляет его пустым; повторный init активного state - * возвращает SC_ERR_INVALID_ARG без изменения ресурсов и хеша. Оба init: SC_OK / отрицательный SC_ERR_*. +/* init — только для пустого state; ошибка не меняет state. При корректных входных аргументах + * повторный init активного state возвращает SC_ERR_INVALID_ARG. Оба init: SC_OK / отрицательный SC_ERR_*. * Подпись использует ключ из initialized ctx->pk; peer/ECDH не требуются, ctx нужен только на время init. */ sc_status_t sc_stream_sign_init(sc_context_t *ctx, struct sc_stream_sign_state *state); /* ed25519_pubkey — обязательный бинарный ключ[32], копируется OpenSSL во внутренний ресурс state. */ diff --git a/src/transport_layer/secure_channel_doc.md b/src/transport_layer/secure_channel_doc.md index a08591a0..6163d812 100644 --- a/src/transport_layer/secure_channel_doc.md +++ b/src/transport_layer/secure_channel_doc.md @@ -145,8 +145,9 @@ sc_stream_sign_cleanup(&state); // Безопасно после ошибок, ``` Перед первым использованием состояние обнуляют. `init` требует пустого состояния и создаёт ресурсы -до его заполнения: отказ оставляет все поля пустыми. Повторный `init` активного состояния возвращает -`SC_ERR_INVALID_ARG`, сохраняя ресурсы и накопленный хеш; для новой операции сначала нужен `cleanup`. +до его заполнения: отказ оставляет все поля пустыми. При корректных входных аргументах повторный `init` +активного состояния возвращает `SC_ERR_INVALID_ARG`, сохраняя ресурсы и накопленный хеш; +для новой операции сначала нужен `cleanup`. Подписи достаточно локального ключа из инициализированного `ctx`; ECDH с пиром не требуется. `final` проверяет capacity не менее 64 до финализации. NULL аргументы, недостаточная capacity и неверный