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

795 lines
57 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 — отчёт
**Аудитор:** Claude Opus 4.7 (1M context), модель `claude-opus-4-7[1m]`
**Дата проведения:** 2026-04-26, T20:18:05 — T20:55:00 (московское время плюс)
**Локация:** `/Users/kh./Python/Ничто/Монтана/Русский/Протокол/Код/`
**Длительность:** ~37 минут активного аудита
**Режим выполнения:** одно ядро / один процесс (соблюдён `.cargo/config.toml`: `jobs = 1`, `RUST_TEST_THREADS = 1`)
---
## 1. Аудиторская методология и доверие
### Что я доверял (источники истины)
- **Только исходный код:** `*.rs`, `*.c`, `*.h`, `Cargo.toml`, `Cargo.lock`, `build.rs`, `rust-toolchain.toml`, `clippy.toml`, `.cargo/config.toml`
- **Тестовые fixtures** при условии независимой верификации против внешнего источника
- **Внешние публичные стандарты** как основу истины:
- NIST FIPS 204 (ML-DSA), FIPS 203 (ML-KEM), FIPS 180-4 (SHA-256)
- RFC 5869 (HKDF), RFC 4231 (HMAC-SHA-256), RFC 7914 (PBKDF2 test vectors), RFC 8018 (PBKDF2)
- BIP-39 (контрольная сумма мнемоники)
- **Публичный репозиторий NIST CAVP** — `https://github.com/usnistgov/ACVP-Server` как независимый источник KAT-векторов
- **RustSec Advisory DB** через `cargo audit` (1058 advisories, обновлено 2026-04-25)
### Что я НЕ доверял (исключённые источники)
- **Все markdown-файлы** в `/Users/kh./Python/Ничто/Монтана/Русский/Протокол/`:
- `AUDIT.md` — рассматривался как **текст с заявлениями требующими проверки**
- `CLAUDE.md`, `CRITIC.md` (роли архитектора и критика)
- `ROADMAP.md`, `VERSION.md`, `README.md`
- Спецификация `Montana v33.0.0.md` и приложения `Montana App v3.8.0.md`
- Все исторические артефакты в `Архив/`
- **Комментарии в коде** (`// SAFETY:`, `// spec, раздел "..."`) — рассматривались как **claims-to-verify**, не как авторитетные утверждения
- **Хардкоженные тестовые expected hex значения** в `tests/` — где они self-derived (regression baseline), а не cross-checked против NIST/RFC, оценивались как regression-tests, не как conformance-proofs
### Источники ground truth, использованные в аудите
| Стандарт / источник | URL / ссылка | Применение |
|---------------------|---------------|------------|
| NIST FIPS 204 (ML-DSA) | csrc.nist.gov/pubs/fips/204/final | Размеры ключей, deterministic Sign Algorithm 2 |
| NIST FIPS 203 (ML-KEM) | csrc.nist.gov/pubs/fips/203/final | Размеры ключей, KeyGen_internal(d, z) |
| NIST FIPS 180-4 (SHA-256) | csrc.nist.gov/pubs/fips/180/4/final | Hash test vector "abc" |
| NIST CAVP ACVP-Server | github.com/usnistgov/ACVP-Server | 51 KAT для ML-DSA-65, ML-KEM-768, ML-DSA SigGen |
| RFC 5869 (HKDF) | rfc-editor.org/rfc/rfc5869 | Test vectors A.1, A.2, A.3 |
| RFC 4231 (HMAC-SHA-256) | rfc-editor.org/rfc/rfc4231 | Test cases 1, 2, 4, 6 |
| RFC 7914 (PBKDF2) §11 | rfc-editor.org/rfc/rfc7914 | Test vectors 1, 2 |
| BIP-39 | github.com/bitcoin/bips/blob/master/bip-0039.mediawiki | Структура мнемоники, контрольная сумма |
| RustSec Advisory DB | github.com/RustSec/advisory-db | 1058 advisories на 2026-04-25 |
---
## 2. Объём аудита и фактическое состояние кода
### 2.1. Workspace inventory
Workspace содержит **14 crates** на момент начала аудита (изменилось до 14 в той же конфигурации к моменту окончания: один crate `mt-timechain` был замен на `mt-timechain` во время аудита — отмечено как finding F-12).
Crates присутствующие в workspace:
```
mt-account mt-codec mt-consensus mt-crypto mt-crypto-native
mt-entry mt-examples mt-genesis mt-lottery mt-merkle
mt-mnemonic mt-timechain mt-state mt-store
```
### 2.2. Audit scope (M1 — фундаментальный криптографический слой)
Согласно `AUDIT.md` (которому я не доверяю, но используется как scope-marker), audit-ready scope ограничен M1:
| Crate | Файл | Строк (фактически) | Назначение |
|-------|------|---------------------|------------|
| mt-crypto | crates/mt-crypto/src/lib.rs | **643** | Public Rust API (ML-DSA-65 + ML-KEM-768 + SHA-256) |
| mt-crypto-native (Rust) | crates/mt-crypto-native/src/lib.rs | **40** | FFI декларации к Layer 2 |
| mt-crypto-native (C) | crates/mt-crypto-native/csrc/mt_crypto.c | **375** | C-обёртка над OpenSSL EVP API |
| mt-crypto-native (header) | crates/mt-crypto-native/csrc/mt_crypto.h | **56** | C декларации + 13 кодов ошибок |
| mt-crypto-native build | crates/mt-crypto-native/build.rs | **45** | Сборка vendored OpenSSL + С |
| mt-mnemonic core | crates/mt-mnemonic/src/lib.rs | 17 | Re-exports |
| mt-mnemonic mnemonic | crates/mt-mnemonic/src/mnemonic.rs | **194** | Mnemonic ↔ master_seed ↔ per-role seeds |
| mt-mnemonic pbkdf2 | crates/mt-mnemonic/src/pbkdf2.rs | **136** | PBKDF2-HMAC-SHA-256 |
| mt-mnemonic hkdf | crates/mt-mnemonic/src/hkdf.rs | **180** | HKDF-Expand RFC 5869 |
| mt-mnemonic hmac | crates/mt-mnemonic/src/hmac.rs | **143** | HMAC-SHA-256 RFC 2104 |
| mt-mnemonic bit_packing | crates/mt-mnemonic/src/bit_packing.rs | **135** | 24×11 бит ↔ 33 байта |
| mt-mnemonic wordlist | crates/mt-mnemonic/src/wordlist.rs | **132** | 2048-слов wordlist + fingerprint check |
| mt-codec | crates/mt-codec/src/lib.rs | **351** | CanonicalEncode + Domain separators (32) |
| mt-merkle | crates/mt-merkle/src/lib.rs | **474** | Sparse Merkle Tree depth=256 |
**Total M1 audit surface (без тестов): ~2 921 строка.**
### 2.3. Тестовая инфраструктура M1
| Test файл | Строк | Тестов | Назначение |
|-----------|-------|---------|------------|
| crates/mt-crypto/tests/security_invariants.rs | 246 | 13 | Security invariants (no Clone, heap, no log) |
| crates/mt-crypto-native/tests/kat_independent.rs | 228 | 6 | Internal regression baselines |
| crates/mt-crypto-native/tests/nist_acvp_kat.rs | 279 | 3 | **NIST CAVP cross-check (51/51 cases)** |
| crates/mt-mnemonic/tests/keygen_vectors.rs | 170 | 7 | 5 KAT-векторов KeyGen + determinism |
| crates/mt-mnemonic/tests/test_vectors.rs | 121 | 6 | M-1 binding векторы (mnemonic → master_seed) |
| crates/mt-mnemonic/tests/e2e_recovery.rs | 161 | 3 | End-to-end recovery определённый идемпотентным |
NIST CAVP fixtures (verified против externally downloaded NIST source):
| Fixture | Тестов | Источник | Размер |
|---------|---------|---------|---------|
| ml_dsa_65_keygen.json | 25 | NIST ACVP-Server gen-val/json-files/ML-DSA-keyGen-FIPS204 | 302 940 байт |
| ml_kem_768_keygen.json | 25 | NIST ACVP-Server gen-val/json-files/ML-KEM-keyGen-FIPS203 | 184 841 байт |
| ml_dsa_65_siggen_det_external_pure_empty_ctx.json | 1 | NIST ACVP-Server gen-val/json-files/ML-DSA-sigGen-FIPS204 (tgId=3, deterministic, external, pure preHash) | 19 503 байта |
### 2.4. Unsafe blocks (фактический подсчёт)
В `crates/mt-crypto/src/lib.rs` найдено **7** unsafe-блоков на строках:
| Строка | Контекст | Содержит `// SAFETY:` |
|--------|----------|------------------------|
| 168 | `impl Drop for SecretKey``libc::munlock` | НЕТ |
| 187 | `fn alloc_locked_secret_box``libc::mlock` | НЕТ |
| 224 | `fn keypair_from_seed` — FFI в `mt_keypair_from_seed_mldsa` | ДА (lines 225-229) |
| 267 | `fn sign` — FFI в `mt_sign_mldsa` | ДА (lines 268-272) |
| 282 | `fn verify` — FFI в `mt_verify_mldsa` | ДА (lines 283-286) |
| 351 | `impl Drop for MlkemSecretKey``libc::munlock` | НЕТ |
| 365 | `fn keypair_from_seed_mlkem` — FFI в `mt_keypair_from_seed_mlkem` | ДА (lines 366-372) |
**Итог:** 7 unsafe-блоков, из которых только 4 имеют формальный `// SAFETY:` комментарий.
### 2.5. Зависимости (Cargo.lock)
Полное дерево production-зависимостей mt-crypto:
```
mt-crypto
├── libc =0.2.169
├── sha2 =0.10.9
│ ├── cfg-if =1.0.4
│ ├── cpufeatures =0.2.17
│ └── digest =0.10.7
│ ├── block-buffer =0.10.4
│ └── crypto-common =0.1.7
│ └── generic-array =0.14.7
│ ├── typenum =1.19.0
│ └── version_check =0.9.5
├── zeroize =1.8.1
└── mt-crypto-native (path)
├── libc =0.2.169
└── (build) openssl-src =300.5.5+3.5.5
└── (build) cc =1.2.16
├── jobserver =0.1.32
└── shlex =1.3.0
```
**Все версии закреплены exact** (`=X.Y.Z`). Это правильно для воспроизводимости. Production-зависимостей в реальном release-билде — **8** на верхнем уровне (libc, sha2, zeroize, mt-crypto-native, и transitive cfg-if, cpufeatures, digest, block-buffer, crypto-common, generic-array, typenum, version_check).
OpenSSL версия: **3.5.5 LTS** (vendored через openssl-src). Это **production-grade** библиотека с FIPS 140-3 валидацией, многолетней эксплуатацией в TLS-стеке, поддержкой до апреля 2030 года.
---
## 3. Сильные стороны
### 3.1. NIST FIPS conformance подтверждена независимо
**Самый значимый positive finding аудита.** Я скачал источники NIST CAVP test vectors напрямую с публичного репозитория `https://github.com/usnistgov/ACVP-Server` и сравнил байт-в-байт с локальными fixtures:
| Тест | Байт-в-байт совпадение |
|------|--------------------------|
| ML-DSA-65 KeyGen (25 cases, tcId 26-50) | **25/25** ✅ |
| ML-KEM-768 KeyGen (25 cases) | **25/25** ✅ |
| ML-DSA-65 SigGen (1 case, tgId=3 deterministic external pure empty ctx) | **1/1** ✅ |
Canonical SHA-256 локального ML-DSA-65 fixture: `2cbfd5571eabd93255bfee654f97b5a29d61351e11d17024cabf726b4f864b67`
Canonical SHA-256 NIST source ML-DSA-65 группы 2: `2cbfd5571eabd93255bfee654f97b5a29d61351e11d17024cabf726b4f864b67`
**Это значит:** код Montana (через OpenSSL 3.5.5 LTS backend) byte-exact производит pubkey/secretkey/signature такие же, как заявлено NIST как official correct output для этих PQ алгоритмов.
### 3.2. Постквантовая криптография через production-grade backend
Архитектурное решение использовать OpenSSL 3.5.5 LTS вместо pre-1.0 RustCrypto pure-Rust крейтов **корректное** для production audit readiness:
- OpenSSL 3.5 имеет встроенную поддержку ML-DSA и ML-KEM начиная с этой версии
- FIPS 140-3 валидированный криптографический модуль
- Десятилетия эксплуатации в TLS-стеке (Apache, nginx, OpenSSH, Linux ядро, AWS, Cloudflare)
- Audit history: множественные публичные аудиты OpenSSL Foundation и партнёров
Layer 2 (own thin C wrapper) **корректно реализован** — 375 строк фокусированной обвязки EVP API, читаемых и аудируемых.
### 3.3. Hygiena секретного материала
Реализация хранения секретов реализована с двумя слоями защиты:
1. **Heap-allocation через `Box<[u8; SECRET_KEY_SIZE]>`** — секретные байты живут в одной heap-локации от создания до уничтожения, никаких stack memcpy при move-операциях.
2. **`libc::mlock` на heap-странице** — best-effort защита от swap-out. На macOS использует kern.maxlockedmem, на Linux требует CAP_IPC_LOCK либо адекватного RLIMIT_MEMLOCK. При неудаче — fallback на non-locked Box (полагается на encrypted swap: FileVault / LUKS).
`Drop` реализация для `SecretKey` и `MlkemSecretKey` правильная:
- Сначала `self.0.zeroize()` — перезапись байтов нулями
- Затем `libc::munlock` — освобождение mlock'а перед dealloc
Compile-time проверки в `tests/security_invariants.rs`:
- `secret_key_is_not_clone` — гарантия что `SecretKey` не может быть случайно склонирован через `#[derive(Clone)]`
- `mlkem_secret_key_is_not_clone` — то же для ML-KEM
- `secret_key_no_partial_eq_to_prevent_timing_leak` — нет `PartialEq` (защита от timing-leak через memcmp)
- `secret_key_is_heap_allocated``size_of::<SecretKey>() == size_of::<usize>()` (1 указатель)
- `secret_key_needs_drop``std::mem::needs_drop::<SecretKey>()` true
Также **file-content scan** в тесте `no_println_or_log_on_secret_bytes_in_lib_code` — runtime проверка что в `mt-crypto/src/` нет логирующих макросов с `sk.as_bytes()`/`sk.0`/`SecretKey` references.
### 3.4. Memory safety FFI границы
Все 4 unsafe блока, реально пересекающие FFI границу к C-коду (`mt_keypair_from_seed_mldsa`, `mt_sign_mldsa`, `mt_verify_mldsa`, `mt_keypair_from_seed_mlkem`), имеют:
- `// SAFETY:` комментарий с объяснением валидности указателей
- Указатели на стек или heap-buffer известного размера
- Размеры буферов соответствуют объявленным C-константам
C-код в `mt_crypto.c` следует правильному pattern:
- `goto cleanup` для error handling
- NULL-checks для всех входных указателей
- NULL-check для `msg` только когда `msg_len != 0` — корректная edge case
- Memory cleanup на ВСЕХ путях (включая ошибки)
- Размеры проверяются `actual_len != expected_len` после OpenSSL вызовов
- Deterministic Sign явно установлен через `OSSL_SIGNATURE_PARAM_DETERMINISTIC=1`
### 3.5. Самостоятельные реализации криптопримитивов с RFC test vectors
Recovery flow (mnemonic → master_seed → per-role keys) реализован собственным кодом, не зависит от внешних crypto-крейтов:
- **`pbkdf2_hmac_sha256`** (136 строк) — реализация по RFC 8018 §5.2, с проверкой против:
- RFC 7914 §11 vector 1 (passwd, salt, c=1, dkLen=64)
- RFC 7914 §11 vector 2 (Password, NaCl, c=80000, dkLen=64)
- Public CryptoJS vector (password, salt, c=4096, dkLen=32)
- **`hkdf_expand`** (180 строк) — реализация по RFC 5869 §2.3, с проверкой против:
- RFC 5869 §A.1 (basic case with SHA-256)
- RFC 5869 §A.2 (long inputs)
- RFC 5869 §A.3 (empty info)
- **`hmac_sha256`** (143 строки) — реализация по RFC 2104, с проверкой против:
- RFC 4231 §4.2 case 1 (key 0x0b×20, "Hi There")
- RFC 4231 §4.3 case 2 ("Jefe", "what do ya want for nothing?")
- RFC 4231 §4.5 case 4 (long key, repeated 0xCD)
- RFC 4231 §4.7 case 6 (key longer than block size — triggers SHA-256 reduction)
Каждая реализация прошла RFC test vectors на момент аудита (через cargo test).
### 3.6. Anti-brute-force защита мнемоники
PBKDF2 итераций: **`KDF_ITER = 1_048_576 = 2²⁰`** — на 9 порядков сильнее BIP-39 стандарта (2²¹¹ = 2048). Это сознательное усиление защиты от brute-force.
Wordlist binding: SHA-256 fingerprint встроенного `Montana wordlist.txt` файла проверяется при инициализации (`init_wordlist`). Mismatch = panic при первом обращении к wordlist (paranoid integrity check).
### 3.7. Build infrastructure
- `rust-toolchain.toml` — pinned channel = stable, components = rustfmt + clippy
- `clippy.toml``msrv = "1.70"`
- `.cargo/config.toml` — single-thread/single-process для предотвращения перегрева (jobs=1, RUST_TEST_THREADS=1)
- `Cargo.toml``[profile.release]` с `lto = "fat"`, `codegen-units = 1`, `panic = "abort"`, `overflow-checks = true`
- `build.rs` корректно использует `CARGO_CFG_TARGET_OS` (а не `cfg!(target_os)`) для cross-compile
### 3.8. Cargo audit clean
`cargo audit` показал:
- **0 уязвимостей** (`vulnerabilities.found = false`, `count = 0`)
- **0 информационных warnings** (`warnings = {}`)
- 39 транзитивных зависимостей просканированы
- Advisory DB: 1058 advisories, последнее обновление 2026-04-25
### 3.9. Cargo clippy clean
`cargo clippy --all-targets -- -D warnings` прошёл успешно (exit 0). Все 14 crates checked, ни одного warning.
### 3.10. Все тесты M1 проходят
Прогон тестов M1 в одно ядро / один процесс:
| Crate | Тестов passed | Тестов failed | Время |
|-------|----------------|----------------|--------|
| mt-crypto unit | 23 | 0 | 0.06s |
| mt-crypto security_invariants | 13 | 0 | 0.01s |
| mt-crypto-native kat_independent | 6 | 0 | 0.14s |
| mt-crypto-native nist_acvp_kat | 3 | 0 | 0.02s |
| mt-mnemonic unit | 57 | 0 | 136s |
| mt-mnemonic e2e_recovery | 3 | 0 | 175s |
| mt-mnemonic keygen_vectors | 7 | 0 | 167s |
| mt-mnemonic test_vectors | 6 | 0 | 215s |
**Итог: 118 тестов passed, 0 failed.**
Длительность mt-mnemonic тестов объясняется PBKDF2 итерациями 2²⁰ (необходимо для anti-brute-force защиты).
### 3.11. Detached-keys design
Архитектура recovery flow **не хранит** долгосрочно privkey:
- Источник истины — мнемоника (24 слова на устройстве пользователя)
- master_seed выводится из мнемоники по требованию через PBKDF2-HMAC-SHA-256
- Per-role keys (account_key, node_key, app_encryption_key) выводятся из master_seed через HKDF-Expand
- Privkey материализуется в памяти только в момент подписи
Это **правильная** структура для recovery flow.
### 3.12. Domain separation
`mt-codec` определяет 32 различных domain separators (все начинающиеся с `mt-`). Используются для разделения contexts хеширования и derivation:
- Hashing: `mt-op`, `mt-proposal`, `mt-bundle`, `mt-merkle-leaf`, ...
- Identity derivation: `mt-account-key`, `mt-node-key`, `mt-app-encryption-key`
- PBKDF2 salt: `mt-seed`
Domain separation предотвращает cross-protocol confusion атаки на хеши.
---
## 4. Слабые стороны и Findings
Все findings нумерованы для трассировки. Severity:
- **CRITICAL** — может привести к компрометации secret material или consensus break
- **HIGH** — существенный риск безопасности или discipline
- **MEDIUM** — требует устранения для production audit
- **LOW** — minor / cosmetic / документация
### F-1 [LOW] — AUDIT.md устарел: расхождение line counts
**Описание.** AUDIT.md (line 16) заявляет что `mt-crypto/src/lib.rs` содержит **568 строк**. Фактически **643 строки** (расхождение 75 строк, 13.2%).
AUDIT.md также суммирует «Total own audit surface (Layer 1 + Layer 2): 1084 lines». Фактически: 643 + 40 + 375 + 56 + 45 = **1 159 строк**.
**Воспроизведение.**
```
wc -l crates/mt-crypto/src/lib.rs crates/mt-crypto-native/src/lib.rs \
crates/mt-crypto-native/csrc/mt_crypto.c crates/mt-crypto-native/csrc/mt_crypto.h \
crates/mt-crypto-native/build.rs
```
**Воздействие.** Аудитор может пропустить новый код добавленный после написания AUDIT.md. Внешний аудитор, читающий AUDIT.md как ground truth, получит неверную картину объёма работы.
**Рекомендация.** Установить CI gate: при любом PR верифицировать что line counts в AUDIT.md соответствуют реальным. Либо удалить hardcoded counts из AUDIT.md и оставить только команду для проверки.
### F-2 [HIGH] — `cargo fmt --check` FAILS, AUDIT.md заявляет «clean»
**Описание.** `AUDIT.md` (раздел 7) явно заявляет: `[x] cargo fmt --all -- --check clean`. Фактический прогон возвращает exit code 1 с 48 строк diff в `mt-crypto/src/lib.rs`.
**Воспроизведение.**
```
cd "/Users/kh./Python/Ничто/Монтана/Русский/Протокол/Код" && cargo fmt --all -- --check; echo "EXIT=$?"
```
Результат: EXIT=1, diff в строках 137, 149, 166, 235.
**Воздействие.**
- Нарушение discipline формализованной в роли архитектора (CLAUDE.md): «Все четыре — зелёные. Иначе не коммитить.»
- Несоответствие задекларированному pre-audit self-attestation
- Демонстрирует что AUDIT.md заявления не верифицированы автоматически перед публикацией
**Рекомендация.** Запустить `cargo fmt --all`, закоммитить, обновить AUDIT.md self-attestation только после реального прохождения проверки. Установить pre-commit hook, отвергающий коммиты при не-чистом fmt.
### F-3 [MEDIUM] — Stale references к pre-migration RustCrypto в example-коде
**Описание.** В `crates/mt-examples/examples/m1_crypto.rs`:
- Line 127: `print_kv("library", "ml-dsa 0.1.0-rc.8 (RustCrypto pure-Rust)");`
- Line 299: `print_kv("internal", "ml_dsa::ExpandedSigningKey::sign_deterministic (FIPS 204 Algorithm 2, deterministic variant)");`
Реальная library: OpenSSL 3.5.5 LTS через own thin C wrapper. RustCrypto `ml-dsa` крейт **не присутствует** в `Cargo.lock`.
**Воздействие.** Пользователь, запустивший пример, видит ложную информацию о backend. При external audit это вызывает confusion: «они мигрировали с RustCrypto на OpenSSL или нет?». Свидетельствует о неполной cleanup при миграции (M1-E phase migration).
**Рекомендация.** Обновить строки 127, 299 в `m1_crypto.rs` на актуальную информацию: `"OpenSSL 3.5.5 LTS via own C FFI wrapper"` и `"EVP_DigestSign with OSSL_SIGNATURE_PARAM_DETERMINISTIC=1"`.
### F-4 [MEDIUM] — 3 unsafe блока без `// SAFETY:` комментария
**Описание.** В `crates/mt-crypto/src/lib.rs` следующие unsafe-блоки **не имеют** формального `// SAFETY:` префикса:
| Строка | Контекст | Что делает |
|--------|----------|------------|
| 168 | `impl Drop for SecretKey` | `libc::munlock` heap-страницы |
| 187 | `fn alloc_locked_secret_box` | `libc::mlock` heap-страницы |
| 351 | `impl Drop for MlkemSecretKey` | `libc::munlock` heap-страницы |
Объяснения в обычных комментариях есть рядом, но не следуют требуемому формату `// SAFETY:` который установлен ролью архитектора (CLAUDE.md, Code Style: «`unsafe` блоки без архитектурного обоснования (комментарий формата `// SAFETY: ...`)`»).
**Воздействие.** AUDIT.md (line 16) явно указывает «Все `unsafe` blocks с `// SAFETY:` комментариями (4 блока: ...)». Это и неточно (фактически 7 блоков), и неверно по содержанию (3 из 7 без формального SAFETY).
Сами unsafe-операции (`mlock`/`munlock`) — простые системные вызовы с известной семантикой. Реального security-риска от отсутствия SAFETY-комментария нет, но это нарушение discipline и AUDIT.md utterance.
**Рекомендация.** Добавить `// SAFETY:` префикс к каждому из 3 блоков с явным обоснованием (например: «pointer valid for the lifetime of the Box; size matches allocated size»).
### F-5 [MEDIUM] — Best-effort `mlock` без runtime warning при failure
**Описание.** Функция `alloc_locked_secret_box` (строки 185-193 в `mt-crypto/src/lib.rs`):
```rust
fn alloc_locked_secret_box(size: usize) -> Box<[u8]> {
let boxed = vec![0u8; size].into_boxed_slice();
unsafe {
let _ = libc::mlock(boxed.as_ptr() as *const libc::c_void, size);
}
boxed
}
```
Return code `mlock` игнорируется через `let _ =`. При failure (например `RLIMIT_MEMLOCK` exceeded на Linux без CAP_IPC_LOCK, или `kern.maxlockedmem` exceeded на macOS) `mlock` возвращает `-1`, но код продолжает работу с **non-locked** Box.
Это означает: secret bytes могут быть выгружены в swap при memory pressure ОС. Защитой остаётся **только** encrypted swap (FileVault / LUKS) — оборона второй линии, которая зависит от настройки системы (не гарантирована).
Комментарий в коде (lines 177-184) корректно описывает это как «best-effort», но **никакой runtime сигнал** не идёт пользователю/администратору о fallback.
**Воздействие.**
- На systems без CAP_IPC_LOCK (типичный Docker container, default user account на Linux) `mlock` будет fail silently
- Администратор не узнает что secret material выгружается на диск
- При unencrypted swap — реальная утечка privkey
**Рекомендация.** Один из двух вариантов:
1. Логировать через telemetry/stderr при первом failure `mlock` («WARNING: secret memory не залочена в RAM, fallback на encrypted swap»)
2. Делать `mlock` обязательным (panic при failure), force-ить администратора настроить ulimit/CAP_IPC_LOCK
CLAUDE.md упоминает «Failure сигнал документируется через future telemetry, не блокирует операцию» — finding закрывается этим в roadmap.
### F-6 [HIGH] — Test-only `keypair()` использует слабую энтропию
**Описание.** В `crates/mt-crypto/src/lib.rs` строки 244-263:
```rust
#[cfg(any(test, feature = "testing"))]
pub fn keypair() -> (PublicKey, SecretKey) {
let mut seed = [0u8; KEYPAIR_SEED_SIZE];
let now = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH)...;
hasher.update(now.to_le_bytes());
hasher.update(std::process::id().to_le_bytes());
let addr = &seed as *const _ as usize;
hasher.update(addr.to_le_bytes());
...
}
```
Energy: `SystemTime::now()` (наносекунды UNIX epoch) + PID + stack address → SHA-256(64 байт). Это **не CSPRNG**.
**Воздействие.**
- Привязка через `#[cfg(any(test, feature = "testing"))]` означает функция доступна **только** в test-сборке либо при явном включении feature flag.
- Если разработчик ошибочно включит `--features testing` в production-бинарь — production identity будет генерироваться с низкоэнтропийным seed.
- Atttacker наблюдающий время запуска + знающий PID + heuristics на typical stack address может narrow-down keyspace до brute-forceable
- Используется внутри тестов **только как sanity-check** для primitive (real identity всегда через `keypair_from_seed` из HKDF)
**Рекомендация.** Заменить на CSPRNG через `getrandom` крейт (или `OsRng`) даже в test-сборке. Альтернативно: разместить `keypair()` в отдельном `mt-test-utils` крейте с `dev-dependencies`-only, чтобы исключить любой риск активации в production.
### F-7 [LOW] — Промежуточные buffers PBKDF2/HKDF/HMAC не zeroized
**Описание.** В `mt-mnemonic/src/pbkdf2.rs`:
- `t_i: Hash32`, `u_prev: Hash32`, `u_k: Hash32` — массивы 32 байта на стеке
- `salt_with_counter: Vec<u8>` — heap-allocated
- `dk: Vec<u8>` — выходной buffer
Эти буферы содержат **производные значения от password** (entropy ≡ secret после M-1 шага). После функции return:
- Stack-resident `t_i`/`u_prev`/`u_k` могут быть в неинициализированной stack-памяти до next stack-frame overwrite
- `salt_with_counter`/`dk` Vec drop'аются БЕЗ zeroize (Rust default Drop для Vec не zeroize)
- `dk` возвращается caller'у — caller отвечает за zeroize
В `mt-mnemonic/src/hkdf.rs`: то же для `hmac_input`, `t_prev`, `t_i`.
В `mt-mnemonic/src/hmac.rs`:
- `key_block`, `key_ipad`, `key_opad` — массивы 64 байта на стеке (содержат key-derived material)
- `combined: Vec<u8>` в `sha256_concat` — heap-allocated (содержит inner padding ⊕ key plus message)
**Воздействие.**
- При side-channel атаке через memory inspection (например ядро crash-дамп, debugger attach) промежуточные buffer'ы могут быть найдены ещё некоторое время
- Production risk низкий: kernel core dump unusable без kernel exploit; debugger требует root
- Это hygiene-issue, не immediate vulnerability
**Рекомендация.** Импортировать `zeroize::Zeroizing<T>` для wrappе intermediate buffers, или explicit `.zeroize()` перед drop в коде. Не критично для current risk model, но повышает defense-in-depth.
### F-8 [MEDIUM] — Ограниченное покрытие SigGen NIST KAT
**Описание.** В `nist_acvp_kat.rs` функция `nist_acvp_ml_dsa_65_siggen_deterministic_external_pure_empty_context` тестирует только **1 case** из NIST CAVP коллекции. Это test от группы **tgId=3** ML-DSA-65 (deterministic, external interface, pure preHash, empty context), tcId=40, sk начинается с `EE564C44...`.
NIST CAVP содержит 8 групп для ML-DSA-65 SigGen:
- tgId=3: deterministic + external + pure (15 tests) — **тестируется 1/15**
- tgId=4: deterministic + external + preHash (15 tests) — не тестируется
- tgId=9, 10: deterministic + internal interface (15 + 15 tests) — не тестируется
- tgId=15, 16, 21, 22: nondeterministic variants — не тестируется (Montana только deterministic)
**Воздействие.**
- ML-DSA SigGen byte-exact conformance подтверждена только для 1 узкой комбинации параметров
- Если OpenSSL имеет bug в обработке non-empty context — Montana code это не поймает
- AUDIT.md acknowledges это в "Known limitations 1" — известный gap
**Рекомендация.** Расширить fixture до всех 15 cases tgId=3 (тот же group, разные seeds). Это даёт стабильную coverage для current Montana usage pattern (deterministic + external + pure + empty context). Расширение на не-empty context — отдельная phase когда понадобится FIPS context support.
### F-9 [MEDIUM] — KAT-baselines в `kat_independent.rs` self-derived, не cross-checked
**Описание.** В `crates/mt-crypto-native/tests/kat_independent.rs` все hardcoded SHA-256 fingerprints — **own baseline**, derived при первом прогоне теста, а не из NIST или другой external source.
Имя файла «kat_independent» вводит в заблуждение: «independent» здесь означает «independent of HKDF-derivation-tested-elsewhere», а не «independent reference implementation». Это **regression-tests**, не conformance-tests.
Real conformance-tests находятся в `nist_acvp_kat.rs` (51 case verified).
**Воздействие.** Минорное. Для самой conformance-проверки это OK — `nist_acvp_kat.rs` покрывает основной path (KeyGen). Но naming `kat_independent` может ввести в заблуждение auditor-а, читающего этот файл первым.
**Рекомендация.** Переименовать `kat_independent.rs``regression_baselines.rs` либо `internal_baselines.rs` для ясности. Удалить из его docstring любые claims о cross-implementation conformance.
### F-10 [LOW] — Self-test не проверяет против NIST output
**Описание.** Функция `mt_crypto::self_test()` (lines 430-460) проверяет:
1. Sizes match
2. Determinism (двойной KeyGen с тем же seed)
3. Sign/verify roundtrip
4. KAT 1 byte-exact: `keypair_from_seed([0x00; 32])` → SHA-256(pk) == hardcoded `EXPECTED_KAT_1_PK_SHA256` (own baseline)
KAT 1 hardcoded — **own baseline**, не NIST-derived. Если OpenSSL когда-то поменяет implementation (например baseline changes между OpenSSL 3.5.5 → 3.5.6), self_test fails — но это regression detection, не conformance proof.
**Воздействие.** Self-test покрывает определённый corner case (zero seed) и ловит drift, но не проверяет NIST conformance. Hardcoded values **могли быть** скопированы из failing implementation и потом self-test passing — без cross-check невозможно отличить.
**Рекомендация.** В `self_test()` добавить минимум 1-2 NIST CAVP test cases byte-exact (не self-derived hashes, а реальные NIST expected pk/sk). Это превратит self_test из pure-regression в **mini-conformance** check.
### F-11 [MEDIUM] — Cargo.lock divergence от Cargo.toml в момент аудита
**Описание.** В начале аудита (T20:18) Cargo.lock содержал запись `mt-timechain` (version 0.0.0). В конце аудита (T20:48) `Cargo.toml` workspace.members содержит `mt-timechain` вместо `mt-timechain`. Папка `crates/mt-timechain/` появилась во время аудита.
Это означает что **состояние репозитория изменилось во время аудита** — кто-то (вероятно агент архитектора в фоне или manual edit) переименовал/добавил crate.
**Воздействие.**
- Аудит провёл на снимке кода в T20:18
- Cargo build в момент T20:48 (cached) уже работает с новой структурой
- Cargo.lock на момент чтения был **out-of-sync** с Cargo.toml — `cargo build` обновит его при следующем запуске
Не security finding, но методологическая нота для аудита.
**Рекомендация.** Внешний аудит должен проводиться на **frozen branch** (release tag), не на active development branch. Подписать audit branch git tag перед началом, audit на этот tag.
### F-12 [LOW] — `mnemonic.split(' ')` строгий single-space parsing
**Описание.** В `mt-mnemonic/src/mnemonic.rs` line 41:
```rust
let words: Vec<&str> = mnemonic.split(' ').collect();
```
Если пользователь введёт мнемонику с двойным пробелом, табом, или newline — `split(' ')` даст пустые элементы или wrong tokens, что приведёт к `MnemonicError::WordCount` либо `MnemonicError::UnknownWord`. Многие BIP-39 wallets используют `split_whitespace()` для большей user-friendly.
**Воздействие.**
- UX issue: пользователь может думать что мнемоника не работает, хотя просто скопировал её с лишним whitespace
- Не security risk: malformed input correctly rejected
- Может быть intentional strict mode
**Рекомендация.** Проверить design intent. Если строгость намеренна (anti-tampering) — задокументировать в API docs. Если нет — заменить на `split_whitespace()`.
### F-13 [LOW] — 13 vs 12 error codes — semantic ambiguity
**Описание.** AUDIT.md (line 24) заявляет «13 error codes» в `mt_crypto.h`. Фактически:
- `MT_OK = 0` (success, не error)
- `MT_ERR_INVALID_INPUT = 1` ... `MT_ERR_SIGN_LENGTH_MISMATCH = 12` (12 error codes)
Итого **13 кодов** total, **12 errors**. Семантическая неоднозначность в documentation: «13 error codes» vs «12 errors + 1 ok = 13 total status codes».
Также: `from_code()` функция в `mt-crypto/src/lib.rs` обрабатывает 10 error variants явно + Other(c) catch-all. Не обрабатывает явно `MT_ERR_VERIFY_FAILED` (5) и `MT_ERR_KAT_MISMATCH` (6) — попадают в `Other(c)`. Не проблема (Verify возвращает bool, KAT-mismatch только из self_test), но также не отражено в error display.
**Рекомендация.** Уточнить AUDIT.md: «13 status codes (1 success + 12 errors)». Опционально: добавить явные variants для `VerifyFailed` и `KatMismatch` в `CryptoError` enum для полноты.
### F-14 [INFO] — Side-channel свойства не verified конструкцией
**Описание.** Constant-time свойства cryptographic operations (защита от timing-based extraction privkey) **не доказаны** для Montana code:
- ML-DSA/ML-KEM internal — ответственность OpenSSL (документировано как constant-time для production-grade builds)
- Montana FFI wrapper — простой проброс, без data-dependent branches
- HMAC/PBKDF2/HKDF собственные реализации в `mt-mnemonic` — XOR/SHA-256 операции, **в принципе constant-time** для текущей реализации, но без формального verification
- `memcmp` нигде не используется на user-controlled secret material (хорошо)
- Compile-time `!PartialEq` на SK types — защита от случайного `==` use
**Воздействие.** Без formal verification (через `subtle` crate, `dudect` testing, F* / hax) constant-time property — assumption based on code reading, не proof. На VPS (cloud neighbour shared cache) timing-based extraction теоретически возможна для не constant-time operations.
**Рекомендация.**
1. Документировать threat model явно: Montana не предполагает физический доступ или cloud-neighbour atтак (single-tenant deployment)
2. Опционально: добавить `subtle::ConstantTimeEq` для всех critical comparisons
3. Опционально: dudect testing harness для hot path операций
### F-15 [INFO] — Нет fuzzing infrastructure
**Описание.** В репозитории нет `fuzz/` директории, нет `cargo fuzz` setup, нет AFL/libFuzzer harness'ов. AUDIT.md упоминает fuzzing как possibly-applicable в pre-prerequisite checklist, но ни один harness не присутствует.
**Воздействие.** FFI entry points (`mt_keypair_from_seed_mldsa`, `mt_sign_mldsa`, `mt_verify_mldsa`, `mt_keypair_from_seed_mlkem`) не fuzzed. Malformed input через FFI может вызвать crash в C-коде или OpenSSL. C-wrapper делает NULL checks, но boundary conditions (например `msg_len = SIZE_MAX`) не explicitly tested.
Также: `mnemonic_to_master_seed` принимает `&str` от пользователя — fuzzing на это не настроен.
**Рекомендация.** Установить `cargo-fuzz` harness:
- `fuzz/fuzz_targets/fuzz_sign.rs` — fuzz `mt_crypto::sign(sk, msg)` с various sk corruption + various msg
- `fuzz/fuzz_targets/fuzz_verify.rs` — fuzz `mt_crypto::verify(pk, msg, sig)`
- `fuzz/fuzz_targets/fuzz_mnemonic.rs` — fuzz `mnemonic_to_master_seed`
С учётом доступа к серверам Moscow/Frankfurt — запустить 24-48 часов fuzzing на каждом.
### F-16 [INFO] — Отсутствует signature aggregation / threshold infrastructure
**Описание.** Заметка по архитектуре: M1 покрывает только individual key generation + sign + verify. Threshold signatures, multi-signature, signature aggregation — отсутствуют в текущем коде (что соответствует scope M1 по AUDIT.md).
**Воздействие.** Это **не bug** и **не finding** в M1 scope. Просто scope acknowledgment.
**Рекомендация.** Если будущие phases требуют threshold ML-DSA signatures (или ML-KEM-based encapsulation) — учесть что OpenSSL EVP API не предоставляет эти примитивы напрямую. Потребуется отдельный crate либо C extension.
### F-17 [LOW] — `serde_json` зависимость только для test fixtures parse
**Описание.** `mt-crypto-native/Cargo.toml` имеет `dev-dependencies`:
```toml
serde = { version = "=1.0.219", features = ["derive"] }
serde_json = "=1.0.140"
```
Используется **только** в `tests/nist_acvp_kat.rs` для парсинга NIST JSON fixtures. Это `dev-dependencies` — не попадает в production builds.
`serde_json` имеет довольно большое transitive deps (proc-macro2, quote, syn, serde_derive — auxiliary build crates). Не угроза но adds dependency surface.
**Воздействие.** Минимальное (dev-only). Acknowledgment.
**Рекомендация.** Опционально: заменить на ручной JSON parser через `std::str::Lines` для NIST fixtures (avoid serde dependency). Не приоритетно.
### F-18 [LOW] — `parallel` feature `cc` крейта противоречит single-thread политике
**Описание.** В `mt-crypto-native/Cargo.toml`:
```toml
cc = { version = "=1.2.16", features = ["parallel"] }
```
`.cargo/config.toml` устанавливает `jobs = 1` глобально. Feature `parallel` для `cc` крейта позволяет parallel компиляцию C файлов. У нас единственный C файл (`mt_crypto.c`), так что фича не активирует параллелизм. Но **противоречит** заявленной политике «single-process / single-thread».
**Воздействие.** Поведенчески — никакого (один C файл). Документально — рассогласование с .cargo/config.toml comment.
**Рекомендация.** Удалить `features = ["parallel"]` из `cc` dependency для consistency.
### F-19 [LOW] — `OSSL_PARAM_construct_octet_string` имплицитный const-cast
**Описание.** В `mt_crypto.c` line 62:
```c
params[0] = OSSL_PARAM_construct_octet_string(
seed_param_name, (void*)seed, seed_len
);
```
`seed` — declared as `const uint8_t*`. Cast `(void*)seed` теоретически удаляет const. Это **convention** OpenSSL API: `OSSL_PARAM_construct_octet_string` принимает `void*`, но не модифицирует данные — но тип API не выражает immutability. Не bug, но C compiler без `-Wcast-qual` это не ловит.
**Воздействие.** Никакого. `OSSL_PARAM_construct_octet_string` documented как read-only on input data. Это OpenSSL API limitation, не Montana bug.
**Рекомендация.** Acknowledgment в SAFETY-комментарии или документация. Альтернативно: добавить C-flag `-Wno-cast-qual` в build.rs если warning ловится.
---
## 5. Известные ограничения этого аудита
Что я **не мог** проверить даже с серверным доступом:
1. **Side-channel attacks через физическое оборудование** — нет осциллографа, измерителя мощности, EM-зонда
2. **Cryptanalysis самих ML-DSA / ML-KEM** — академическая работа NIST PQC competition, out of scope
3. **Formal verification** через F\* / hax / EasyCrypt / Coq — toolchain недоступен в среде, требует переписывания кода под proof framework
4. **Корректность OpenSSL внутри (Layer 3)** — миллионы строк C-кода, отдельный аудит OpenSSL Foundation
5. **Bugs в компиляторе rustc/cc** — атака «Trusting Trust» (Ken Thompson 1984), требует второго независимого compilers
6. **Документ-уровневая legal certification** — нет печати NCC Group / Trail of Bits / Quarkslab / Cure53 / Kudelski
Что я мог бы сделать с серверами но **не делал** в этой сессии (ограничение 1 ядро / 1 процесс + рамки времени):
7. **Двойная независимая Docker сборка** для верификации reproducible builds (Mac + Moscow + Frankfurt)
8. **Long-running fuzzing** 24-48 часов через `cargo fuzz` на сервере
9. **Cross-platform smoke testing** на Linux x86_64 vs macOS ARM64
10. **Supply chain audit** OpenSSL bytes из openssl-src vs openssl.org official tarball SHA-256
11. **Statistical timing measurement** на серверах для weak constant-time signal
Эти 5 пунктов **не были выполнены** в текущем аудите но **доступны** для расширения.
---
## 6. Рекомендации по приоритетам
### Приоритет «закрыть до production audit» (HIGH severity)
1. **F-2**`cargo fmt --all`, обновить AUDIT.md self-attestation после real-prog проверки
2. **F-6** — заменить `keypair()` test helper на CSPRNG-based, либо вынести в `mt-test-utils` крейт
3. **F-5** — runtime warning при `mlock` failure, либо makemandatory с graceful error
### Приоритет «закрыть до v1.0 release» (MEDIUM severity)
4. **F-3** — обновить stale comments в `m1_crypto.rs` (line 127, 299)
5. **F-4** — добавить `// SAFETY:` комментарии к 3 unused-marker блокам (lines 168, 187, 351)
6. **F-8** — расширить SigGen NIST KAT до 15 cases tgId=3
7. **F-9** — переименовать `kat_independent.rs``regression_baselines.rs`
### Приоритет «закрыть для документационного качества» (LOW severity)
8. **F-1** — sync line counts в AUDIT.md
9. **F-10** — добавить NIST CAVP byte-exact в `self_test()` функцию
10. **F-13** — уточнить «13 status codes (1 success + 12 errors)»
11. **F-12** — design intent strict whitespace mode mnemonic
12. **F-7**`Zeroizing<T>` для PBKDF2/HKDF/HMAC intermediate state
13. **F-17** — опционально удалить `serde_json` dev-dependency
14. **F-18** — удалить `cc` parallel feature
15. **F-19** — комментарий OpenSSL API const-cast convention
### Приоритет «infrastructure improvement» (INFO)
16. **F-14** — формализовать constant-time свойства (subtle crate / dudect)
17. **F-15** — установить `cargo-fuzz` harness
18. **F-11** — audit на frozen git tag
### Приоритет «требуется external auditor с физическим/легальным доступом»
- Side-channel hardware testing (осциллограф, power meter)
- Formal verification ML-DSA/ML-KEM internal — ответственность OpenSSL Foundation / NIST
- Audit firm signature (NCC Group / Trail of Bits / Quarkslab / Cure53 / Kudelski)
---
## 7. Итоговая оценка уровня безопасности
### Шкала 1-10:
- **10** — formally verified, audit firm signed, side-channel proven, multi-vendor reviewed, deployed at scale years
- **9** — audited by recognized firm, side-channel constant-time documented, NIST FIPS validated implementation
- **8** — strong cryptographic foundation, NIST conformance independently verified, minimal attack surface, comprehensive testing, documented threat model, with minor discipline/documentation findings
- **7** — strong foundation but multiple medium-severity findings outstanding
- **6** — code reads well but missing critical infrastructure (fuzzing, formal verification, external audit)
- **≤5** — security-critical issues found
### Оценка Montana M1: **8 / 10**
**Обоснование оценки 8:**
**За что ставлю 8 (положительное):**
1. ✅ NIST FIPS 204/203 byte-exact conformance **независимо подтверждена** (51/51 KAT)
2. ✅ Production-grade backend — OpenSSL 3.5.5 LTS (не pre-1.0 RustCrypto)
3. ✅ Heap-allocated SK с mlock + Drop+zeroize — правильная hygiene
4. ✅ Compile-time security invariants (No Clone/Copy/PartialEq на SK)
5. ✅ 0 уязвимостей в `cargo audit` (39 deps scanned, 1058 advisories)
6.Все RFC test vectors PBKDF2/HKDF/HMAC проходят
7. ✅ 118 тестов passed, 0 failed
8. ✅ Clippy clean (`-D warnings`)
9. ✅ Strict version pinning (все exact `=X.Y.Z`)
10. ✅ Cross-platform build correctness через `CARGO_CFG_TARGET_OS`
11. ✅ Reproducible build infrastructure prepared (Cargo.lock + rust-toolchain.toml)
12. ✅ Thoughtful threat model (heap+mlock, deterministic Sign, no logging SK)
**За что снимаю 2 (отрицательное):**
1.`cargo fmt --check` FAILS (F-2) — нарушение discipline которая декларируется
2. ❌ AUDIT.md заявления не сверены с фактом (F-1, F-4, F-13, F-18) — документация устарела
3. ❌ Stale references к pre-migration RustCrypto (F-3) — confusion для auditor
4. ❌ Test-only `keypair()` использует weak entropy (F-6) — теоретический risk при misuse
5. ❌ Best-effort `mlock` без runtime warning (F-5) — silent fallback на encrypted swap
6. ❌ Coverage SigGen NIST KAT — только 1/15 cases в supported group (F-8)
7. ❌ Нет fuzzing infrastructure (F-15) — FFI boundary не tested на malformed input
8. ❌ Constant-time свойства не verified конструкцией (F-14)
9. ❌ Нет внешней audit firm signature (требование regulator/insurer)
10. ❌ Нет formal verification (F\* / hax / Coq)
**Чтобы поднять до 9:** закрыть F-2, F-3, F-4, F-5, F-6, F-8 + sync AUDIT.md (F-1) + установить fuzzing harness (F-15).
**Чтобы поднять до 10:** + audit firm signature + formal verification критических путей + side-channel hardware testing + multi-tenant deployment hardening.
### Заключение
Кодовая база Montana M1 (foundational crypto + identity recovery) демонстрирует **сильную инженерную дисциплину** и **независимо подтверждённую NIST FIPS 204/203 conformance**. Архитектурный выбор использовать OpenSSL 3.5.5 LTS вместо pre-1.0 RustCrypto — правильный для production audit readiness.
Найденные findings — преимущественно **документационная drift** (AUDIT.md устарел) и **minor security hygiene** issues. Критические уязвимости отсутствуют в audited scope.
Код **готов к external audit firm review** после закрытия HIGH-severity findings (F-2, F-5, F-6). До закрытия этих — рекомендую дополнительный pass самокритики со стороны команды.
Я **не подменяю** аудит recognized firm, не предоставляю legal certification, не закрываю side-channel и formal verification gaps. Этот отчёт — **подготовительный аудит** уровня внутренней проверки качества, который сэкономит платный audit firm часы на очевидное и предоставит им более чистую базу для критического обзора.
---
## 8. Метаданные воспроизведения
**Команды для проверки findings (одной строкой каждая):**
```
cd "/Users/kh./Python/Ничто/Монтана/Русский/Протокол/Код" && wc -l crates/mt-crypto/src/lib.rs
```
```
cd "/Users/kh./Python/Ничто/Монтана/Русский/Протокол/Код" && cargo fmt --all -- --check
```
```
cd "/Users/kh./Python/Ничто/Монтана/Русский/Протокол/Код" && cargo clippy --all-targets -- -D warnings
```
```
cd "/Users/kh./Python/Ничто/Монтана/Русский/Протокол/Код" && cargo audit --json
```
```
cd "/Users/kh./Python/Ничто/Монтана/Русский/Протокол/Код" && cargo test -p mt-crypto-native --test nist_acvp_kat -- --nocapture
```
```
cd "/Users/kh./Python/Ничто/Монтана/Русский/Протокол/Код" && cargo test -p mt-crypto -p mt-crypto-native -p mt-mnemonic
```
```
cd /tmp && curl -sL "https://raw.githubusercontent.com/usnistgov/ACVP-Server/master/gen-val/json-files/ML-DSA-keyGen-FIPS204/internalProjection.json" -o nist_mldsa_keygen.json
```
**Среда выполнения:**
- Платформа: Darwin 24.6.0 (macOS)
- Архитектура: ARM64 (Apple Silicon)
- rustc: 1.92.0 (Homebrew, ded5c06cf 2025-12-08)
- cargo: 1.92.0 (Homebrew)
- cargo-audit: установлен в `/Users/kh./.cargo/bin/cargo-audit`
**Доступные но не использованные ресурсы (для будущего расширения):**
- montana-moscow (176.124.208.93, Linux x86_64)
- montana-frankfurt (89.19.208.158, Linux x86_64)
---
**Аудитор:** Claude Opus 4.7 (1M context)
**Подпись модели:** `claude-opus-4-7[1m]`
**Дата создания отчёта:** 2026-04-26
**Идентификатор аудита:** claude-opus-4-7_2026-04-26_T201805