SHA256
154 lines
11 KiB
Markdown
154 lines
11 KiB
Markdown
# Деривация секрета и ключей SHiNE (формулы)
|
||
|
||
> **Статус: ИСТОЧНИК ИСТИНЫ (single source of truth) по конкретной деривации.**
|
||
> Этот файл описывает, как из пароля получается секрет и как из секрета выводятся
|
||
> все ключи (root, blockchain, client, homeserver) — формулами, байт-в-байт.
|
||
> Если в коде меняется деривация (формула секрета, параметры Argon2id, соль, формула
|
||
> ключа, разделитель `|`, набор/имена суффиксов, формат homeserver-ключа, связь
|
||
> blockchain key ↔ Solana-адрес) — **в том же изменении обязательно править этот документ**.
|
||
> Роли и назначение ключей описаны отдельно в `docs/Keys/README.md` (архитектура).
|
||
> Здесь — только механика. Документ намеренно краткий.
|
||
|
||
---
|
||
|
||
## 1. Секрет (masterSecret)
|
||
|
||
`masterSecret` — 32 байта. Два источника:
|
||
|
||
**А. Из пароля пользователя (основной путь, UI).**
|
||
|
||
```
|
||
login = trim(lowercase(login))
|
||
salt = SHA-256("shine-auth-v2|login=" + login + "|suffix=master.secret")[0..16) // первые 16 байт
|
||
material = utf8(login + "\n" + password)
|
||
masterSecret(32) = Argon2id(material, salt, t=2, m=65536 KiB, p=1, dkLen=32)
|
||
```
|
||
|
||
- Параметры Argon2id фиксированы: `t=2`, `m=65536` (64 МиБ), `p=1`, `dkLen=32`.
|
||
- Логин входит и в соль, и в начало `material` (склейка через `\n`).
|
||
- Пустой пароль **запрещён**: легаси-fallback без Argon2 удалён, `deriveMasterSecretFromPassword` бросает ошибку на пустом пароле, а форма регистрации в UI блокирует пустой пароль (`register-view.js`).
|
||
|
||
**Б. Случайный (прошивка ESP32, новый аккаунт без пароля).**
|
||
|
||
```
|
||
masterSecret(32) = 32 случайных байта (esp_random) // хранится на устройстве как base58
|
||
```
|
||
|
||
Дальше деривация ключей одинакова независимо от источника секрета.
|
||
|
||
---
|
||
|
||
## 2. Производные ключи
|
||
|
||
Все ключи выводятся из `masterSecret` по **одной формуле**, отличается только суффикс:
|
||
|
||
```
|
||
material = base64_std(masterSecret) + "|" + <суффикс>
|
||
seed(32) = SHA-256(material)
|
||
(pub, priv) = Ed25519_keypair_from_seed(seed)
|
||
```
|
||
|
||
- `base64_std` — стандартный base64 (не url-safe).
|
||
- Разделитель — символ `|`.
|
||
- Суффиксы значимы байт-в-байт (регистр и точки важны).
|
||
|
||
| Ключ | Суффикс | Назначение (кратко) |
|
||
|------|---------|---------------------|
|
||
| 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. |
|
||
| homeserver | `homeserver.key:<имя>` | Ключ homeserver-устройства, по одному на каждый homeserver (различитель — имя). См. §4. |
|
||
|
||
Полные роли каждого ключа — в `docs/Keys/README.md`.
|
||
|
||
---
|
||
|
||
## 3. Solana-кошелёк и authority
|
||
|
||
Отдельного «solana.key» нет. В актуальной модели Solana-wallet пользователя — **активный `blockchain.key`**:
|
||
|
||
- адрес кошелька = `base58(activeBlockchainPub)`;
|
||
- им оплачивается регистрация (`create_user_pda`) и обычные `update_user_pda`;
|
||
- обычное обновление PDA авторизуется активным blockchain key;
|
||
- новый fork подписывает новое PDA новым blockchain key, а старый активный blockchain key разрешает обычный переход;
|
||
- полная смена пароля/ключей — особый recovery-переход: старый `root.key` дополнительно разрешает одно новое unsigned PDA state, а **новый blockchain key** подписывает тот же hash и становится новым wallet/authority.
|
||
|
||
`root.key` не является fee payer. Он используется как холодное дополнительное разрешение только для полной ротации root + client + blockchain fork.
|
||
|
||
`client.key` больше не используется как Solana-wallet/fee payer. Он остаётся клиентским криптографическим ключом (DM/устройства) и входом в отдельный протокол derivation Arweave-кошелька.
|
||
|
||
При fork Solana-адрес меняется вместе с blockchain key. Перенос SOL со старого blockchain-wallet на новый является отдельным необязательным этапом ротации; в текущей первой реализации этот этап оставлен явной заглушкой.
|
||
|
||
---
|
||
|
||
## 4. Ключи homeserver
|
||
|
||
У пользователя может быть несколько homeserver-ов. Каждый имеет **своё имя** и **свой приватный ключ**,
|
||
выведенный из секрета по той же формуле с именованным суффиксом:
|
||
|
||
```
|
||
suffix = "homeserver.key:" + <имя homeserver> // имя по умолчанию: "homeserver1"
|
||
material = base64_std(masterSecret) + "|" + suffix
|
||
seed(32) = SHA-256(material)
|
||
(pub, priv) = Ed25519_keypair_from_seed(seed)
|
||
```
|
||
|
||
Пример для двух homeserver-ов:
|
||
|
||
```
|
||
homeserver.key:home-a -> ключ A
|
||
homeserver.key:home-b -> ключ B
|
||
```
|
||
|
||
В PDA 1.2 `SessionsBlock` удалён, поэтому homeserver-ключи больше не публикуются через user PDA. Сама детерминированная деривация ключа сохранена как отдельный механизм устройства; способ его будущей публикации/авторизации определяется отдельно от PDA 1.2.
|
||
|
||
> Это переименование прежней схемы `subserver.key:<имя>` → `homeserver.key:<имя>`.
|
||
> Термин «саб-сервер» по проекту заменяется на «homeserver».
|
||
|
||
---
|
||
|
||
## 5. Где это в коде
|
||
|
||
### Деривация секрета и ключей (UI, каноническая)
|
||
- `shine-UI/js/services/crypto-utils.js`
|
||
- секрет из пароля: `makeArgon2Salt`, `deriveMasterSecretArgon2id`, `deriveMasterSecretFromPassword` (~129–218);
|
||
- ключ из секрета: `deriveEd25519FromMasterSecret` (~220).
|
||
- `shine-UI/js/services/auth-service.js` — набор root/bch/dev из `masterSecret` (~732–758).
|
||
- `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` — адрес пополнения = `base58(blockchainPub)`.
|
||
- `shine-UI/js/pages/topup-view.js` — тот же адрес из `preGeneratedKeyBundle.blockchainPair`.
|
||
- `shine-UI/js/services/solana-wallet-service.js` — текущий пользовательский Solana-wallet загружается из сохранённого `blockchainKey`.
|
||
|
||
### Деривация ключей (прошивка ESP32)
|
||
- `ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/main-device/shine_homeserver_main/shine_homeserver_main.ino`
|
||
- основной скетч ESP32-проекта `SHiNE`; `deriveKeysFromMasterSecret` (~782), `restoreDerivedKeysFromSecret` (~806), `deriveFreshSecretAndWallet` (~829);
|
||
- регистрация/подпись Solana: `registerHomeserverOnSolana` (~1182), `signMessageEd25519` (~1147).
|
||
- `ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/main-device/shine_homeserver_ui/shine_homeserver_ui.ino`
|
||
- старый тестовый вариант; оставлен как legacy-скетч для сравнения и диагностики.
|
||
|
||
### Формат PDA (куда попадают ключи)
|
||
- `shine-solana/shine/doc/formats/shine-user-pda-format-v.1.2.md`
|
||
— `RootKeyBlock`, `ClientKeyBlock`, append-only `BlockchainRegistryBlock` и экономика лимита. `SessionsBlock` в PDA 1.2 отсутствует.
|
||
|
||
### Сервер (тестовый seed)
|
||
- `SHiNE-server/src/test/java/test/it/cases/SeedDataPopulationHelper.java` `deriveKeysFromPassword` (~246) —
|
||
выводит ключи как `Ed25519(SHA-256(base64(SHA-256(password)) + suffix))`, **без** Argon2 и **без** разделителя `|`.
|
||
Это **не баг**, а точное повторение легаси-пути UI `derivePasswordSeed` (для пустого пароля), у которого тоже нет `|`.
|
||
С современным путём `masterSecret`-bundle (Argon2 + `base64(secret)|suffix`) он **не совпадает** by design.
|
||
Если потребуется, чтобы seed совпадал с реальными клиентами на Argon2 — нужно отдельно портировать
|
||
Argon2id+masterSecret в Java (на сервере Argon2 сейчас нет). Простое добавление `|` было бы **неверным**:
|
||
сломало бы совпадение с легаси-путём и всё равно не дало бы совпадения с Argon2-путём.
|
||
|
||
---
|
||
|
||
## 6. Правило синхронизации (обязательно)
|
||
|
||
1. Этот документ — источник истины по деривации секрета и ключей.
|
||
2. Любое изменение кода, затрагивающее формулу секрета, параметры Argon2id, соль, формулу ключа,
|
||
разделитель `|`, набор/имена суффиксов, формат homeserver-ключа или связь blockchain key ↔ Solana-адрес —
|
||
**обязательно** отражать здесь в том же изменении.
|
||
3. Пункты, помеченные ⚠️, — это долг к устранению, а не норма.
|
||
4. Нельзя сознательно оставлять код и этот документ в рассинхроне без отдельной явной договорённости.
|