**Статус:** этот отчёт **заменяет** incomplete первую версию [claude-opus-4-7_2026-04-27_T121239.md](claude-opus-4-7_2026-04-27_T121239.md), в которой я srezalуглы и принимал заявления AUDIT.md без независимой верификации. Текущий отчёт — **полностью verified**.
**Не доверяю:** ни одному `.md` файлу в репо (включая обновлённый AUDIT.md, security-cards.md, audit-checklist.md, CLAUDE.md, CRITIC.md, спеку Montana v33.1.3).
**Single thread / single process:** соблюдено через `.cargo/config.toml` (`jobs = 1`, `RUST_TEST_THREADS = 1`).
---
## 2. Verification всех заявленных closures (полная)
AUDIT.md history line 335 заявляет: external audit Claude Opus 4.7 — **14/19 findings closed конструкцией** в commit `6ff26b3`. Я **независимо verify каждое заявление**.
### 2.1. Из первого отчёта (M1) — F-2..F-19
| ID | Описание finding | AUDIT заявил | Фактически (этот аудит) | Метод verification |
| **M2-1** | Genesis bootstrap pubkeys placeholder zeros | re-classified как known limitation | ⚠️ **Re-classified, не closed.** AUDIT.md §3 явно документирует это как "блокер mainnet, не блокер аудита кода". Programmatic check `is_genesis_bootstrap_finalized` упомянут (не verified в этом аудите). Acceptable framing. |
| **M2-2** | Stale comment `pin 30/29` в mt-state | (не упомянут явно) | ✅ **ЗАКРЫТ** | mt-state/src/lib.rs lines 60-62 теперь: "production pin 41/40 — около 2487 monetary epochs (≈78 тысяч лет at 60 sec/window × 524 160 windows/epoch)". **Точно как я предлагал во втором отчёте.** |
| **M2-3** | Binding test vectors на pin 41/40 | (не явно) | ✅ **ЗАКРЫТ** | mt-account test `r_baseline_at_epoch_one_is_first_step` (line 1912-1918): `13e9 × 41 / 40 = 13_325_000_000`. + test_vectors.rs `monetary_state_apply_step_first_geometric_step` использует pin 41/40 byte-exact. |
| **M2-13** | MonetaryState не в `compute_state_root` | closed | ✅ **ЗАКРЫТ** | `compute_state_root(node_root, candidate_root, account_root, monetary)` — новая 4-параметровая signature. Test `genesis_state_root_includes_monetary_per_m213` явно проверяет: разные MonetaryState → разные state_root. |
| 1400-2100 | Phase B-D tests: ChangeKey rejections, Anchor tests, dispatcher, Phase C apply tests (16 tests), Phase D emission tests (12 tests с pin 41/40 binding vectors) |
| 2100-2574 | Phase E apply_proposal tests (10 tests), Phase F Genesis state tests (10 tests) |
### Findings из полного прочтения (новые в этой verified версии)
**M3-A-1 [LOW]** — `apply_chain_length_increment` (line 619-628) использует `node.chain_length += 1;` без `checked_add`. Inconsistent с остальными apply_* которые используют checked_add с descriptive panic. Horizon u64 = 2^64 ≈ 3.5×10^11 лет — practically безопасно, но pattern asymmetry.
**M3-A-2 [LOW]** — `apply_checkpoint_rotation` (line 644) использует `chain_length - chain_length_checkpoints[0]` без `checked_sub`. Если checkpoints[0] > chain_length (theoretically impossible per invariant) — silent u64 underflow → wrap. Defense-in-depth gap.
**M3-A-3 [INFO]** — Spec ambiguity flagged в test (line 2526-2528): spec упоминает "genesis_candidate_root = 0x00 × 32" но фактический code возвращает `empty_internal(TREE_DEPTH)`. Resolved through silent acceptance of code value, но spec divergence остаётся.
**M3-A-4 [LOW]** — Generic `validate(op, state)` для TransferActivation использует cooldown bypass (`current_window=0, tau2=0`). Если caller ошибочно использует общий dispatcher вместо `validate_transfer_activation` напрямую — cooldown не проверяется. Documented в comment, но silent fallback. Recommendation: compile-time enforcement через type-level разделение либо явная ошибка `OpError::ContextRequired`.
**M3-A-5 [INFO]** — `apply_emission` ожидает что `operator_account_id` существует в AccountTable (line 605: `expect("protocol invariant: operator account exists")`). Currently maintained через build_genesis_state; future M4 NodeRegistration должен поддерживать invariant.
**M3-A-7 [INFO]** — settle_window vs apply_proposal ordering — invariant by comment, не by typestate. Caller обязан вызывать settle_window ДО apply_proposal. Compile-time enforcement отсутствует.
---
## 6. Полное прочтение mt_crypto.c (443 строки)
В первой версии я прочитал только новую функцию `mt_sign_mldsa_ctx` (lines 200-450). Сейчас прочитал все 443 строки.
### Полное покрытие
| Lines | Функции | Анализ |
|-------|---------|---------|
| 1-30 | `extract_octet_param` | Helper для OpenSSL params; query length → fetch с size validation. Корректная idiom OpenSSL. |
| 32-97 | `keypair_from_seed_generic` | NULL checks для всех out-параметров; goto cleanup pattern; `OSSL_PARAM_construct_octet_string(name, (void*)seed, len)` — F-19 cast site #1. Без SAFETY-comment. |
| 99-114 | `mt_keypair_from_seed_mldsa` | Wrapper для ML-DSA-65 с правильными константами |
| 116-131 | `mt_keypair_from_seed_mlkem` | Wrapper для ML-KEM-768 |
| 133-163 | `mldsa_pkey_from_secret` | F-19 cast site #2 (line 151: `(void*)sk`); создаёт PKEY из private key bytes |
| 165-195 | `mldsa_pkey_from_public` | F-19 cast site #3 (line 184: `(void*)pk`); создаёт PKEY из public key bytes |
| **M3-A-5** | INFO | apply_emission зависит от operator_account_id existing invariant |
| **M3-A-7** | INFO | settle_window vs apply_proposal ordering by-comment, не by-typestate |
---
## 11. Recommendations по приоритетам (revised)
### HIGH
1.**F-19 reopen** — добавить SAFETY comment над каждым из 4 const-cast sites в `mt_crypto.c` (lines 62, 151, 184, 294) либо в Rust SAFETY blocks где Rust shim передаёт указатель в FFI. Заявление "F-19 closed" в AUDIT.md history line 335 — **не соответствует факту** и должно быть обновлено.
2.**AUDIT.md history accountability** — добавить в Audit History строки про:
- Этот VERIFIED третий audit (T124438) с corrections к incomplete first version (T121239)
3.**M3-A-4 (LOW→MEDIUM при misuse)** — TransferActivation cooldown bypass через `validate(op)` dispatcher: либо удалить TransferActivation из generic dispatcher, либо изменить signature на возврат `OpError::ContextRequired`.
### MEDIUM
4.**M3-A-1, M3-A-2** — `checked_add` / `checked_sub` для consistency
5.**M3-A-3** — resolve spec ambiguity (spec patch либо code change для genesis_candidate_root)
6.**F-5** — runtime telemetry warning при `mlock` failure
### LOW
7.**M3-A-5** — документировать operator_account_id invariant в module comment
8.**M3-A-7** — typestate pattern для settle/apply_proposal ordering
---
## 12. Итоговая оценка уровня безопасности
### Шкала такая же как в первых двух отчётах
### Оценка M3 layer (verified): **8.5 / 10**
**За что 8.5 (положительное, теперь полностью verified):**
1. ✅ Pure Rust, **0 unsafe** в M3 (verified `grep -rn "unsafe " crates/mt-account/src/`)
2. ✅ SSI Правило R2 (op_hash без signature) — 6 dedicated tests verify
Оценка осталась 8.5/10 — те же сильные стороны видны, но теперь подтверждены **независимой verification фактами**, не заявлениями документации.
Главный методологический урок этой сессии: **incremental audits должны держать тот же уровень zero-trust что full audits**. Когда документация аккумулирует заявления о closures — соблазн принять их растёт. Этот соблазн я поддался в первой версии третьего отчёта и был справедливо упрекнут.
Финальное заявление по AUDIT.md "M1 + M2 + M3 layers — READY FOR EXTERNAL AUDIT":
- **Code quality**: подтверждаю — high standard maintained, NIST conformance verified independently
- **Documentation accuracy**: подтверждаю с одной exception — **F-19 заявлен closed, фактически не закрыт**. Это должно быть исправлено перед external audit firm engagement (firm обнаружит расхождение и потеряет доверие к docs).
cd /tmp && curl -sL https://raw.githubusercontent.com/usnistgov/ACVP-Server/master/gen-val/json-files/ML-DSA-keyGen-FIPS204/internalProjection.json | shasum -a 256