# Внешний аудит кода 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::() == size_of::()` (1 указатель) - `secret_key_needs_drop` — `std::mem::needs_drop::()` 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` — heap-allocated - `dk: Vec` — выходной 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` в `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` для 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` для 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