Обновление архитектуры ключей и кошельков

This commit is contained in:
AidarKC
2026-10-01 17:11:38 +03:00
parent f452ce1f1e
commit 8d22602761
51 changed files with 565 additions and 898 deletions
+3 -3
View File
@@ -285,9 +285,9 @@
## `KeyRotationContinue`
Продолжает интерактивные post-PDA этапы. В текущей версии два будущих этапа являются честными заглушками:
Продолжает интерактивные post-PDA этапы. Сервер по-прежнему не хранит приватные ключи и сам не переводит средства. В текущем UI перед вызовом `KeyRotationContinue` на этапе `WALLET_MIGRATION` пользователь может перевести весь доступный SOL со старого blockchain-wallet на новый (или явно пропустить перенос). После этого сервер фиксирует этап как `NOT_IMPLEMENTED`/завершённый placeholder и переводит процесс дальше.
- `WALLET_MIGRATION`: `walletMigrationStatus = NOT_IMPLEMENTED`, затем переход в `MESSAGE_MIGRATION`;
- `WALLET_MIGRATION`: клиентский UI выполняет перенос SOL локально, используя временно доступный старый blockchain private key; сервер только продолжает state machine. Перенос AR/Turbo пока TODO;
- `MESSAGE_MIGRATION`: `messageMigrationStatus = NOT_IMPLEMENTED`, затем `FINALIZING -> COMPLETE`.
Request:
@@ -300,7 +300,7 @@ Request:
}
```
`KeyRotationContinue` не переводит деньги и не перешифровывает DM. Это специально оставленные точки расширения для будущей реализации.
`KeyRotationContinue` сам не переводит деньги и не перешифровывает DM. SOL-перевод реализован на стороне UI до этого запроса; серверная часть WALLET_MIGRATION остаётся точкой координации. AR/Turbo и DM остаются отдельными точками расширения.
## `KeyRotationAbort`
+1 -1
View File
@@ -70,7 +70,7 @@ TEXT-тип хранит сообщения, материалы и редакт
[32] toBlockHash32
```
`toForkNumber`: `1..999` — fork, который видел автор действия; `0` зарезервирован как unknown/legacy. Поле информационное и **не входит в логическую идентичность цели**. Цель определяется только `toLogin + toBlockGlobalNumber + toBlockHash32`; несовпадение текущего fork само по себе не инвалидирует ссылку.
`toForkNumber`: строго `1..999` — fork, который видел автор действия. Поле информационное и **не входит в логическую идентичность цели**. Цель определяется только `toLogin + toBlockGlobalNumber + toBlockHash32`; несовпадение текущего fork само по себе не инвалидирует ссылку.
Собственные edit (`TEXT_EDIT_POST`, `TEXT_EDIT_REPLY`) используют компактный target:
+1 -1
View File
@@ -24,4 +24,4 @@
[32] toBlockHash32
```
`toForkNumber` — только историческая метка (`1..999`, `0` reserved unknown/legacy). Логическая идентичность реакции: `toLogin + toBlockGlobalNumber + toBlockHash32`. При fork номер fork может измениться; если number+hash сохранены в новой ветке, LIKE/UNLIKE продолжает указывать на тот же логический блок.
`toForkNumber` — только историческая метка и всегда находится в диапазоне `1..999`. Логическая идентичность реакции: `toLogin + toBlockGlobalNumber + toBlockHash32`. При fork номер fork может измениться; если number+hash сохранены в новой ветке, LIKE/UNLIKE продолжает указывать на тот же логический блок.
+1 -1
View File
@@ -30,7 +30,7 @@ CONNECTION-тип описывает социальные связи и подп
[32] toBlockHash32
```
`toForkNumber` хранит fork, который видел автор связи (`1..999`), но не участвует в identity. `0` зарезервирован как unknown/legacy; `1000+` недопустим. Для связи на пользователя используется **реальный HEADER hash**: `toBlockGlobalNumber=0`, `toBlockHash32=SHA-256(Frame HEADER)`. Нулевой hash запрещён.
`toForkNumber` хранит fork, который видел автор связи (`1..999`), но не участвует в identity. Значения `0` и `1000+` недопустимы. Для связи на пользователя используется **реальный HEADER hash**: `toBlockGlobalNumber=0`, `toBlockHash32=SHA-256(Frame HEADER)`. Нулевой hash запрещён.
## Правила target
+1 -1
View File
@@ -48,7 +48,7 @@
Где:
- `toLogin` — login владельца целевого блока;
- `toForkNumber` — историческая метка fork (`1..999`, `0` reserved unknown/legacy), не часть identity;
- `toForkNumber` — историческая метка fork (`1..999`), не часть identity;
- `toBlockGlobalNumber` — номер целевого блока;
- `toBlockHash32` — хэш целевого блока;
- `text` — опциональное пояснение пользователя к статусу.
@@ -17,7 +17,7 @@ Block 0 новой пользовательской истории — `HEADER`:
SHiNE + login + initialBlockchainKey32
```
`initialBlockchainKey32` — public blockchain-signing key fork №1. При смене ключа сохранённый префикс, включая HEADER, перепубликуется с тем же Frame; поэтому это поле остаётся исходным ключом, а новый ключ конкретного fork определяется подписью/owner и PDA.
`initialBlockchainKey32` — public blockchain-signing key fork №1. Это только историческая запись происхождения цепочки. При смене ключа сохранённый префикс, включая HEADER, перепубликуется с тем же Frame; поле остаётся исходным ключом и не сравнивается с текущим ключом fork. Текущий ключ конкретного fork определяется подписью/owner и PDA.
## 3. Внешний target
@@ -31,7 +31,7 @@ SHiNE + login + initialBlockchainKey32
[32] toBlockHash32
```
`toForkNumber` — информационная историческая метка: fork, на который смотрел автор действия. `0` зарезервирован как unknown/legacy и не является реальным fork. Поле не участвует в identity и не должно сравниваться с текущим active fork как условие валидности.
`toForkNumber` — информационная историческая метка: fork, на который смотрел автор действия. Допустимы только реальные fork `1..999`; `0` запрещён. Поле не участвует в identity и не должно сравниваться с текущим active fork как условие валидности.
Логическая identity цели:
+16 -4
View File
@@ -55,8 +55,8 @@ seed(32) = SHA-256(material)
| Ключ | Суффикс | Назначение (кратко) |
|------|---------|---------------------|
| root | `root.key` | Личность. Подписывает unsigned-часть PDA-записи (`RootKeyBlock`). |
| blockchain | `blockchain.key` | Подписывает пользовательские блоки/ANS-104 DataItem и является текущим Solana-wallet/fee payer. |
| client | `client.key` | Общий клиентский ключ для DM/устройств и derivation отдельного Arweave SAWD-кошелька; не является текущим Solana-wallet. |
| blockchain | `blockchain.key` | Подписывает пользовательские блоки/ANS-104 DataItem и является каноническим источником пользовательских кошельков: Solana и Arweave SAWD-v1. |
| client | `client.key` | Общий клиентский ключ для входа на сервер, устройств и DM. Не используется как источник штатных кошельков. |
| homeserver | `homeserver.key:<имя>` | Ключ homeserver-устройства, по одному на каждый homeserver (различитель — имя). См. §4. |
Полные роли каждого ключа — в `docs/Keys/README.md`.
@@ -75,9 +75,21 @@ seed(32) = SHA-256(material)
`root.key` не является fee payer. Он используется как холодное дополнительное разрешение только для полной ротации root + client + blockchain fork.
`client.key` больше не используется как Solana-wallet/fee payer. Он остаётся клиентским криптографическим ключом (DM/устройства) и входом в отдельный протокол derivation Arweave-кошелька.
`client.key` не используется как wallet/fee payer. Он остаётся клиентским криптографическим ключом для серверной авторизации, устройств и DM. Нативный Arweave SAWD-v1 wallet теперь детерминированно выводится из активного `blockchain.key`, как и Solana-wallet.
При fork Solana-адрес меняется вместе с blockchain key. Перенос SOL со старого blockchain-wallet на новый является отдельным необязательным этапом ротации; в текущей первой реализации этот этап оставлен явной заглушкой.
При fork адреса кошельков меняются вместе с blockchain key. В UI ротации реализован отдельный этап переноса доступного SOL со старого blockchain-wallet на новый за вычетом комиссии сети. Перенос AR/Turbo при ротации пока отмечен отдельным TODO.
## 3.1. Хранение приватных ключей в текущем UI
После обычного входа или подключения нового доверенного устройства UI автоматически сохраняет в зашифрованном локальном контейнере два рабочих приватных ключа:
- `blockchain.key`;
- `client.key`.
Приватный `root.key` (Recovery key) **никогда не сохраняется** в IndexedDB/localStorage и **никогда не передаётся** при pairing/QR. Для операций, которым он нужен по текущему Solana-протоколу, root временно выводится из введённого пользователем пароля и существует только в оперативной памяти процесса. Публичный root key, записанный в PDA, этим правилом не затрагивается.
Ограниченный режим устройства архитектурно возможен благодаря разделению ключей, но в текущем релизе не включён: обычное авторизованное устройство всегда получает и хранит оба рабочих ключа.
---
+20 -30
View File
@@ -8,16 +8,16 @@
В SHiNE у пользователя есть несколько уровней ключей:
- `root key` - холодный recovery-ключ: при полной ротации дополнительно разрешает замену root + client + blockchain fork. Это не кошелёк.
- `blockchain key` - ключ записи в персональный SHiNE-блокчейн и текущий Solana-wallet/fee payer пользователя.
- `client key` - общий ключ пользовательских устройств для повседневной работы, звонков и DM; Solana-wallet им больше не является.
- `root key` / **ключ восстановления** - recovery-authority. Его приватная часть не хранится на устройствах и временно выводится из пароля только для операций, где нужна по текущему протоколу. Это не кошелёк.
- `blockchain key` / **ключ блокчейна** - постоянно хранимый рабочий ключ записи в персональный SHiNE-блокчейн и канонический источник пользовательских кошельков (Solana, Arweave SAWD-v1, Turbo через Solana).
- `client key` / **ключ доступа** - постоянно хранимый рабочий ключ для входа на сервер, устройства, звонков и DM. Кошельки из него не выводятся.
- `session key` - ключ конкретной сессии или конкретного устройства для авторизации на сервере.
Главная идея: самые важные ключи можно держать на доверенном серверном или аппаратном устройстве, а обычные клиентские устройства получают только ключи, нужные для текущей работы.
В текущем релизе обычное авторизованное устройство автоматически хранит два рабочих ключа: `blockchain key` и `client key`. Ограниченного режима пока нет. Приватный `root key` не сохраняется и не передаётся между устройствами.
## `root key`
`root key` - главный ключ пользователя.
`root key` - ключ восстановления пользователя. Его приватная часть не является постоянно сохранённым ключом устройства.
Назначение:
@@ -27,7 +27,7 @@
Обычные PDA-update **не требуют root key**: их выполняет активный blockchain key. При полной ротации старый root подписывает тот же hash нового unsigned PDA state, который подписывает новый blockchain key. Так root разрешает переход, не становясь повседневным ключом.
Важно не путать recovery-authority и кошелёк: `root key` не является fee payer. Текущий Solana-wallet/fee payer — активный `blockchain key`. Подробнее — `docs/Keys/DERIVATION.md`, §3.
Важно не путать recovery-authority и кошелёк: `root key` не является fee payer. Его публичная часть хранится в PDA, а приватная часть в текущем UI выводится из пароля только на время защищённой операции и затем не сохраняется. Текущий Solana-wallet/fee payer — активный `blockchain key`. Подробнее — `docs/Keys/DERIVATION.md`, §3.
## `blockchain key`
@@ -38,7 +38,9 @@
- подпись записей в персональном блокчейне пользователя;
- подтверждение действий, которые должны попасть в SHiNE-блокчейн;
- обычные обновления пользовательской PDA;
- текущий Solana-wallet/fee payer (`base58(active blockchain public key)`).
- текущий Solana-wallet/fee payer (`base58(active blockchain public key)`);
- источник штатного Arweave SAWD-v1 wallet;
- ключ Solana-кошелька, которым пополняется Turbo.
У пользователя может быть несколько персональных блокчейнов или веток. При смене `blockchain key` фактически создаётся новая ветка записи:
@@ -58,15 +60,9 @@
- повседневные входящие и исходящие личные сообщения;
- звонки и связанные с ними сообщения;
- self-messages, то есть внутренние сообщения пользователя самому себе;
- derivation Arweave-кошелька;
- оплата или подготовка добавления данных в Arweave-кошелек по отдельному протоколу.
- self-messages, то есть внутренние сообщения пользователя самому себе.
Arweave-кошелёк должен выводиться из `client key` по протоколу:
- `docs/Протоколы/SHINE_ARWEAVE_DERIVATION_V1.md`
Если пользователь теряет только `client key`, в худшем случае ломается повседневная переписка и доступ конкретных устройств к ежедневным операциям. `root key` и `blockchain key` при правильной архитектуре остаются отдельно защищёнными.
Штатные кошельки из `client key` не выводятся. Arweave SAWD-v1 использует `blockchain key`: `docs/Протоколы/SHINE_ARWEAVE_DERIVATION_V1.md`.
## `session key`
@@ -82,7 +78,7 @@ Arweave-кошелёк должен выводиться из `client key` по
- авторизация сессии на сервере;
- привязка устройства к пользователю;
- подтверждение запросов от конкретной сессии;
- доступ к зашифрованному `client key` после успешной авторизации.
- доступ к локально зашифрованным рабочим ключам (`client key` и `blockchain key`) после успешной авторизации.
Одна и та же сессия может быть пригодна для подключения к нескольким серверам пользователя, если архитектура конкретного пользователя это допускает.
@@ -102,22 +98,16 @@ Arweave-кошелёк должен выводиться из `client key` по
- обычная пользовательская сессия;
- серверная сессия;
- аппаратная или доверенная сессия с доступом к расширенным ключам.
- аппаратная или доверенная сессия для будущих специализированных сценариев.
Обычное устройство обычно имеет:
Обычное авторизованное устройство в текущем релизе имеет:
- собственный `session key`;
- зашифрованный `client key`, который открывается после авторизации;
- доступ к DM, звонкам и обычным пользовательским операциям.
- зашифрованный `client key`;
- зашифрованный `blockchain key`;
- доступ к DM, блокчейн-действиям и пользовательским кошелькам.
Доверенное серверное или аппаратное устройство может иметь:
- `root key`;
- `blockchain key`;
- `client key`;
- собственный `session key`.
Такая сессия может подписывать операции повышенной важности по запросам пользователя.
Приватный `root key` не хранится даже на обычном доверенном устройстве и не передаётся при подключении другого устройства. Будущий аппаратный режим может расширить эту модель отдельно, но текущий UI такого режима не включает.
## Внутренние self-messages
@@ -126,7 +116,7 @@ Self-message - это сообщение пользователя самому
Такие сообщения нужны, чтобы обычное устройство могло попросить доверенное устройство выполнить действие:
- подписать запись `blockchain key` и передать её в SHiNE-блокчейн;
- подписать изменение настройки через `root key`;
- инициировать защищённую настройку, после чего Recovery key при необходимости должен быть временно выведен из пароля на устройстве пользователя;
- обновить ключи;
- сохранить внутреннюю команду или настройку;
- отправить сообщение другому пользователю с сохранением копии себе;
@@ -163,7 +153,7 @@ Self-message - это сообщение пользователя самому
- `docs/Blockchain/README.md` - точка входа по форматам SHiNE-блокчейна.
- `docs/Solana_Architecture/README.md` - архитектура Solana-программ, PDA-счетов, DAO и движения средств.
- `docs/Инициализация_Solana_регистрации/README.md` - деплой и первичная инициализация Solana-регистрации.
- `docs/Протоколы/SHINE_ARWEAVE_DERIVATION_V1.md` - derivation Arweave-кошелька из `client key`.
- `docs/Протоколы/SHINE_ARWEAVE_DERIVATION_V1.md` - derivation Arweave-кошелька из `blockchain key`.
## Что нужно уточнить перед реализацией
@@ -3,11 +3,11 @@
Сокращение: **SAWD-v1**.
## Назначение
Из 32-байтного `clientKey32` пользователя получить один и тот же нативный Arweave RSA-4096 JWK wallet и один и тот же Arweave address.
Из 32-байтного приватного seed активного `blockchainKey32` пользователя получить один и тот же нативный Arweave RSA-4096 JWK wallet и один и тот же Arweave address.
## Вход
- `clientKey32`: ровно 32 байта.
- Если исходный `client.key` хранится как Ed25519 PKCS8 base64, нужно извлечь последние 32 байта из PKCS8.
- `blockchainKey32`: ровно 32 байта.
- Если исходный `blockchain.key` хранится как Ed25519 PKCS8 base64, нужно извлечь последние 32 байта из PKCS8.
- Если используется Solana keypair JSON на 64 байта, используются только `bytes[0..31]`.
## Выход
@@ -46,8 +46,8 @@
- `SMALL_PRIME_LIMIT = 10000`
## Алгоритм
1. Проверить `clientKey32.length == 32`.
2. `masterSeed32 = HMAC-SHA256(key = UTF8(MASTER_LABEL), message = clientKey32)`.
1. Проверить `blockchainKey32.length == 32`.
2. `masterSeed32 = HMAC-SHA256(key = UTF8(MASTER_LABEL), message = blockchainKey32)`.
3. Реализовать `deriveBytes(label, length)`:
- `output = empty`
- `counter = 0`
@@ -71,9 +71,9 @@
- `baseBytes = HMAC-SHA256(key = masterSeed32, message = UTF8(MR_LABEL) || UTF8("/") || UTF8(label) || UTF8("/") || uint64_be(index) || UTF8("/") || uint32_be(round))`
- `a = 2 + (unsigned_big_endian_integer(baseBytes) mod (candidate - 3))`
6. `p = derivePrime("p")`, `q = derivePrime("q")`.
7. Если `p == q`, продолжить поиск `q`.
7. Продолжать детерминированный поиск `q` (`q/index+1`, `q/index+2`, ...), если `p == q` **или** `bitLength(p * q) != 4096`. Это гарантирует настоящий 4096-битный RSA modulus, а не допустимый математически, но неподходящий для стандарта 4095-битный product двух 2048-битных простых.
8. Если `p > q`, поменять местами. В SAWD-v1 всегда `p < q`.
9. `n = p * q`
9. `n = p * q`; обязательно `bitLength(n) == 4096`
10. `e = 65537`
11. `lambda = lcm(p - 1, q - 1)`
12. `d = modular_inverse(e, lambda)`
@@ -101,4 +101,4 @@
## Версионирование стандарта
Если меняется любая константа или шаг алгоритма — это уже **SAWD-v2**.
Пользователи, созданные на SAWD-v1, должны продолжать восстанавливаться через SAWD-v1.
До публичного запуска legacy-совместимость не требуется. После запуска источник и алгоритм SAWD-v1 считаются частью стабильного формата: изменение источника ключа или любой константы потребует новой версии стандарта.