SHA256
Миграция PDA на client.key
This commit is contained in:
@@ -35,7 +35,7 @@
|
||||
"blockchainName": "anya-001",
|
||||
"solanaKey": "BASE64_32_PUBLIC_KEY",
|
||||
"blockchainKey": "BASE64_32_PUBLIC_KEY",
|
||||
"deviceKey": "BASE64_32_PUBLIC_KEY",
|
||||
"clientKey": "BASE64_32_PUBLIC_KEY",
|
||||
"bchLimit": 1000000
|
||||
}
|
||||
}
|
||||
@@ -99,7 +99,7 @@
|
||||
"blockchainName": "anya-001",
|
||||
"solanaKey": "BASE64_32_PUBLIC_KEY",
|
||||
"blockchainKey": "BASE64_32_PUBLIC_KEY",
|
||||
"deviceKey": "BASE64_32_PUBLIC_KEY",
|
||||
"clientKey": "BASE64_32_PUBLIC_KEY",
|
||||
"serverLastGlobalNumber": 128,
|
||||
"serverLastGlobalHash": "4f...ab",
|
||||
"serverBlockchainSizeBytes": 45212,
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
Логика раздела такая:
|
||||
|
||||
- сначала клиент либо начинает создание новой сессии через `deviceKey`;
|
||||
- сначала клиент либо начинает создание новой сессии через `clientKey`;
|
||||
- либо начинает вход в уже созданную сессию через `sessionKey`;
|
||||
- сервер на первом шаге выдаёт challenge/nonce;
|
||||
- на втором шаге клиент присылает подписанный ответ;
|
||||
@@ -55,7 +55,7 @@
|
||||
2. Вход в существующую сессию:
|
||||
`SessionChallenge` -> `SessionLogin`
|
||||
|
||||
`deviceKey` используется для создания новой сессии.
|
||||
`clientKey` используется для создания новой сессии.
|
||||
|
||||
`sessionKey` используется для входа в уже созданную сессию.
|
||||
|
||||
@@ -119,7 +119,7 @@ ed25519/BASE64_PUBLIC_KEY
|
||||
"storagePwd": "BASE64_OR_APP_SPECIFIC_SECRET",
|
||||
"timeMs": 1774600000123,
|
||||
"authNonce": "nonce",
|
||||
"deviceKey": "BASE64_DEVICE_PUBLIC_KEY",
|
||||
"clientKey": "BASE64_DEVICE_PUBLIC_KEY",
|
||||
"signatureB64": "BASE64_SIGNATURE",
|
||||
"sessionType": 1,
|
||||
"clientPlatform": "Web",
|
||||
@@ -138,15 +138,15 @@ AUTH_CREATE_SESSION:{login}:{sessionKey}:{storagePwd}:{timeMs}:{authNonce}
|
||||
|
||||
Перед проверкой подписи сервер должен:
|
||||
|
||||
1. взять актуальный `solana_users.device_key`;
|
||||
2. сравнить его с `payload.deviceKey`;
|
||||
1. взять актуальный `solana_users.client_key`;
|
||||
2. сравнить его с `payload.clientKey`;
|
||||
3. только потом проверять подпись.
|
||||
|
||||
Если ключ не совпадает, сервер возвращает ошибку `DEVICE_KEY_NOT_ACTUAL`.
|
||||
Если `clientKey` не совпадает, сервер возвращает ошибку `DEVICE_KEY_NOT_ACTUAL`.
|
||||
|
||||
На будущее:
|
||||
|
||||
- для ротации `device_key` желательно добавить перепроверку через Solana.
|
||||
- для ротации `client_key` желательно добавить перепроверку через Solana.
|
||||
|
||||
### Успешный ответ
|
||||
|
||||
@@ -172,15 +172,15 @@ AUTH_CREATE_SESSION:{login}:{sessionKey}:{storagePwd}:{timeMs}:{authNonce}
|
||||
- `501 / NO_LOGIN` — у пользователя на сервере не заполнен `login`.
|
||||
- `400 / EMPTY_STORAGE_PWD` — пустой `storagePwd`.
|
||||
- `400 / EMPTY_SESSION_KEY` — пустой `sessionKey`.
|
||||
- `422 / UNSUPPORTED_KEY_ALGORITHM` — префикс алгоритма в `sessionKey` или `deviceKey` не поддерживается текущим сервером.
|
||||
- `400 / BAD_BASE64` — неверный Base64 в `sessionKey`, `deviceKey` или `signatureB64`.
|
||||
- `422 / UNSUPPORTED_KEY_ALGORITHM` — префикс алгоритма в `sessionKey` или `clientKey` не поддерживается текущим сервером.
|
||||
- `400 / BAD_BASE64` — неверный Base64 в `sessionKey`, `clientKey` или `signatureB64`.
|
||||
- `400 / EMPTY_SIGNATURE` — пустая подпись.
|
||||
- `400 / TIME_SKEW` — время клиента отличается от серверного больше допустимого окна.
|
||||
- `400 / NO_DEVICE_KEY` — у пользователя в БД отсутствует `deviceKey`.
|
||||
- `400 / NO_DEVICE_KEY` — у пользователя в БД отсутствует `clientKey`.
|
||||
- `400 / EMPTY_AUTH_NONCE` — пустой `authNonce`.
|
||||
- `400 / AUTH_NONCE_MISMATCH` — `authNonce` не соответствует значению из `AuthChallenge`.
|
||||
- `400 / EMPTY_DEVICE_KEY` — в запросе не передан `deviceKey`.
|
||||
- `422 / DEVICE_KEY_NOT_ACTUAL` — `deviceKey` не совпадает с актуальной версией на сервере.
|
||||
- `400 / EMPTY_DEVICE_KEY` — в запросе не передан `clientKey`.
|
||||
- `422 / DEVICE_KEY_NOT_ACTUAL` — `clientKey` не совпадает с актуальной версией на сервере.
|
||||
- `422 / BAD_SIGNATURE` — подпись не прошла проверку.
|
||||
- `460 / SESSION_TYPE_MISMATCH` — `sessionType` не совпадает с типом сессии, уже опубликованным для этого `sessionKey` в Solana PDA.
|
||||
- `501 / SESSION_TYPE_PDA_CHECK_FAILED` — сервер не смог проверить `sessionType` по Solana PDA.
|
||||
@@ -314,7 +314,7 @@ SESSION_LOGIN:{sessionId}:{timeMs}:{nonce}
|
||||
|
||||
В этом потоке:
|
||||
|
||||
- новое устройство не владеет `deviceKey` и не проходит обычный `CreateAuthSession`;
|
||||
- новое устройство не владеет `clientKey` и не проходит обычный `CreateAuthSession`;
|
||||
- пароль проверяется сервером только как фильтр;
|
||||
- решение об одобрении принимает уже авторизованная доверенная сессия пользователя;
|
||||
- сервер не расшифровывает `encryptedPayload` и не становится источником приватных ключей.
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"param": "display_name",
|
||||
"time_ms": 1774700000123,
|
||||
"value": "Alice",
|
||||
"device_key": "BASE64_DEVICE_PUBLIC_KEY",
|
||||
"client_key": "BASE64_DEVICE_PUBLIC_KEY",
|
||||
"signature": "BASE64_SIGNATURE"
|
||||
}
|
||||
}
|
||||
@@ -76,7 +76,7 @@
|
||||
"param": "display_name",
|
||||
"time_ms": 1774700000123,
|
||||
"value": "Alice",
|
||||
"device_key": "BASE64_DEVICE_PUBLIC_KEY",
|
||||
"client_key": "BASE64_DEVICE_PUBLIC_KEY",
|
||||
"signature": "BASE64_SIGNATURE"
|
||||
}
|
||||
}
|
||||
@@ -116,7 +116,7 @@
|
||||
"param": "display_name",
|
||||
"time_ms": 1774700000123,
|
||||
"value": "Alice",
|
||||
"device_key": "BASE64_DEVICE_PUBLIC_KEY",
|
||||
"client_key": "BASE64_DEVICE_PUBLIC_KEY",
|
||||
"signature": "BASE64_SIGNATURE"
|
||||
}
|
||||
]
|
||||
@@ -126,4 +126,4 @@
|
||||
|
||||
## Примечание
|
||||
|
||||
Имена JSON-полей `time_ms` и `device_key` сейчас соответствуют Java-модели ответа/запроса и должны передаваться именно в таком виде.
|
||||
Имена JSON-полей `time_ms` и `client_key` сейчас соответствуют Java-модели ответа/запроса и должны передаваться именно в таком виде.
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ ESP32 становится аппаратным HSM (hardware security module):
|
||||
### ESP32 (основная работа)
|
||||
- [ ] Инициализация WiFi (SSID/пароль в NVS)
|
||||
- [ ] WebSocket-клиент (`WebSocketsClient`) — постоянное соединение с сервером
|
||||
- [ ] Авторизация на сервере: `AuthChallenge` → `CreateAuthSession` через `deviceKey` (уже есть в NVS), сохранить `sessionId` в NVS
|
||||
- [ ] Авторизация на сервере: `AuthChallenge` → `CreateAuthSession` через `clientKey` (уже есть в NVS), сохранить `sessionId` в NVS
|
||||
- [ ] Обработчик входящих WebSocket-событий: JSON-парсинг, диспетчер по типу
|
||||
- [ ] Новые UI-экраны: «Разрешить сессию?» и «Подписать?» с кнопками Да/Нет
|
||||
- [ ] Расширенное хранилище ключей в NVS (произвольные именованные ключи сверх базовых трёх)
|
||||
|
||||
@@ -56,7 +56,7 @@ seed(32) = SHA-256(material)
|
||||
|------|---------|---------------------|
|
||||
| root | `root.key` | Личность. Подписывает unsigned-часть PDA-записи (`RootKeyBlock`). |
|
||||
| blockchain | `bch.key` | Подписывает `LastBlockState` персонального блокчейна (`blockchain_public_key`). |
|
||||
| device / **Solana** | `dev.key` | Ключ устройства = Solana-ключ. Fee payer и подпись Solana-транзакций; адрес кошелька = `base58(devicePub)`. См. §3. |
|
||||
| device / **Solana** | `client.key` | Ключ устройства = Solana-ключ. Fee payer и подпись Solana-транзакций; адрес кошелька = `base58(clientPub)`. См. §3. |
|
||||
| homeserver | `homeserver.key:<имя>` | Ключ homeserver-устройства, по одному на каждый homeserver (различитель — имя). См. §4. |
|
||||
|
||||
Полные роли каждого ключа — в `Dev_Docs/Keys/README.md`.
|
||||
@@ -67,16 +67,16 @@ seed(32) = SHA-256(material)
|
||||
|
||||
Отдельного «солана-ключа» нет. На Solana работают два ключа:
|
||||
|
||||
- **`dev.key` (device) — пополняемый кошелёк и fee payer.** Solana-адрес = `base58(devicePub)`.
|
||||
- **`client.key` (device) — пополняемый кошелёк и fee payer.** Solana-адрес = `base58(clientPub)`.
|
||||
Этим ключом оплачиваются и подписываются `create_user_pda` / `update_user_pda`.
|
||||
Пополнять SOL нужно именно на этот адрес.
|
||||
- **`root.key` — авторитет записи**, подписывает unsigned-часть PDA через Ed25519-инструкцию, но **не** является fee payer.
|
||||
|
||||
Соответствует формату PDA `shine-solana/shine/doc/formats/shine-user-pda-format-v.1.0.md` §2.1
|
||||
(«create/update оплачиваются с `device_key`», «root_key — не fee payer»).
|
||||
(«create/update оплачиваются с `client_key`», «root_key — не fee payer»).
|
||||
|
||||
Кратко про роли на Solana: `root.key` — это **главный (master) ключ**: им управляют PDA-записью
|
||||
(`create/update`) и через это можно заменить все остальные ключи; `dev.key` — это **пополняемый
|
||||
(`create/update`) и через это можно заменить все остальные ключи; `client.key` — это **пополняемый
|
||||
кошелёк и плательщик комиссий**. Полное описание ролей — `Dev_Docs/Keys/README.md`.
|
||||
|
||||
---
|
||||
@@ -118,9 +118,9 @@ homeserver.key:home-b -> ключ B
|
||||
- `shine-UI/server-ui/js/server-ui-shared.js` — те же root/bch/dev для серверного UI (~147–160).
|
||||
|
||||
### Solana-ключ / адрес кошелька (UI)
|
||||
- `shine-UI/js/pages/registration-payment-view.js` — `deriveUserWalletAddress`: адрес = `base58(devicePub)` (~113).
|
||||
- `shine-UI/js/pages/topup-view.js` — `deviceWalletAddressFromBundle`: тот же канонический адрес из `preGeneratedKeyBundle.devicePair`.
|
||||
Прежний расходящийся путь `deriveWalletFromPassword` (прямой Argon2 по `dev.key`, мимо `masterSecret`) удалён.
|
||||
- `shine-UI/js/pages/registration-payment-view.js` — `deriveUserWalletAddress`: адрес = `base58(clientPub)` (~113).
|
||||
- `shine-UI/js/pages/topup-view.js` — `clientWalletAddressFromBundle`: тот же канонический адрес из `preGeneratedKeyBundle.clientPair`.
|
||||
Прежний расходящийся путь `deriveWalletFromPassword` (прямой Argon2 по `client.key`, мимо `masterSecret`) удалён.
|
||||
|
||||
### Деривация ключей (прошивка ESP32)
|
||||
- `ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/main-device/shine_homeserver_main/shine_homeserver_main.ino`
|
||||
@@ -131,7 +131,7 @@ homeserver.key:home-b -> ключ B
|
||||
|
||||
### Формат PDA (куда попадают ключи)
|
||||
- `shine-solana/shine/doc/formats/shine-user-pda-format-v.1.0.md`
|
||||
— `RootKeyBlock` §6, `DeviceKeyBlock` §7, `blockchain_public_key` §9, `SessionsBlock`/`session_type=100` §13, оплата §2.1.
|
||||
— `RootKeyBlock` §6, `ClientKeyBlock` §7, `blockchain_public_key` §9, `SessionsBlock`/`session_type=100` §13, оплата §2.1.
|
||||
|
||||
### Сервер (тестовый seed)
|
||||
- `SHiNE-server/src/test/java/test/it/cases/SeedDataPopulationHelper.java` `deriveKeysFromPassword` (~246) —
|
||||
|
||||
+14
-14
@@ -8,9 +8,9 @@
|
||||
|
||||
В SHiNE у пользователя есть несколько уровней ключей:
|
||||
|
||||
- `root key` - главный (master) ключ пользователя: тот, кто им владеет, управляет пользовательской PDA в Solana и может заменить все остальные ключи. Это не пополняемый кошелёк (комиссии платит `device key`).
|
||||
- `root key` - главный (master) ключ пользователя: тот, кто им владеет, управляет пользовательской PDA в Solana и может заменить все остальные ключи. Это не пополняемый кошелёк (комиссии платит `client key`).
|
||||
- `blockchain key` - ключ записи в персональный SHiNE-блокчейн пользователя.
|
||||
- `device key` - общий ключ пользовательских устройств для повседневной работы, звонков, DM и мелких платежей.
|
||||
- `client key` - общий ключ пользовательских устройств для повседневной работы, звонков, DM и мелких платежей.
|
||||
- `session key` - ключ конкретной сессии или конкретного устройства для авторизации на сервере.
|
||||
|
||||
Главная идея: самые важные ключи можно держать на доверенном серверном или аппаратном устройстве, а обычные клиентские устройства получают только ключи, нужные для текущей работы.
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
`root key` — это **главный (master) ключ** в следующем смысле: зная `root key`, можно управлять пользовательской PDA-записью в Solana (`create_user_pda` / `update_user_pda`) и тем самым **заменить все остальные ключи** пользователя (device, blockchain, homeserver). Поэтому компрометация `root key` равносильна компрометации всей личности пользователя.
|
||||
|
||||
Важно не путать авторитет и кошелёк: `root key` — это авторитет над PDA-записью, а **SOL-комиссии за create/update платит `device key`** (он же fee payer и адрес для пополнения). Подробнее о том, какой ключ за что отвечает на Solana, — в `Dev_Docs/Keys/DERIVATION.md`, §3.
|
||||
Важно не путать авторитет и кошелёк: `root key` — это авторитет над PDA-записью, а **SOL-комиссии за create/update платит `client key`** (он же fee payer и адрес для пополнения). Подробнее о том, какой ключ за что отвечает на Solana, — в `Dev_Docs/Keys/DERIVATION.md`, §3.
|
||||
|
||||
## `blockchain key`
|
||||
|
||||
@@ -50,9 +50,9 @@
|
||||
|
||||
Рабочая логика по умолчанию должна использовать последнюю актуальную ветку. Старые ветки остаются читаемыми и показывают историю смены ключей.
|
||||
|
||||
## `device key`
|
||||
## `client key`
|
||||
|
||||
`device key` - общий ключ, который знают доверенные устройства пользователя.
|
||||
`client key` - общий ключ, который знают доверенные устройства пользователя.
|
||||
|
||||
Назначение:
|
||||
|
||||
@@ -63,11 +63,11 @@
|
||||
- derivation Arweave-кошелька;
|
||||
- оплата или подготовка добавления данных в Arweave-кошелек по отдельному протоколу.
|
||||
|
||||
Arweave-кошелёк должен выводиться из `device key` по протоколу:
|
||||
Arweave-кошелёк должен выводиться из `client key` по протоколу:
|
||||
|
||||
- `Dev_Docs/Протоколы/SHINE_ARWEAVE_DERIVATION_V1.md`
|
||||
|
||||
Если пользователь теряет только `device key`, в худшем случае ломается повседневная переписка и доступ конкретных устройств к ежедневным операциям. `root key` и `blockchain key` при правильной архитектуре остаются отдельно защищёнными.
|
||||
Если пользователь теряет только `client key`, в худшем случае ломается повседневная переписка и доступ конкретных устройств к ежедневным операциям. `root key` и `blockchain key` при правильной архитектуре остаются отдельно защищёнными.
|
||||
|
||||
## `session key`
|
||||
|
||||
@@ -83,7 +83,7 @@ Arweave-кошелёк должен выводиться из `device key` по
|
||||
- авторизация сессии на сервере;
|
||||
- привязка устройства к пользователю;
|
||||
- подтверждение запросов от конкретной сессии;
|
||||
- доступ к зашифрованному `device key` после успешной авторизации.
|
||||
- доступ к зашифрованному `client key` после успешной авторизации.
|
||||
|
||||
Одна и та же сессия может быть пригодна для подключения к нескольким серверам пользователя, если архитектура конкретного пользователя это допускает.
|
||||
|
||||
@@ -108,14 +108,14 @@ Arweave-кошелёк должен выводиться из `device key` по
|
||||
Обычное устройство обычно имеет:
|
||||
|
||||
- собственный `session key`;
|
||||
- зашифрованный `device key`, который открывается после авторизации;
|
||||
- зашифрованный `client key`, который открывается после авторизации;
|
||||
- доступ к DM, звонкам и обычным пользовательским операциям.
|
||||
|
||||
Доверенное серверное или аппаратное устройство может иметь:
|
||||
|
||||
- `root key`;
|
||||
- `blockchain key`;
|
||||
- `device key`;
|
||||
- `client key`;
|
||||
- собственный `session key`.
|
||||
|
||||
Такая сессия может подписывать операции повышенной важности по запросам пользователя.
|
||||
@@ -139,7 +139,7 @@ Self-message - это сообщение пользователя самому
|
||||
|
||||
Входящее сообщение может быть зашифровано:
|
||||
|
||||
- `device key`;
|
||||
- `client key`;
|
||||
- `session key`;
|
||||
- отдельным ключом конкретного чата;
|
||||
- другим ключом, который уже известен клиенту.
|
||||
@@ -158,12 +158,12 @@ Self-message - это сообщение пользователя самому
|
||||
|
||||
## Связанные документы
|
||||
|
||||
- `Dev_Docs/Keys/DERIVATION.md` - **источник истины по конкретной деривации** секрета и ключей (формулы Argon2id, `base64|suffix→SHA-256→Ed25519`, суффиксы `root.key`/`bch.key`/`dev.key`/`homeserver.key:<имя>`, Solana-ключ, ссылки на код).
|
||||
- `Dev_Docs/Keys/DERIVATION.md` - **источник истины по конкретной деривации** секрета и ключей (формулы Argon2id, `base64|suffix→SHA-256→Ed25519`, суффиксы `root.key`/`bch.key`/`client.key`/`homeserver.key:<имя>`, Solana-ключ, ссылки на код).
|
||||
- `Dev_Docs/Personal_Messages/README.md` - текущая документация личных сообщений.
|
||||
- `Dev_Docs/Blockchain/README.md` - точка входа по форматам SHiNE-блокчейна.
|
||||
- `Dev_Docs/Solana_Architecture/README.md` - архитектура Solana-программ, PDA-счетов, DAO и движения средств.
|
||||
- `Dev_Docs/Инициализация_Solana_регистрации/README.md` - деплой и первичная инициализация Solana-регистрации.
|
||||
- `Dev_Docs/Протоколы/SHINE_ARWEAVE_DERIVATION_V1.md` - derivation Arweave-кошелька из `device key`.
|
||||
- `Dev_Docs/Протоколы/SHINE_ARWEAVE_DERIVATION_V1.md` - derivation Arweave-кошелька из `client key`.
|
||||
|
||||
## Что нужно уточнить перед реализацией
|
||||
|
||||
@@ -172,5 +172,5 @@ Self-message - это сообщение пользователя самому
|
||||
- какие операции требуют `root key`, а какие достаточно подписывать `blockchain key`;
|
||||
- формат self-message-команд;
|
||||
- порядок перебора ключей при расшифровке входящих сообщений;
|
||||
- правила ротации `device key` и восстановления доступа после потери устройства;
|
||||
- правила ротации `client key` и восстановления доступа после потери устройства;
|
||||
- какие типы серверных и аппаратных сессий нужны в первой реализации.
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
- на ESP32 после успешной homeserver-авторизации в `SETTINGS` появляется пункт `Device requests` первым;
|
||||
- `REFRESH` реально загружает активные заявки;
|
||||
- на экране видно две плитки, список листается вертикально;
|
||||
- client-session заявка после `YES` подключается с передачей только `device key`;
|
||||
- client-session заявка после `YES` подключается с передачей только `client key`;
|
||||
- wallet-session заявка после `YES` подключается без передачи ключей, через выпуск отдельной wallet-session;
|
||||
- `NO` отклоняет заявку и она исчезает из списка активных.
|
||||
|
||||
|
||||
+3
-3
@@ -2,20 +2,20 @@
|
||||
|
||||
- краткое описание:
|
||||
- в ESP32 заменён домашний блок `баланс + QR` на единый вход в экран кошелька;
|
||||
- добавлен выбор активного кошелька `DeviceKey / RootKey / Custom`;
|
||||
- добавлен выбор активного кошелька `ClientKey / RootKey / Custom`;
|
||||
- для browser extension добавлен первый RPC `get_wallet_public_key` через существующий `wallet-session` и `CallSignalToSession`;
|
||||
- в popup расширения добавлен запрос текущего кошелька и копирование `publicKeyBase58`.
|
||||
|
||||
- что проверять:
|
||||
- на ESP32 после ввода секрета на главном экране видна кнопка `Кошелёк: ...`;
|
||||
- экран `WALLET` открывается и показывает текущий тип кошелька;
|
||||
- экран `WALLET_SELECT` переключает `DeviceKey`, `RootKey` и `Custom`;
|
||||
- экран `WALLET_SELECT` переключает `ClientKey`, `RootKey` и `Custom`;
|
||||
- для `Custom` открывается ввод имени и после сохранения derivation работает;
|
||||
- `Показать баланс кошелька` читает баланс именно активного кошелька;
|
||||
- `Показать QR-код кошелька` показывает QR и адрес именно активного кошелька;
|
||||
- browser extension после подключения wallet-session может запросить текущий кошелёк у ESP32;
|
||||
- extension показывает тип кошелька, полный `publicKeyBase58`, результат проверки через PDA и копирует ключ в буфер;
|
||||
- для `dev.key` и `root.key` проверка через PDA даёт ожидаемое совпадение.
|
||||
- для `client.key` и `root.key` проверка через PDA даёт ожидаемое совпадение.
|
||||
|
||||
- ожидаемый результат:
|
||||
- активный кошелёк на ESP32 реально влияет на баланс, QR и ответ `get_wallet_public_key`;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
- Что проверять:
|
||||
1. На ESP32 открыть `HOME`, `WALLET`, `SELECT WALLET`, `WALLET QR` и убедиться, что пользовательские строки на экране только на английском.
|
||||
2. Проверить сценарий выбора `DeviceKey`, `RootKey`, `Custom` и чтение баланса/QR после переключения.
|
||||
2. Проверить сценарий выбора `ClientKey`, `RootKey`, `Custom` и чтение баланса/QR после переключения.
|
||||
3. В расширении открыть popup, запустить `Подключить` для логина, у которого ещё не настраивался trusted-device login.
|
||||
4. Убедиться, что `StartTrustedDeviceLogin` больше не падает с `NullPointerException`.
|
||||
5. После создания pairing-запроса проверить, что homeserver/UI может его увидеть и обработать как раньше.
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@
|
||||
6. Проверить оба варианта:
|
||||
- `APPROVE` возвращает сайту подписанную транзакцию;
|
||||
- `REJECT` возвращает отказ.
|
||||
7. Проверить сценарии для `DeviceKey`, `RootKey`, `Custom`.
|
||||
7. Проверить сценарии для `ClientKey`, `RootKey`, `Custom`.
|
||||
|
||||
- Ожидаемый результат:
|
||||
- сайт может подключить кошелёк через provider расширения;
|
||||
|
||||
@@ -12,8 +12,9 @@
|
||||
|
||||
- логин пользователя;
|
||||
- неизменяемые параметры создания записи;
|
||||
- публичный recovery-ключ пользователя;
|
||||
- корневой публичный ключ пользователя;
|
||||
- ключ устройства;
|
||||
- клиентский публичный ключ пользователя;
|
||||
- данные одного или нескольких пользовательских блокчейнов SHiNE;
|
||||
- серверные данные пользователя, если пользователь выступает сервером;
|
||||
- серверы доступа пользователя;
|
||||
@@ -34,9 +35,9 @@
|
||||
|
||||
## 2.1. Кто оплачивает create/update PDA
|
||||
|
||||
- Инструкции `create_user_pda` и `update_user_pda` оплачиваются с `device_key`.
|
||||
- Инструкции `create_user_pda` и `update_user_pda` оплачиваются с `client_key`.
|
||||
- `root_key` используется для подписи unsigned части записи через Ed25519 instruction и не является fee payer.
|
||||
- Для server PDA это правило то же самое: пополнять SOL нужно на адрес `device_key`.
|
||||
- Для server PDA это правило то же самое: пополнять SOL нужно на адрес `client_key`.
|
||||
|
||||
## 3. Общие правила кодирования
|
||||
|
||||
@@ -85,8 +86,9 @@ UserPdaRecordV1
|
||||
|
||||
| block_type | Блок | Назначение |
|
||||
|------------|------|------------|
|
||||
| `0` | `RecoveryKeyBlock` | Ключ восстановления пользователя. |
|
||||
| `1` | `RootKeyBlock` | Корневой ключ пользователя. |
|
||||
| `2` | `DeviceKeyBlock` | Ключ устройства пользователя. |
|
||||
| `2` | `ClientKeyBlock` | Клиентский ключ пользователя. |
|
||||
| `3` | `BlockchainRegistryBlock` | Один или несколько блокчейнов пользователя. |
|
||||
| `30` | `ServerProfileBlock` | Серверные данные пользователя. |
|
||||
| `40` | `AccessServersBlock` | Серверы доступа/relay. |
|
||||
@@ -97,13 +99,31 @@ UserPdaRecordV1
|
||||
Правила:
|
||||
|
||||
- неизвестный `block_type` в `format_major = 1` считается ошибкой;
|
||||
- обязательные блоки: `RootKeyBlock`, `DeviceKeyBlock`, `BlockchainRegistryBlock`;
|
||||
- обязательные блоки: `RecoveryKeyBlock`, `RootKeyBlock`, `ClientKeyBlock`, `BlockchainRegistryBlock`;
|
||||
- необязательные блоки: `ServerProfileBlock`, `AccessServersBlock`, `SessionsBlock`, `TrustedStateBlock`;
|
||||
- каждый обязательный блок должен встречаться ровно один раз;
|
||||
- порядок блоков в записи фиксируется для простоты проверки:
|
||||
`RootKey`, `DeviceKey`, `BlockchainRegistry`, `ServerProfile`, `AccessServers`, `Sessions`, `TrustedState`.
|
||||
`RecoveryKey`, `RootKey`, `ClientKey`, `BlockchainRegistry`, `ServerProfile`, `AccessServers`, `Sessions`, `TrustedState`.
|
||||
|
||||
## 6. RootKeyBlock
|
||||
## 6. RecoveryKeyBlock
|
||||
|
||||
Recovery-ключ нужен для будущих сценариев восстановления и ротации остальных ключей. В текущей версии он только публикуется в записи и не меняется через обычный `update_user_pda`.
|
||||
|
||||
```text
|
||||
RecoveryKeyBlock
|
||||
- block_type: u8 = 0
|
||||
- block_version: u8 = 0
|
||||
- recovery_key: [u8; 32]
|
||||
```
|
||||
|
||||
Правила:
|
||||
|
||||
- при создании задается публичный recovery-ключ пользователя;
|
||||
- при обновлении `recovery_key` должен совпадать с предыдущей записью;
|
||||
- приватный `recovery.key` в PDA не хранится;
|
||||
- отдельная ротация recovery-ключа будет отдельным форматом/сценарием в будущем.
|
||||
|
||||
## 7. RootKeyBlock
|
||||
|
||||
Смена `root_key` пока не проектируется и не реализуется. Блок фиксирует только стадию `0`.
|
||||
|
||||
@@ -120,24 +140,24 @@ RootKeyBlock
|
||||
- при обновлении `root_key` должен совпадать с предыдущей записью;
|
||||
- ротация root-key будет отдельным форматом/сценарием в будущем.
|
||||
|
||||
## 7. DeviceKeyBlock
|
||||
## 8. ClientKeyBlock
|
||||
|
||||
Смена `device_key` пока также не проектируется как отдельная ротация. В версии `0` хранится один ключ устройства.
|
||||
Смена `client_key` пока также не проектируется как отдельная ротация. В версии `0` хранится один клиентский ключ пользователя.
|
||||
|
||||
```text
|
||||
DeviceKeyBlock
|
||||
ClientKeyBlock
|
||||
- block_type: u8 = 2
|
||||
- block_version: u8 = 0
|
||||
- device_key: [u8; 32]
|
||||
- client_key: [u8; 32]
|
||||
```
|
||||
|
||||
Правила:
|
||||
|
||||
- при создании задается текущий публичный ключ устройства;
|
||||
- при обновлении ключ устройства может быть обновлен только если это отдельно разрешено бизнес-логикой инструкции;
|
||||
- история устройств и несколько устройств в этом формате не хранятся.
|
||||
- при создании задается текущий клиентский публичный ключ пользователя;
|
||||
- при обновлении `client_key` должен совпадать с предыдущей записью;
|
||||
- история устройств и несколько клиентских ключей в этом формате не хранятся.
|
||||
|
||||
## 8. BlockchainRegistryBlock
|
||||
## 9. BlockchainRegistryBlock
|
||||
|
||||
Блок хранит данные пользовательских блокчейнов SHiNE. Сейчас используется один блокчейн, но структура сразу сделана как список.
|
||||
|
||||
@@ -155,7 +175,7 @@ BlockchainRegistryBlock
|
||||
- в будущем можно увеличить количество записей без изменения смысла `BlockchainRecord`;
|
||||
- каждый `BlockchainRecord` описывает один пользовательский SHiNE-блокчейн.
|
||||
|
||||
## 9. BlockchainRecord
|
||||
## 10. BlockchainRecord
|
||||
|
||||
```text
|
||||
BlockchainRecord
|
||||
@@ -191,7 +211,7 @@ BlockchainRecord
|
||||
|
||||
Arweave `tx_id` - обычное поле внутри записи конкретного блокчейна. Solana-программа не проверяет, что такой Arweave transaction действительно существует и содержит корректные данные; это ответственность клиента/сервера/пользователя.
|
||||
|
||||
## 10. Правила обновления BlockchainRecord
|
||||
## 11. Правила обновления BlockchainRecord
|
||||
|
||||
При обновлении записи:
|
||||
|
||||
@@ -229,7 +249,7 @@ last_block_signature = Ed25519(blockchain_public_key, message)
|
||||
|
||||
Причина проверки подписи `LastBlockState`: `root_key` управляет Solana-записью пользователя, а `blockchain_public_key` подтверждает состояние конкретного пользовательского блокчейна. Подписывается не голый хэш, а связка логина, имени блокчейна, номера последнего блока, хэша последнего блока и занятого размера.
|
||||
|
||||
## 11. ServerProfileBlock
|
||||
## 12. ServerProfileBlock
|
||||
|
||||
Блок присутствует, если пользователь выступает сервером.
|
||||
|
||||
@@ -255,7 +275,7 @@ ServerProfileBlock
|
||||
- `server_address` - строковый адрес сервера в соответствии с `address_format_type`;
|
||||
- `sync_servers` - логины SHiNE-пользователей, зарегистрированных как серверы, с которыми этот сервер синхронизирует блокчейн и личные сообщения. Solana-программа не обязана проверять, что эти логины действительно зарегистрированы как серверы.
|
||||
|
||||
## 12. AccessServersBlock
|
||||
## 13. AccessServersBlock
|
||||
|
||||
Блок хранит серверы доступа/relay для пользователя.
|
||||
|
||||
@@ -274,7 +294,7 @@ AccessServersBlock
|
||||
- `access_servers` - логины пользователей системы, используемых как серверы доступа/relay. Solana-программа не обязана проверять, что эти логины действительно зарегистрированы как серверы;
|
||||
- точная семантика выбора сервера доступа определяется клиентской/серверной логикой SHiNE.
|
||||
|
||||
## 13. SessionsBlock
|
||||
## 14. SessionsBlock
|
||||
|
||||
Блок хранит опубликованные пользовательские сессии. На текущем этапе регистрация пользователя не добавляет туда записи автоматически, поэтому стандартный create/update продолжает работать с пустым списком.
|
||||
|
||||
@@ -309,6 +329,7 @@ SessionRecord
|
||||
| Значение | Смысл |
|
||||
|----------|-------|
|
||||
| `1` | Обычная пользовательская сессия. |
|
||||
| `50` | Кошелёк пользователя. |
|
||||
| `100` | Homeserver пользователя. |
|
||||
|
||||
Правила:
|
||||
@@ -320,7 +341,7 @@ SessionRecord
|
||||
- внутри одного блока должны быть уникальны и `session_name`, и `session_pub_key`;
|
||||
- на текущем этапе UI и регистрация не обязаны добавлять туда записи автоматически.
|
||||
|
||||
## 14. TrustedStateBlock
|
||||
## 15. TrustedStateBlock
|
||||
|
||||
Пока trusted-логика не реализована полностью, поэтому блок хранит только счетчик.
|
||||
|
||||
@@ -333,7 +354,7 @@ TrustedStateBlock
|
||||
|
||||
Пока блок с доверенными лицами не реализуется, потому что полный формат trusted-логики еще не составлен. В будущем trusted-связи, очереди, таймеры и подтверждения должны быть вынесены в отдельный формат.
|
||||
|
||||
## 15. Подпись user_pda
|
||||
## 16. Подпись user_pda
|
||||
|
||||
Подписывается не вся PDA целиком, а unsigned-часть записи:
|
||||
|
||||
@@ -354,7 +375,7 @@ Solana-программа проверяет подпись через встр
|
||||
|
||||
Смену формата подписи сейчас не трогаем.
|
||||
|
||||
## 16. Регистрация пользователя
|
||||
## 17. Регистрация пользователя
|
||||
|
||||
При регистрации:
|
||||
|
||||
@@ -372,12 +393,12 @@ Solana-программа проверяет подпись через встр
|
||||
- если покупается дополнительный лимит, пользователь платит комиссию за этот лимит;
|
||||
- вся unsigned-часть записи подписана `root_key`.
|
||||
|
||||
## 17. Обновление пользователя
|
||||
## 18. Обновление пользователя
|
||||
|
||||
При обновлении:
|
||||
|
||||
- PDA должна существовать;
|
||||
- `login`, `created_at_ms`, `root_key` не меняются;
|
||||
- `login`, `created_at_ms`, `recovery_key`, `root_key`, `client_key` не меняются;
|
||||
- `record_number = previous_record_number + 1`;
|
||||
- `prev_record_hash` равен хэшу unsigned-части предыдущей записи;
|
||||
- `updated_at_ms` обновляется;
|
||||
@@ -387,7 +408,7 @@ Solana-программа проверяет подпись через встр
|
||||
- при увеличении оплаченного лимита пользователь доплачивает комиссию;
|
||||
- Arweave `tx_id` может быть пустым или обновленным, но его содержимое Solana не валидирует.
|
||||
|
||||
## 18. Отличия от старого линейного формата
|
||||
## 19. Отличия от старого линейного формата
|
||||
|
||||
Старый формат после `login` хранил поля линейно:
|
||||
|
||||
@@ -395,8 +416,8 @@ Solana-программа проверяет подпись через встр
|
||||
- `root_key`;
|
||||
- `blockchain_key_status`;
|
||||
- `blockchain_key`;
|
||||
- `device_key_status`;
|
||||
- `device_key`;
|
||||
- `client_key_status`;
|
||||
- `client_key`;
|
||||
- `chain_number`;
|
||||
- `balance`;
|
||||
- серверные поля;
|
||||
@@ -407,17 +428,54 @@ Solana-программа проверяет подпись через встр
|
||||
|
||||
Новый целевой формат сохраняет первые 9 фиксированных полей как заголовок, но дальше переходит на типизированные блоки:
|
||||
|
||||
- recovery-ключ становится отдельным обязательным блоком;
|
||||
- ключи становятся отдельными блоками;
|
||||
- данные блокчейна становятся расширенным блоком со своим публичным ключом, лимитом, занятым размером, вершиной цепочки и Arweave `tx_id`;
|
||||
- серверные данные и access-серверы отделяются от данных блокчейна;
|
||||
- расширение формата делается добавлением новых версий блоков или новых `block_type`, а не вставкой полей в середину линейной записи.
|
||||
|
||||
## 18. Что пока не входит в формат
|
||||
## 20. Деривация ключей из master secret
|
||||
|
||||
Сама Solana-программа не вычисляет ключи из секрета и не хранит приватные ключи. Но текущая согласованная клиентская схема деривации для публичной версии формата фиксируется здесь как reference для UI/ESP32/внешних клиентов.
|
||||
|
||||
Базовая формула:
|
||||
|
||||
```text
|
||||
seed = SHA-256("SHiNE-key" || 0x00 || master_secret32 || 0x00 || suffix_utf8)
|
||||
```
|
||||
|
||||
Где:
|
||||
|
||||
- `master_secret32` — 32-байтовый master secret пользователя;
|
||||
- `suffix_utf8` — строка назначения ключа.
|
||||
|
||||
Согласованные suffix:
|
||||
|
||||
```text
|
||||
"recovery.key"
|
||||
"root.key"
|
||||
"blockchain.key"
|
||||
"client.key"
|
||||
```
|
||||
|
||||
Соответствие:
|
||||
|
||||
```text
|
||||
recovery.seed = SHA-256("SHiNE-key" || 0x00 || master_secret32 || 0x00 || "recovery.key")
|
||||
root.seed = SHA-256("SHiNE-key" || 0x00 || master_secret32 || 0x00 || "root.key")
|
||||
blockchain.seed = SHA-256("SHiNE-key" || 0x00 || master_secret32 || 0x00 || "blockchain.key")
|
||||
client.seed = SHA-256("SHiNE-key" || 0x00 || master_secret32 || 0x00 || "client.key")
|
||||
```
|
||||
|
||||
Далее каждая строка `seed` интерпретируется off-chain как `seed32` для отдельной пары Ed25519.
|
||||
|
||||
## 21. Что пока не входит в формат
|
||||
|
||||
Пока не проектируем:
|
||||
|
||||
- ротацию `recovery_key`;
|
||||
- ротацию `root_key`;
|
||||
- сложную ротацию `device_key`;
|
||||
- сложную ротацию `client_key`;
|
||||
- ротацию `blockchain_public_key`;
|
||||
- проверку содержимого Arweave transaction;
|
||||
- хранение полной истории пользовательского блокчейна внутри Solana;
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
5. `DAO_TREASURY_WALLET` / `dao_wallet` — казна DAO.
|
||||
6. `manager_wallet` — кошелек менеджера, которому DAO выдает лимиты на создание тикетов.
|
||||
7. `user root_key` — корневой ключ пользователя для подписи пользовательской записи.
|
||||
8. `user device_key` — ключ устройства пользователя.
|
||||
8. `user client_key` — ключ устройства пользователя.
|
||||
9. `server_key` — ключ сервера пользователя, если пользователь является сервером.
|
||||
|
||||
Текущие адреса из `programs/common/src/deploy_config.rs`:
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
|
||||
`UserMutableFields`:
|
||||
|
||||
- `device_key: Pubkey`
|
||||
- `client_key: Pubkey`
|
||||
- `blockchain_public_key: Pubkey`
|
||||
- `blockchain_name: String`
|
||||
- `used_bytes: u64`
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
<rect class="box actor" x="52" y="150" width="210" height="78"/>
|
||||
<text class="txt" x="72" y="181">Пользователь</text>
|
||||
<text class="small" x="72" y="206">signer, root_key, device_key</text>
|
||||
<text class="small" x="72" y="206">signer, root_key, client_key</text>
|
||||
|
||||
<rect class="box actor" x="52" y="310" width="210" height="78"/>
|
||||
<text class="txt" x="72" y="341">Покупатель тикета</text>
|
||||
|
||||
|
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 7.1 KiB |
@@ -43,8 +43,8 @@ bump, подмена аккаунта оракула) не найдено. Вс
|
||||
|---|---|---|---|---|---|---|
|
||||
| `init_users_economy_config` | ✓ | `owner == system` + `data_is_empty` (анти-reinit) | деривация + сверка | ✓ | — | значения из `settings`, не из ввода |
|
||||
| `update_users_economy_config` | ✓ + `signer == DAO_AUTHORITY` | `owner == program_id` | деривация + сверка | — | — | `lamports_per_limit_step > 0` |
|
||||
| `create_user_pda` | ✓ + `signer == device_key` | user_pda `owner == system` + empty; econ_config `owner == program_id` | user_pda, econ_config, inflow_vault, login_guard — все сверены | ✓ | ed25519 (record sig idx −2, last_block idx −1) | `inflow_vault` сверен с PDA `shine_payments`; login_guard сверен дважды |
|
||||
| `update_user_pda` | ✓ + `signer == device_key` | user_pda `owner == program_id`; econ_config `owner == program_id` | деривация + сверка | ✓ | ed25519 + `version == old+1` + `prev_hash == hash(old)` | immutable-поля сверены с прежней записью |
|
||||
| `create_user_pda` | ✓ + `signer == client_key` | user_pda `owner == system` + empty; econ_config `owner == program_id` | user_pda, econ_config, inflow_vault, login_guard — все сверены | ✓ | ed25519 (record sig idx −2, last_block idx −1) | `inflow_vault` сверен с PDA `shine_payments`; login_guard сверен дважды |
|
||||
| `update_user_pda` | ✓ + `signer == client_key` | user_pda `owner == program_id`; econ_config `owner == program_id` | деривация + сверка | ✓ | ed25519 + `version == old+1` + `prev_hash == hash(old)` | immutable-поля сверены с прежней записью |
|
||||
|
||||
### shine_payments
|
||||
|
||||
@@ -111,7 +111,7 @@ commit-reveal; для текущей модели — приемлемый ри
|
||||
- **Подмена PDA** невозможна нигде: всюду пара «деривация `find_program_address` + сверка полного адреса». Пользовательский bump не принимается, `create_program_address` с внешним bump не используется — bump-seed атаки исключены.
|
||||
- **Проверка владельца** при каждом чтении PDA: `read_state` и `validate_singleton_state_pda` (`shine_payments`) требуют `owner == id()`; `validate_users_economy_config_pda` и проверка `user_pda.owner == program_id` (`shine_users`) — перед десериализацией данных.
|
||||
- **Создаваемые PDA**: проверка `is_uninitialized` / `owner == system && data_is_empty` исключает повторную инициализацию и перезапись чужого аккаунта.
|
||||
- **signer / authority**: все handler начинают с обязательного `is_signer`; привилегированные операции дополнительно сверяют ключ с авторитетом (`config.dao_wallet`, `DAO_AUTHORITY`, `allowance.manager_wallet`, `ticket.recipient_wallet`, `device_key`).
|
||||
- **signer / authority**: все handler начинают с обязательного `is_signer`; привилегированные операции дополнительно сверяют ключ с авторитетом (`config.dao_wallet`, `DAO_AUTHORITY`, `allowance.manager_wallet`, `ticket.recipient_wallet`, `client_key`).
|
||||
- **system-program** сверяется с `system_program::ID` там, где идёт создание аккаунта/перевод; **sysvar инструкций** сверяется с `sysvar::instructions::id()` перед ed25519-интроспекцией.
|
||||
- **Аккаунт оракула**: пин адреса `PYTH_SOL_USD_ACCOUNT` + `owner == pyth_receiver` + `feed_id` + возраст (120 с) + доверительный интервал (10%).
|
||||
- **Ed25519 в `shine_users`**: относительные индексы −1/−2, `num_signatures == 1`, все три `ix_index == u16::MAX` (offset-данные внутри самой ed25519-инструкции), сверка `program_id == ed25519_program` и pubkey/signature/message по хэшу — указать на чужую инструкцию нельзя.
|
||||
|
||||
@@ -84,15 +84,15 @@ anchor deploy -p shine_users
|
||||
|
||||
## Кто оплачивает create/update user_pda
|
||||
|
||||
- И обычная регистрация `create_user_pda`, и последующее `update_user_pda` оплачиваются с `deviceKey`.
|
||||
- И обычная регистрация `create_user_pda`, и последующее `update_user_pda` оплачиваются с `clientKey`.
|
||||
- В UI это означает, что Solana fee payer всегда берётся из `device`-ключа пользователя/сервера.
|
||||
- `rootKey` нужен для подписи unsigned PDA-записи, но не оплачивает транзакцию.
|
||||
- Для server UI это особенно важно: перед `create` и `update` нужно пополнять именно Solana-адрес `deviceKey`.
|
||||
- Для server UI это особенно важно: перед `create` и `update` нужно пополнять именно Solana-адрес `clientKey`.
|
||||
|
||||
## Важно
|
||||
|
||||
- `init_users_economy_config` выполняется один раз на программу.
|
||||
Если PDA уже создан, повторный вызов вернёт ошибку "already initialized" (это нормальное поведение).
|
||||
- Серверные приватные ключи для Solana не используются как отдельный backend-wallet: в UI/server UI транзакцию оплачивает именно `deviceKey`, а содержимое записи подписывает `rootKey`.
|
||||
- Серверные приватные ключи для Solana не используются как отдельный backend-wallet: в UI/server UI транзакцию оплачивает именно `clientKey`, а содержимое записи подписывает `rootKey`.
|
||||
- `shine_users` внутри `create_user_pda` требует корректный адрес `shine_login_guard` для CPI-классификации логина.
|
||||
Несовпадение адреса приведёт к ошибке регистрации.
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
## 1. Текущие режимы
|
||||
|
||||
### 1. Создание новой сессии через `deviceKey`
|
||||
### 1. Создание новой сессии через `clientKey`
|
||||
|
||||
Поток:
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
|
||||
Смысл:
|
||||
|
||||
- новое устройство уже владеет приватным `deviceKey`;
|
||||
- сервер проверяет подпись `deviceKey`;
|
||||
- новое устройство уже владеет приватным `clientKey`;
|
||||
- сервер проверяет подпись `clientKey`;
|
||||
- создаётся обычная активная сессия пользователя;
|
||||
- этот поток остаётся без изменений.
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
|
||||
## 4. Чего сервер в этой версии не делает
|
||||
|
||||
- не передаёт приватный `deviceKey`;
|
||||
- не передаёт приватный `clientKey`;
|
||||
- не расшифровывает `encryptedPayload`;
|
||||
- не проверяет криптографию содержимого payload;
|
||||
- не делает клиентский UI;
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
Сокращение: **SAWD-v1**.
|
||||
|
||||
## Назначение
|
||||
Из 32-байтного `deviceKey32` пользователя получить один и тот же нативный Arweave RSA-4096 JWK wallet и один и тот же Arweave address.
|
||||
Из 32-байтного `clientKey32` пользователя получить один и тот же нативный Arweave RSA-4096 JWK wallet и один и тот же Arweave address.
|
||||
|
||||
## Вход
|
||||
- `deviceKey32`: ровно 32 байта.
|
||||
- Если исходный `device.key` хранится как Ed25519 PKCS8 base64, нужно извлечь последние 32 байта из PKCS8.
|
||||
- `clientKey32`: ровно 32 байта.
|
||||
- Если исходный `client.key` хранится как Ed25519 PKCS8 base64, нужно извлечь последние 32 байта из PKCS8.
|
||||
- Если используется Solana keypair JSON на 64 байта, используются только `bytes[0..31]`.
|
||||
|
||||
## Выход
|
||||
@@ -46,8 +46,8 @@
|
||||
- `SMALL_PRIME_LIMIT = 10000`
|
||||
|
||||
## Алгоритм
|
||||
1. Проверить `deviceKey32.length == 32`.
|
||||
2. `masterSeed32 = HMAC-SHA256(key = UTF8(MASTER_LABEL), message = deviceKey32)`.
|
||||
1. Проверить `clientKey32.length == 32`.
|
||||
2. `masterSeed32 = HMAC-SHA256(key = UTF8(MASTER_LABEL), message = clientKey32)`.
|
||||
3. Реализовать `deriveBytes(label, length)`:
|
||||
- `output = empty`
|
||||
- `counter = 0`
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
На устройстве в UI пользователь выбирает текущий активный кошелёк:
|
||||
|
||||
- `dev.key`
|
||||
- `client.key`
|
||||
- `root.key`
|
||||
- `custom`
|
||||
|
||||
@@ -115,7 +115,7 @@ ESP32 возвращает:
|
||||
- `requestId` — должен совпадать с `requestId` исходного запроса.
|
||||
- `ok` — признак успешного результата.
|
||||
- `wallet.type` — тип активного кошелька:
|
||||
- `dev.key`
|
||||
- `client.key`
|
||||
- `root.key`
|
||||
- `custom`
|
||||
- `wallet.publicKeyBase58` — публичный ключ активного кошелька в `Base58`.
|
||||
@@ -156,11 +156,11 @@ ESP32 возвращает:
|
||||
|
||||
Расширение уже знает публичные ключи пользователя из Solana PDA. Поэтому оно может дополнительно проверить ответ ESP32:
|
||||
|
||||
- если `wallet.type = dev.key`, то `publicKeyBase58` должен совпасть с `deviceKey`, прочитанным из PDA;
|
||||
- если `wallet.type = client.key`, то `publicKeyBase58` должен совпасть с `clientKey`, прочитанным из PDA;
|
||||
- если `wallet.type = root.key`, то `publicKeyBase58` должен совпасть с `rootKey`, прочитанным из PDA;
|
||||
- если `wallet.type = custom`, такой проверки по PDA в первой версии нет.
|
||||
|
||||
При несовпадении для `dev.key` или `root.key` расширение должно показать пользователю предупреждение, что возвращённый ключ не совпал с ожидаемым ключом из PDA.
|
||||
При несовпадении для `client.key` или `root.key` расширение должно показать пользователю предупреждение, что возвращённый ключ не совпал с ожидаемым ключом из PDA.
|
||||
|
||||
## 8. Ожидаемое поведение UI расширения
|
||||
|
||||
|
||||
Reference in New Issue
Block a user