# Деривация секрета и ключей SHiNE (формулы)

> **Статус: ИСТОЧНИК ИСТИНЫ (single source of truth) по конкретной деривации.**
> Этот файл описывает, как из пароля получается секрет и как из секрета выводятся
> все ключи (root, blockchain, device/Solana, homeserver) — формулами, байт-в-байт.
> Если в коде меняется деривация (формула секрета, параметры Argon2id, соль, формула
> ключа, разделитель `|`, набор/имена суффиксов, формат homeserver-ключа, связь
> dev-ключ ↔ Solana-адрес) — **в том же изменении обязательно править этот документ**.
> Роли и назначение ключей описаны отдельно в `Dev_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 | `bch.key` | Подписывает `LastBlockState` персонального блокчейна (`blockchain_public_key`). |
| device / **Solana** | `dev.key` | Ключ устройства = Solana-ключ. Fee payer и подпись Solana-транзакций; адрес кошелька = `base58(devicePub)`. См. §3. |
| homeserver | `homeserver.key:<имя>` | Ключ homeserver-устройства, по одному на каждый homeserver (различитель — имя). См. §4. |

Полные роли каждого ключа — в `Dev_Docs/Keys/README.md`.

---

## 3. Solana-ключ

Отдельного «солана-ключа» нет. На Solana работают два ключа:

- **`dev.key` (device) — пополняемый кошелёк и fee payer.** Solana-адрес = `base58(devicePub)`.
  Этим ключом оплачиваются и подписываются `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»).

Кратко про роли на Solana: `root.key` — это **главный (master) ключ**: им управляют PDA-записью
(`create/update`) и через это можно заменить все остальные ключи; `dev.key` — это **пополняемый
кошелёк и плательщик комиссий**. Полное описание ролей — `Dev_Docs/Keys/README.md`.

---

## 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
```

Публичный ключ homeserver-а публикуется в `SessionsBlock` пользовательской PDA как
`session_pub_key` с `session_type = 100`, имя — в `session_name` (формат PDA §13).

> Это переименование прежней схемы `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` — `deriveUserWalletAddress`: адрес = `base58(devicePub)` (~113).
- `shine-UI/js/pages/topup-view.js` — `deviceWalletAddressFromBundle`: тот же канонический адрес из `preGeneratedKeyBundle.devicePair`.
  Прежний расходящийся путь `deriveWalletFromPassword` (прямой Argon2 по `dev.key`, мимо `masterSecret`) удалён.

### Деривация ключей (прошивка 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.0.md`
  — `RootKeyBlock` §6, `DeviceKeyBlock` §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) —
  выводит ключи как `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-ключа или связь dev-ключ ↔ Solana-адрес —
   **обязательно** отражать здесь в том же изменении.
3. Пункты, помеченные ⚠️, — это долг к устранению, а не норма.
4. Нельзя сознательно оставлять код и этот документ в рассинхроне без отдельной явной договорённости.
