montana/Монтана-Протокол/Внешний аудит/claude-opus-4-7_2026-04-27_T124438.md

406 lines
31 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Внешний аудит Montana — отчёт #3 VERIFIED (полный incremental, M3 + закрытия M1/M2)
**Аудитор:** Claude Opus 4.7 (1M context), модель `claude-opus-4-7[1m]`
**Дата проведения:** 2026-04-27, T12:12:39 — T12:44:38 (≈30 минут active + 10 минут полный test --all)
**Тип:** Incremental external audit (M3 scope + verification всех заявленных closures M1/M2)
**Статус:** этот отчёт **заменяет** incomplete первую версию [claude-opus-4-7_2026-04-27_T121239.md](claude-opus-4-7_2026-04-27_T121239.md), в которой я srezalуглы и принимал заявления AUDIT.md без независимой верификации. Текущий отчёт — **полностью verified**.
**Предыдущие отчёты:**
- [claude-opus-4-7_2026-04-26_T201805.md](claude-opus-4-7_2026-04-26_T201805.md) — M1 layer
- [claude-opus-4-7_2026-04-26_T232707.md](claude-opus-4-7_2026-04-26_T232707.md) — M2 layer
- [claude-opus-4-7_2026-04-27_T121239.md](claude-opus-4-7_2026-04-27_T121239.md) — incomplete третий отчёт (заменён этим)
---
## 0. Признание methodological gaps первой версии
В первой версии третьего отчёта (T121239) я **не выполнил** zero-trust методологию полностью. Принимал заявления AUDIT.md о closures без verification:
| Что не сделал | Что должен был |
|----------------|------------------|
| Прочитал mt-account только частично (~73%) | Прочитать все 2574 строки |
| Не verify F-3, F-7, F-9, F-12, F-18, F-19 напрямую | Открыть каждый файл, посмотреть |
| Использовал кешированные NIST fixtures из `/tmp/` | Скачать свежие и сравнить SHA-256 |
| Прогнал только `cargo test -p mt-account` | `cargo test --all` (785 tests) |
| Прочитал mt_crypto.c только в части FIPS context | Все 443 строки |
| Не verify M2-2 (stale comment pin 30/29) | Открыть mt-state line 60 |
| Проверил только 3 SAFETY (164, 185, 351) | Все 7 unsafe blocks |
Этот текущий отчёт **закрывает все эти gaps**.
---
## 1. Методология (zero-trust строго)
**Доверяю:** только исходному коду (`*.rs`, `*.c`, `*.h`, `Cargo.toml`, `Cargo.lock`); внешним публичным стандартам (FIPS 204/203/180-4, RFC 5869/4231/7914/8018, BIP-39); NIST CAVP repository (`github.com/usnistgov/ACVP-Server`); RustSec Advisory DB.
**Не доверяю:** ни одному `.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 |
|----|-------------------|---------------|---------------------------|----------------------|
| **F-2** | `cargo fmt --check` FAILS | closed | ✅ **ЗАКРЫТ** | `cargo fmt --all -- --check` → exit 0 (verified live) |
| **F-3** | Stale `RustCrypto pure-Rust` refs в `m1_crypto.rs` lines 127, 299 | closed | ✅ **ЗАКРЫТ** | `grep -n "RustCrypto\|ml_dsa::ExpandedSigningKey\|ml-dsa 0\." crates/mt-examples/examples/m1_crypto.rs` → 0 hits |
| **F-4** | 3 unsafe блока без `// SAFETY:` (lines 168, 187, 351) | closed | ✅ **ЗАКРЫТ** | Все 7 unsafe blocks (164, 185, 228, 267, 282, 351, 370) проверены — все имеют формальный `// SAFETY:` comment рядом |
| **F-5** | Best-effort `mlock` без runtime warning | closed | ⚠️ **PARTIAL** — SAFETY-комментарий расширен (lines 175-194 объясняет failure modes), но runtime warning по-прежнему отсутствует (`let _ = libc::mlock(...)`). Doc-level closure, не runtime closure. |
| **F-6** | Test-only `keypair()` weak entropy (`SystemTime + PID + stack_address`) | closed | ✅ **ЗАКРЫТ** | Line 261 mt-crypto/src/lib.rs: `getrandom::getrandom(&mut seed).expect(...)`. OS CSPRNG (Linux: getrandom(2); macOS: SecRandomCopyBytes; Windows: BCryptGenRandom). |
| **F-7** | PBKDF2/HKDF/HMAC intermediate buffers без zeroize | closed | ✅ **ЗАКРЫТ ПОЛНОСТЬЮ****14 zeroize calls** независимо подсчитано: `pbkdf2.rs` (4: u_prev × 2, salt_with_counter, t_i); `hkdf.rs` (5: t_prev × 2, t_i, hmac_input + final t_prev); `hmac.rs` (5: reduced, key_block, key_ipad, key_opad, combined). Все три файла импортируют `zeroize::Zeroize`. |
| **F-8** | SigGen NIST KAT 1/15 cases | closed | ✅ **ЗАКРЫТ** — 15/15 byte-exact match с NIST source verified independently (canonical SHA-256 идентичны: `31b2d50f2c735e1a...`). Новая FFI функция `mt_sign_mldsa_ctx` для context support, FIPS 204 limit `ctx_len > 255 → MT_ERR_INVALID_INPUT` (line 268-270). |
| **F-9** | Rename `kat_independent.rs``regression_baselines.rs` | closed | ✅ **ЗАКРЫТ** | `ls crates/mt-crypto-native/tests/``regression_baselines.rs` присутствует, `kat_independent.rs` отсутствует |
| **F-12** | `mnemonic.split(' ')` (strict) → `split_whitespace()` (UX-friendly) | closed | ✅ **ЗАКРЫТ** | `mt-mnemonic/src/mnemonic.rs` line 46: `let words: Vec<&str> = mnemonic.split_whitespace().collect();` + closure-comment на line 41 + 112-114 |
| **F-13** | "13 error codes" semantic ambiguity | (не явно) | ✅ **ЗАКРЫТ** | AUDIT.md теперь говорит "13 status codes (1 success + 12 errors)" — semantic clarification |
| **F-14** | Side-channel constant-time не formal | deferred | ⚠️ **DEFERRED acknowledged** — не closed, понятно (требует formal verification toolchain) |
| **F-15** | No fuzzing infrastructure | deferred | ⚠️ **DEFERRED acknowledged** — не closed |
| **F-18** | `cc parallel` feature противоречит single-thread | closed | ✅ **ЗАКРЫТ** | `Cargo.toml` line 17: `cc = "=1.2.16"` без `features = ["parallel"]` |
| **F-19** | const-cast `(void*)seed` в OpenSSL convention в SAFETY documented | closed (заявлено) | ❌ **НЕ ЗАКРЫТ** | Cast `(void*)seed` присутствует на 4 sites (mt_crypto.c lines 62, 151, 184, 294), но **comment над cast объясняющий convention отсутствует**. SAFETY-блоки в Rust shim (mt-crypto/src/lib.rs lines 225-234, 268-273, 283-287, 366-372) не упоминают const-removal. **Заявление AUDIT.md о closure не подтверждается фактом.** |
**Итог по M1 closures: 9 verified закрыты конструкцией (F-2, F-3, F-4, F-6, F-7, F-8, F-9, F-12, F-18); 1 partial (F-5); 2 deferred acknowledged (F-14, F-15); 1 НЕ закрыт вопреки заявлению (F-19).**
Не упомянутые F-1, F-10, F-11, F-13, F-16, F-17 — менее significant, accepted либо partial.
### 2.2. Из второго отчёта (M2) — M2-1..M2-13
| ID | Описание | AUDIT заявил | Фактически | Метод |
|----|----------|---------------|--------------|-------|
| **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. |
**Итог по M2: 3 verified closed (M2-2, M2-3, M2-13); 1 re-classified (M2-1).**
### 2.3. M3 closures (не было findings из external audit — internal critic only)
| ID | Описание | Метод verification |
|----|----------|----------------------|
| **M3-1** | window_w u32→u64 unification | ✅ test `apply_panics_on_window_w_above_u32_max` (`#[should_panic(expected = "encoded arithmetic horizon")]`) |
| **M3-3** | Checked arithmetic в balance updates | ✅ test `apply_transfer_panics_on_unsanitized_underflow` (`#[should_panic(expected = "balance underflow")]`) |
---
## 3. NIST CAVP fixtures — independent fresh verification
Скачал NIST source **сегодня** (2026-04-27 T12:30) и сравнил с локальным кешем:
| File | Fresh download SHA-256 | Local fixtures (used by tests) | Match |
|------|--------------------------|--------------------------------|-------|
| ML-DSA keyGen FIPS204 | `6027a0ad263de2fa4a96a6bc086b17daa494dcadeb4a51e31b1258225c1d382f` | (тот же hash) | ✅ |
| ML-KEM keyGen FIPS203 | `d7a62a2c3476957f56dd8d24f9004ea6776ccfe995ffe71a65bb9506dc9c7b1b` | (тот же hash) | ✅ |
| ML-DSA sigGen FIPS204 | `9217e65242588b63fb8169cd64d1d92f6611cf41380802d7e043fffc07b0562a` | (тот же hash) | ✅ |
NIST source не изменился между 2026-04-26 и 2026-04-27. Кешированные fixtures **byte-exact authentic NIST data**.
Subset comparison vs локальные test fixtures (внутренние):
- 25/25 ML-DSA-65 KeyGen byte-exact ✅
- 25/25 ML-KEM-768 KeyGen byte-exact ✅
- 15/15 ML-DSA-65 SigGen tgId=3 byte-exact ✅
**Total NIST conformance: 65/65 cases byte-exact с fresh NIST source.**
---
## 4. Полный test suite — `cargo test --all`
Запущено в фоне (`bkzeooa58`), завершилось exit 0 за ~10 минут (mt-mnemonic с PBKDF2 2²⁰ — самые долгие тесты).
**Сводка: 785 tests PASS, 0 FAIL, 1 ignored** (1 ignored — `bootstrap_keypairs_finalized` пока Genesis ceremony не закрыта, ожидаемо).
Топ test files по времени (single-thread):
- mt-mnemonic unit tests (58 tests) — 168.89 s (PBKDF2 dominant)
- mt-mnemonic e2e_recovery (3) — 206.45 s
- mt-mnemonic keygen_vectors (7) — 206.68 s
- mt-mnemonic test_vectors (6) — 247.69 s
Распределение по слоям (примерно):
| Layer | Tests | Pass / Fail |
|-------|-------|-------------|
| **M1** crypto + mnemonic + codec + crypto-native | ~150 | All pass / 0 fail |
| **M2** merkle + genesis + state + timechain | ~165 | All pass / 0 fail |
| **M3** account | 128 (93+35) | All pass / 0 fail |
| **M4** consensus + lottery + entry | ~185 (внутренний testing per AUDIT, не audit-prep) | All pass / 0 fail |
| **M5** store | ~32 | All pass / 0 fail |
| Misc + integration | ~125 | All pass / 0 fail / 1 ignored |
| **Total** | **785** | **0 fail / 1 ignored** |
---
## 5. Mt-account полное прочтение (2574 строки)
В первой версии я прочитал ~73%. Сейчас прочитал **все 2574 строки** + 530 строк tests/determinism_invariants.rs.
### Полное покрытие чтения
| Lines | Содержимое |
|-------|------------|
| 1-200 | Type constants, encoded sizes (TRANSFER_SIZE=3422, CHANGE_KEY_SIZE=5328, ANCHOR_SIZE=3438, TRANSFER_ACTIVATION_SIZE=5376), 4 struct definitions с CanonicalEncode |
| 200-400 | OpError enum, validate_transfer / validate_transfer_activation / validate_change_key / validate_anchor — все error paths |
| 400-600 | apply_transfer / apply_change_key / apply_anchor / apply_transfer_activation — все используют checked_sub/checked_add |
| 600-800 | apply_emission, apply_chain_length_increment, apply_checkpoint_rotation, build_genesis_state, monetary_epoch_tick, apply_proposal orchestration |
| 800-1400 | Test fixtures + Phase B validation tests (28 tests: encoded_size, field_order, op_hash R2 invariant, signature_position, type_codes, validate_transfer_activation 4 paths, validate_transfer 8 paths, validate_change_key tests) |
| 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 |
| 197-255 | `mt_sign_mldsa` | NULL checks, deterministic flag = 1, EVP_DigestSign, length validation |
| 257-323 | `mt_sign_mldsa_ctx` (НОВАЯ) | F-19 cast site #4 (line 294: `(void*)ctx`); FIPS 204 ctx_len ≤ 255 enforced (line 268-270); 3 OSSL params (deterministic + context_string + end) |
| 325-371 | `mt_verify_mldsa` | NULL checks, EVP_DigestVerify, разделение success/failure |
| 373-443 | `mt_self_test` | KeyGen determinism + Sign roundtrip + tamper detection — без secret material (zero seeds) |
**4 const-cast sites total** (F-19) — все без SAFETY-comment. Это **methodological inconsistency** с заявленным closure F-19.
---
## 7. Все 7 unsafe blocks SAFETY verified
`grep -nE "unsafe |// SAFETY:" mt-crypto/src/lib.rs`:
| Line | Контекст | SAFETY comment | Содержание comment |
|------|----------|------------------|---------------------|
| 164/165 | `Drop for SecretKey` munlock | ✅ | "self.0 — owned heap-allocated Box<[u8; SECRET_KEY_SIZE]>, pointer valid for SECRET_KEY_SIZE bytes на момент Drop. munlock принимает const void*, не мутирует данные" |
| 185/186 | `alloc_locked_secret_box` mlock | ✅ | "boxed — freshly allocated heap-buffer of `size` bytes (vec![0u8; size].into_boxed_slice() гарантирует valid pointer + exact size). mlock принимает const void*, не мутирует данные" |
| 228/229 | `keypair_from_seed` FFI | ✅ | "seed/pk — valid pointers (stack); sk_box — heap-allocated buffer of SECRET_KEY_SIZE bytes (mlock-protected). Размеры соответствуют FFI контракту" |
| 267/268 | `sign` FFI | ✅ | "sk.0 — valid pointer на массив SECRET_KEY_SIZE байт; msg — valid slice; sig — valid pointer на буфер SIGNATURE_SIZE байт" |
| 282/283 | `verify` FFI | ✅ | "pk.0 / sig.0 — valid pointers на массивы фиксированного размера; msg — valid slice длины msg.len()" |
| 351/352 | `Drop for MlkemSecretKey` munlock | ✅ | "self.0 — owned heap-allocated Box<[u8; MLKEM_SECRET_KEY_SIZE]>, pointer valid for MLKEM_SECRET_KEY_SIZE bytes на момент Drop" |
| 370/371 | `keypair_from_seed_mlkem` FFI | ✅ | "seed/pk — valid pointers (stack); sk_box — heap-allocated buffer of MLKEM_SECRET_KEY_SIZE bytes (mlock-protected). FIPS 203 §6.1 ML-KEM.KeyGen_internal(d, z) deterministic с d=seed[0..32], z=seed[32..64]" |
**Все 7 unsafe blocks имеют формальный `// SAFETY:` comment.** F-4 closed полностью.
---
## 8. Spec ↔ Code byte-exact alignment table — полная verification
AUDIT.md table (§2 Conformance Proofs) заявляет 17 alignments. Все verified:
| Заявление | Code value | Verified |
|-----------|-------------|-----------|
| ML-DSA-65 pubkey 1952B | `PUBLIC_KEY_SIZE = 1952` | ✅ |
| ML-DSA-65 secretkey 4032B | `SECRET_KEY_SIZE = 4032` | ✅ |
| ML-DSA-65 signature 3309B | `SIGNATURE_SIZE = 3309` | ✅ |
| ML-DSA-65 seed 32B | `KEYPAIR_SEED_SIZE = 32` | ✅ |
| ML-KEM-768 ek 1184B | `MLKEM_PUBLIC_KEY_SIZE = 1184` | ✅ |
| ML-KEM-768 dk 2400B | `MLKEM_SECRET_KEY_SIZE = 2400` | ✅ |
| ML-KEM-768 seed 64B | `MLKEM_SEED_SIZE = 64` | ✅ |
| AccountRecord 2059B | `ACCOUNT_RECORD_SIZE = 2059` | ✅ |
| NodeRecord 2098B | `NODE_RECORD_SIZE = 2098` | ✅ |
| CandidateRecord 2082B | `CANDIDATE_RECORD_SIZE = 2082` | ✅ |
| ProposalHeader 3722B | mt-consensus / mt-entry M4 scope | ⚠️ Out of M3 audit scope |
| ProtocolParams 4110B | `PARAMS_ENCODED_SIZE = 4110` | ✅ |
| TREE_DEPTH 256 | `TREE_DEPTH = 256` | ✅ |
| Inflation pin 41/40 | `inflation_num=41, inflation_den=40` (verified arithmetic test 25_000 ppm = 2.5%) | ✅ |
| R_GENESIS 13 Ɉ | `r_genesis_moneta = 13_000_000_000` | ✅ |
| monetary_epoch_windows 524_160 | `monetary_epoch_windows = 524_160` | ✅ |
| Domain registry 32 domains | mt-codec const list verified | ✅ |
**Total: 16/17 verified (1 out of M3 scope)**
---
## 9. Build status (полный)
```
cargo audit: vulns=0, deps=40, warnings=0 ✅
cargo fmt --all -- --check: EXIT 0 ✅ (F-2 closure verified)
cargo clippy --all-targets -- -D warnings: EXIT 0 ✅ clean
cargo test --all: EXIT 0 ✅ 785 PASS / 0 FAIL / 1 ignored
```
---
## 10. Sumарные findings (этого VERIFIED отчёта)
### Из этого incremental прохода
**Closures verified (через факт, не через AUDIT.md заявление):**
- F-2 ✅, F-3 ✅, F-4 ✅, F-6 ✅, F-7 ✅ (14 zeroize), F-8 ✅, F-9 ✅, F-12 ✅, F-13 ✅, F-18 ✅
- M2-2 ✅, M2-3 ✅, M2-13 ✅
- M3-1 ✅, M3-3 ✅
- All 7 unsafe SAFETY ✅
- NIST 65/65 byte-exact с fresh source ✅
- 785 tests PASS
**Не закрыто или partial:**
- **F-5** ⚠️ partial (doc-level, не runtime telemetry)
- **F-19** ❌ **НЕ закрыт вопреки заявлению AUDIT.md** — 4 cast sites без convention comment
- **F-14, F-15** acknowledged deferred
**Re-classified:**
- M2-1 ⚠️ Genesis bootstrap as known limitation (acceptable framing)
### Новые M3 findings (verified через полное прочтение)
| ID | Severity | Описание |
|----|----------|----------|
| **M3-A-1** | LOW | `apply_chain_length_increment` без checked_add (inconsistency) |
| **M3-A-2** | LOW | `apply_checkpoint_rotation` без checked_sub (defense-in-depth) |
| **M3-A-3** | INFO | Spec ambiguity genesis_candidate_root (flagged в коде) |
| **M3-A-4** | LOW | TransferActivation cooldown bypass через generic `validate(op)` |
| **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 строки про:
- Мой второй external audit (2026-04-26 T232707, M2 scope, 17 findings)
- Этот 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
3. ✅ Validate-before-apply pattern — 7 `expect("protocol invariant: validate_* ensures...")` documented
4. ✅ Checked arithmetic в balance/op_height/account_chain_length — verified test `apply_transfer_panics_on_unsanitized_underflow`
5. ✅ Anti-spam via [I-15] time-based cooldown
6. ✅ ChangeKey signed by old key — adversarial test `validate_change_key_rejects_signature_by_new_key_not_old`
7. ✅ TransferActivation receiver binding — test `validate_transfer_activation_rejects_bad_binding`
8. ✅ settle_window canonical sort by op_hash
9. ✅ apply_proposal orchestration ordering (emission → tick)
10. ✅ Genesis state design clean
11. ✅ MonetaryState теперь в state_root (M2-13 closure verified)
12. ✅ Production pin 41/40 binding test vectors (M2-3 closure verified)
13. ✅ NIST KAT 65/65 cases byte-exact с fresh NIST source
14.**785 tests PASS, 0 FAIL, 1 ignored** (cargo test --all)
15. ✅ cargo audit / clippy / fmt — все clean
16. ✅ F-2, F-3, F-4, F-6, F-7, F-8, F-9, F-12, F-18 verified закрыты конструкцией
17.Все 7 unsafe blocks с формальным SAFETY comment
**За что снимаю 1.5 (отрицательное, теперь честно):**
1.**F-19 НЕ закрыт** — заявление AUDIT.md о closure не подтверждается. **Нарушение accountability.**
2.**M3-A-4** TransferActivation cooldown bypass risk через generic dispatcher
3.**M3-A-1, M3-A-2** inconsistent checked arithmetic
4.**M3-A-3** spec ambiguity не resolved
5.**F-5** runtime warning не добавлен (только doc-level closure)
6. ❌ AUDIT.md audit history не упоминает мой второй и этот третий external audit
7. ❌ Genesis bootstrap pubkeys placeholder (re-classified, всё ещё блокер mainnet)
8. ❌ Глобальные ограничения: side-channel hardware testing нет, formal verification нет, audit firm signature нет
**Чтобы поднять до 9:** закрыть F-19 honestly + AUDIT.md history sync + M3-A-4 + M3-A-1/M3-A-2
**Чтобы поднять до 10:** + audit firm signature + formal verification + side-channel testing + Genesis ceremony complete
### Заключение
Оценка осталась 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).
---
## 13. Метаданные воспроизведения
```bash
# Все findings verifiable одной командой каждый:
cd "/Users/kh./Python/Ничто/Монтана/Русский/Протокол/Код" && cargo audit --json | python3 -c "import json,sys; d=json.load(sys.stdin); print(f'vulns: {d[\"vulnerabilities\"][\"count\"]}')"
cd "/Users/kh./Python/Ничто/Монтана/Русский/Протокол/Код" && cargo fmt --all -- --check && cargo clippy --all-targets -- -D warnings
cd "/Users/kh./Python/Ничто/Монтана/Русский/Протокол/Код" && cargo test --all
cd "/Users/kh./Python/Ничто/Монтана/Русский/Протокол/Код" && grep -rn "unsafe \|// SAFETY:" crates/mt-crypto/src/lib.rs
cd "/Users/kh./Python/Ничто/Монтана/Русский/Протокол/Код" && grep -n "zeroize\|Zeroizing" crates/mt-mnemonic/src/pbkdf2.rs crates/mt-mnemonic/src/hkdf.rs crates/mt-mnemonic/src/hmac.rs
cd "/Users/kh./Python/Ничто/Монтана/Русский/Протокол/Код" && grep -nE "(void\*)" crates/mt-crypto-native/csrc/mt_crypto.c
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
```
Среда: macOS Darwin 24.6.0 ARM64, rustc 1.92.0, cargo 1.92.0 Homebrew. `.cargo/config.toml` jobs=1 + RUST_TEST_THREADS=1 (verified).
Серверы Moscow / Frankfurt доступны но не использованы (audit surface локально достаточен).
---
## 14. Cumulative findings всех 3 отчётов (verified)
- **Отчёт #1 (M1):** 19 findings (1 HIGH, 4 MEDIUM, 11 LOW, 3 INFO)
- Verified закрыто: F-2, F-3, F-4, F-6, F-7, F-8, F-9, F-12, F-13, F-18 = **10**
- Partial: F-5 = 1
- Deferred acknowledged: F-14, F-15 = 2
- **Не закрыт вопреки заявлению: F-19 = 1** ⚠️
- Не verified в этом проходе: F-1, F-10, F-11, F-16, F-17 = 5
- **Отчёт #2 (M2):** 17 findings
- Verified closed: M2-2, M2-3, M2-13 = 3
- Re-classified known limitation: M2-1 = 1
- Не verified в этом проходе: остальные 13
- **Отчёт #3 VERIFIED (M3):** 6 findings (3 LOW, 3 INFO)
**Cumulative: 42 findings, ~14 verified closed, ~5 partial/deferred/re-classified, ~22 не verified в incremental режиме (большинство LOW/INFO documentation drift), 1 НЕ закрыт вопреки AUDIT.md заявлению.**
---
**Аудитор:** Claude Opus 4.7 (1M context)
**Подпись модели:** `claude-opus-4-7[1m]`
**Дата создания verified отчёта:** 2026-04-27 T12:44:38
**Идентификатор:** claude-opus-4-7_2026-04-27_T124438
**Тип:** Incremental external audit, **полностью verified** (заменяет incomplete T121239)