6.3 KiB
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— 24 слова → master_seed (PBKDF2-HMAC-SHA-256, 2²⁰ итераций, salt"mt-seed"); HKDF-Expand для ролей.mt-crypto— ML-DSA-65 (FIPS 204), ML-KEM-768 (FIPS 203), детерминированные keypair/sign/verify через OpenSSL FIPS provider (черезmt-crypto-native).mt-codec— domain separators (b"mt-seed",b"mt-account-key",b"mt-node-key", …), canonical byte encoding.mt-state—derive_account_id(suite_id, pubkey) = SHA-256("mt-account" || suite_id_LE || pubkey); AccountTable, NodeTable.mt-account— Transaction (Transfer / ChangeKey / Anchor / TransferActivation), правила signed_scope, settlement.
Все эти крейты экспортируются через единственный фасад: crates/mt-bindings — стабильный C ABI (для iOS/macOS staticlib и Android cdylib) + WASM-binding (для веба).
Запреты
Нативный клиент Montana (iOS Swift, Android Kotlin/Java, веб JS/TS, macOS Swift, любой будущий) не имеет права:
- Реализовывать PBKDF2 / HMAC / HKDF / SHA-256 на месте — должен вызывать через FFI.
- Реализовывать ML-DSA-65 / ML-KEM-768 / Ed25519-substitute для генерации ключей или подписи Montana-операций.
- Иметь свою формулу address derivation — только
mt_derive_account_id. - Кодировать
Transactionсвоим способом — только черезmt-accountили его экспорт. - Хранить дублирующие константы (
PUBKEY_SIZE,KDF_ITER, salts, suite_id) — все берутся изmt_bindings.h/ KotlinMtBindings.*/ 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) обязан содержать конформанс-тест, который:
- Принимает мнемонику-эталон.
- Вызывает свой биндинг.
- Сравнивает байт-в-байт с
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.
Жизненный цикл изменений
- Меняется крипто-параметр (например, KDF_ITER или новый suite_id) → меняется Rust + обновляется KAT-вектор.
- После изменения Rust — пересобирается
mt-bindings, переразлитваются артефакты для iOS/Android/Web. - CI прогоняет KAT во всех клиентах. Любое расхождение = abort.
- 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 и эта спека закрывают расхождение.