31 KiB
Внешний аудит 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, в которой я srezalуглы и принимал заявления AUDIT.md без независимой верификации. Текущий отчёт — полностью verified.
Предыдущие отчёты:
- claude-opus-4-7_2026-04-26_T201805.md — M1 layer
- claude-opus-4-7_2026-04-26_T232707.md — M2 layer
- 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
-
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 — не соответствует факту и должно быть обновлено. -
AUDIT.md history accountability — добавить в Audit History строки про:
- Мой второй external audit (2026-04-26 T232707, M2 scope, 17 findings)
- Этот VERIFIED третий audit (T124438) с corrections к incomplete first version (T121239)
-
M3-A-4 (LOW→MEDIUM при misuse) — TransferActivation cooldown bypass через
validate(op)dispatcher: либо удалить TransferActivation из generic dispatcher, либо изменить signature на возвратOpError::ContextRequired.
MEDIUM
- M3-A-1, M3-A-2 —
checked_add/checked_subдля consistency - M3-A-3 — resolve spec ambiguity (spec patch либо code change для genesis_candidate_root)
- F-5 — runtime telemetry warning при
mlockfailure
LOW
- M3-A-5 — документировать operator_account_id invariant в module comment
- M3-A-7 — typestate pattern для settle/apply_proposal ordering
12. Итоговая оценка уровня безопасности
Шкала такая же как в первых двух отчётах
Оценка M3 layer (verified): 8.5 / 10
За что 8.5 (положительное, теперь полностью verified):
- ✅ Pure Rust, 0 unsafe в M3 (verified
grep -rn "unsafe " crates/mt-account/src/) - ✅ SSI Правило R2 (op_hash без signature) — 6 dedicated tests verify
- ✅ Validate-before-apply pattern — 7
expect("protocol invariant: validate_* ensures...")documented - ✅ Checked arithmetic в balance/op_height/account_chain_length — verified test
apply_transfer_panics_on_unsanitized_underflow - ✅ Anti-spam via [I-15] time-based cooldown
- ✅ ChangeKey signed by old key — adversarial test
validate_change_key_rejects_signature_by_new_key_not_old - ✅ TransferActivation receiver binding — test
validate_transfer_activation_rejects_bad_binding - ✅ settle_window canonical sort by op_hash
- ✅ apply_proposal orchestration ordering (emission → tick)
- ✅ Genesis state design clean
- ✅ MonetaryState теперь в state_root (M2-13 closure verified)
- ✅ Production pin 41/40 binding test vectors (M2-3 closure verified)
- ✅ NIST KAT 65/65 cases byte-exact с fresh NIST source
- ✅ 785 tests PASS, 0 FAIL, 1 ignored (cargo test --all)
- ✅ cargo audit / clippy / fmt — все clean
- ✅ F-2, F-3, F-4, F-6, F-7, F-8, F-9, F-12, F-18 verified закрыты конструкцией
- ✅ Все 7 unsafe blocks с формальным SAFETY comment
За что снимаю 1.5 (отрицательное, теперь честно):
- ❌ F-19 НЕ закрыт — заявление AUDIT.md о closure не подтверждается. Нарушение accountability.
- ❌ M3-A-4 TransferActivation cooldown bypass risk через generic dispatcher
- ❌ M3-A-1, M3-A-2 inconsistent checked arithmetic
- ❌ M3-A-3 spec ambiguity не resolved
- ❌ F-5 runtime warning не добавлен (только doc-level closure)
- ❌ AUDIT.md audit history не упоминает мой второй и этот третий external audit
- ❌ Genesis bootstrap pubkeys placeholder (re-classified, всё ещё блокер mainnet)
- ❌ Глобальные ограничения: 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. Метаданные воспроизведения
# Все 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)