# SSOT — Rust как единственный источник истины криптографии и протокола Статус: **обязательный архитектурный инвариант** Montana. Применяется ко всему коду, выпускаемому под именем Montana. Нарушение блокирует релиз. ## Правило Любая криптографическая, ключевая или протокольная операция — **мнемоника → master_seed**, **derive_account_id**, **derive_node_id**, **ML-DSA-65 / ML-KEM-768 keygen / sign / verify / encap / decap**, **Transaction encoding и подпись**, **state-root computation**, **проверка балансов и переводов** — определена **ровно в одном месте**: Rust-крейтах `Montana/Montana-Protocol/Code/crates/`. Канонические крейты: - [`mt-mnemonic`](../../Montana-Protocol/Code/crates/mt-mnemonic/) — 24 слова → master_seed (PBKDF2-HMAC-SHA-256, 2²⁰ итераций, salt `"mt-seed"`); HKDF-Expand для ролей. - [`mt-crypto`](../../Montana-Protocol/Code/crates/mt-crypto/) — ML-DSA-65 (FIPS 204), ML-KEM-768 (FIPS 203), детерминированные keypair/sign/verify через OpenSSL FIPS provider (через `mt-crypto-native`). - [`mt-codec`](../../Montana-Protocol/Code/crates/mt-codec/) — domain separators (`b"mt-seed"`, `b"mt-account-key"`, `b"mt-node-key"`, …), canonical byte encoding. - [`mt-state`](../../Montana-Protocol/Code/crates/mt-state/) — `derive_account_id(suite_id, pubkey) = SHA-256("mt-account" || suite_id_LE || pubkey)`; AccountTable, NodeTable. - [`mt-account`](../../Montana-Protocol/Code/crates/mt-account/) — Transaction (Transfer / ChangeKey / Anchor / TransferActivation), правила signed_scope, settlement. Все эти крейты экспортируются через единственный фасад: [`crates/mt-bindings`](../../Montana-Protocol/Code/crates/mt-bindings/) — стабильный C ABI (для iOS/macOS staticlib и Android cdylib) + WASM-binding (для веба). ## Запреты Нативный клиент Montana (iOS Swift, Android Kotlin/Java, веб JS/TS, macOS Swift, любой будущий) **не имеет права**: 1. Реализовывать **PBKDF2 / HMAC / HKDF / SHA-256** на месте — должен вызывать через FFI. 2. Реализовывать **ML-DSA-65 / ML-KEM-768 / Ed25519-substitute** для генерации ключей или подписи Montana-операций. 3. Иметь свою формулу **address derivation** — только `mt_derive_account_id`. 4. Кодировать `Transaction` своим способом — только через `mt-account` или его экспорт. 5. Хранить дублирующие константы (`PUBKEY_SIZE`, `KDF_ITER`, salts, suite_id) — все берутся из `mt_bindings.h` / Kotlin `MtBindings.*` / WASM exports. ## Эталонный KAT-вектор `crates/mt-bindings/tests/kat_cross_client.rs` фиксирует: ``` entropy = 32 × 0x00 mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art" master[..8] = 38a1421ac3ce191f acc_seed = 08ce5c19768c679fda24c0d3360e57ce03d00c94c175e59f50e9c77894c20818 pubkey[..16] = c1b89f8277a6cec0818419e69add3892 account_id = 9f199584ed120b987b617ba5bff829e176f23e5465dd70cfac5c141dfb131a21 ``` Каждый клиент (Swift через `MontanaBindings`, Kotlin через `libmontana.so`, JS через `mt_bindings.wasm`) обязан содержать конформанс-тест, который: 1. Принимает мнемонику-эталон. 2. Вызывает свой биндинг. 3. Сравнивает байт-в-байт с `account_id = 9f199584ed120b987b617ba5bff829e176f23e5465dd70cfac5c141dfb131a21`. Расхождение хоть в одном бите = клиент сломан, релиз блокируется. ## Сборка артефактов | Цель | Команда | Артефакт | |---|---|---| | macOS arm64 (host) | `cargo build -p mt-bindings --release` | `target/release/libmt_bindings.{a,dylib}` | | iOS sim arm64 | `cargo build -p mt-bindings --release --target aarch64-apple-ios-sim` | `target/aarch64-apple-ios-sim/release/libmt_bindings.a` | | iOS device arm64 | `cargo build -p mt-bindings --release --target aarch64-apple-ios` | (требует iOS-OpenSSL cross — отдельная задача) | | Android arm64 | `cargo ndk -t arm64-v8a build -p mt-bindings --release` | `libmt_bindings.so` | | Android x86_64 | `cargo ndk -t x86_64 build -p mt-bindings --release` | `libmt_bindings.so` | | Web | `wasm-pack build crates/mt-bindings --target web --features wasm` | `pkg/mt_bindings.js` + `.wasm` | C-заголовок: [`crates/mt-bindings/include/mt_bindings.h`](../../Montana-Protocol/Code/crates/mt-bindings/include/mt_bindings.h). ## Жизненный цикл изменений 1. Меняется крипто-параметр (например, KDF_ITER или новый suite_id) → меняется **Rust** + обновляется KAT-вектор. 2. После изменения Rust — пересобирается `mt-bindings`, переразлитваются артефакты для iOS/Android/Web. 3. CI прогоняет KAT во всех клиентах. Любое расхождение = abort. 4. ABI breaking changes — bump `MT_ABI_VERSION` в `lib.rs`; клиенты должны проверить `mt_abi_version()` до использования. ## История - **2026-05-24** — введение инварианта. До этого: iOS, Android, веб реализовывали PBKDF2 / address / signing самостоятельно с расходящимися параметрами (600k итераций vs 2²⁰, "mt+SHA256(pk)[:20]" vs canonical SHA256-with-domain, Ed25519 vs ML-DSA). Это блокировало байт-точные переводы между клиентами. Mt-bindings и эта спека закрывают расхождение.