SHA256
Добавить ESP pairing через доверенные сессии
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
Этот файл описывает именно этапы авторизации клиента, то есть как создать новую сессию и как войти в уже существующую.
|
||||
|
||||
Здесь четыре метода:
|
||||
Здесь четыре базовых метода обычной авторизации:
|
||||
|
||||
- `AuthChallenge`
|
||||
- `CreateAuthSession`
|
||||
@@ -36,6 +36,16 @@
|
||||
|
||||
Ниже в документе сначала описан сценарий, а потом зафиксированы точные форматы запросов и ответов.
|
||||
|
||||
Отдельно появился новый серверный сценарий pairing через доверенный homeserver/ESP. Он не заменяет обычный вход и описан в:
|
||||
|
||||
- `Dev_Docs/Протоколы/ESP_Pairing_и_режимы_подключения.md`
|
||||
|
||||
Кратко:
|
||||
|
||||
- `AuthChallenge/CreateAuthSession` и `SessionChallenge/SessionLogin` остаются каноническими потоками обычной авторизации;
|
||||
- pairing через ESP идёт отдельными `op` и только подготавливает безопасное добавление новой сессии;
|
||||
- решение об одобрении pairing принимает любая уже авторизованная доверенная сессия пользователя.
|
||||
|
||||
## 1. Поток авторизации
|
||||
|
||||
Поддерживаются два сценария:
|
||||
@@ -289,6 +299,30 @@ SESSION_LOGIN:{sessionId}:{timeMs}:{nonce}
|
||||
|
||||
---
|
||||
|
||||
## 6. Pairing через homeserver/ESP
|
||||
|
||||
Новые `op`, относящиеся к этому сценарию:
|
||||
|
||||
- `UpsertEspPairingSettings`
|
||||
- `StartEspPairing`
|
||||
- `ListEspPairingRequests`
|
||||
- `ApproveEspPairing`
|
||||
- `RejectEspPairing`
|
||||
- `GetEspPairingStatus`
|
||||
|
||||
В этом потоке:
|
||||
|
||||
- новое устройство не владеет `deviceKey` и не проходит обычный `CreateAuthSession`;
|
||||
- пароль проверяется сервером только как фильтр;
|
||||
- решение об одобрении принимает уже авторизованная доверенная сессия пользователя;
|
||||
- сервер не расшифровывает `encryptedPayload` и не становится источником приватных ключей.
|
||||
|
||||
Точные форматы этих операций см. в `03_Session_Management_API.md` и в протокольном документе:
|
||||
|
||||
- `Dev_Docs/Протоколы/ESP_Pairing_и_режимы_подключения.md`
|
||||
|
||||
---
|
||||
|
||||
## 6. Пример ошибки
|
||||
|
||||
```json
|
||||
|
||||
@@ -7,6 +7,18 @@
|
||||
- `ListSessions` — получить список активных сессий пользователя;
|
||||
- `CloseActiveSession` — закрыть одну из активных сессий.
|
||||
|
||||
Дополнительно в этом же слое управления сессиями появился сценарий pairing через доверенную уже авторизованную сессию пользователя:
|
||||
|
||||
- `UpsertEspPairingSettings`
|
||||
- `ListEspPairingRequests`
|
||||
- `ApproveEspPairing`
|
||||
- `RejectEspPairing`
|
||||
|
||||
Анонимное новое устройство работает с двумя связанными операциями:
|
||||
|
||||
- `StartEspPairing`
|
||||
- `GetEspPairingStatus`
|
||||
|
||||
Логика раздела такая:
|
||||
|
||||
- сначала пользователь проходит `SessionLogin`;
|
||||
@@ -151,3 +163,226 @@ K9v3nQ4u8jYk0a2p7cD4mLx1zR0sT5wV6bN8eH3fQ1M
|
||||
|
||||
Важно: это **не человеко-читаемое имя**, а непрозрачный идентификатор.
|
||||
Нужно передавать его как есть, без нормализации регистра и без URL-экранирования внутри JSON.
|
||||
|
||||
---
|
||||
|
||||
## 5. ESP pairing через доверенную сессию
|
||||
|
||||
Этот блок относится к сценарию добавления новой сессии через доверенное устройство пользователя.
|
||||
|
||||
### 5.1. `UpsertEspPairingSettings`
|
||||
|
||||
Доступно для любой уже авторизованной доверенной сессии пользователя.
|
||||
|
||||
### Запрос
|
||||
|
||||
```json
|
||||
{
|
||||
"op": "UpsertEspPairingSettings",
|
||||
"requestId": "esp-set-001",
|
||||
"payload": {
|
||||
"enabled": true,
|
||||
"passwordHash": "argon2id$...",
|
||||
"ttlSeconds": 180
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Успешный ответ
|
||||
|
||||
```json
|
||||
{
|
||||
"op": "UpsertEspPairingSettings",
|
||||
"requestId": "esp-set-001",
|
||||
"status": 200,
|
||||
"ok": true,
|
||||
"payload": {
|
||||
"enabled": true,
|
||||
"ttlSeconds": 180
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Ошибки
|
||||
|
||||
- `400 / EMPTY_PASSWORD_HASH` — попытка включить pairing без `passwordHash`.
|
||||
- `463 / PAIRING_REQUIRES_AUTH_SESSION` — операция вызвана без уже авторизованной доверенной сессии пользователя.
|
||||
|
||||
### 5.2. `StartEspPairing`
|
||||
|
||||
Эта операция доступна без уже существующей пользовательской сессии.
|
||||
|
||||
### Запрос
|
||||
|
||||
```json
|
||||
{
|
||||
"op": "StartEspPairing",
|
||||
"requestId": "esp-start-001",
|
||||
"payload": {
|
||||
"login": "alice",
|
||||
"passwordHash": "argon2id$...",
|
||||
"requesterSessionKey": "ed25519/BASE64_PUBLIC_KEY",
|
||||
"requesterSessionType": 1,
|
||||
"requesterClientPlatform": "Android",
|
||||
"payloadType": 1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Поле `trustedSessionOnline` показывает, что у пользователя сейчас есть хотя бы одна онлайн доверенная сессия, способная принять pairing-заявку.
|
||||
|
||||
### Успешный ответ
|
||||
|
||||
```json
|
||||
{
|
||||
"op": "StartEspPairing",
|
||||
"requestId": "esp-start-001",
|
||||
"status": 200,
|
||||
"ok": true,
|
||||
"payload": {
|
||||
"pairingId": "base64url",
|
||||
"state": "created",
|
||||
"shortCode": "4920709",
|
||||
"fingerprintB58": "ASvYDPQidnAroKzQjtCjTuEQE8ckktV5nmmhYRhDzGaA",
|
||||
"expiresAtMs": 1781441990538,
|
||||
"trustedSessionOnline": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Ошибки
|
||||
|
||||
- `400 / EMPTY_LOGIN`
|
||||
- `400 / EMPTY_PASSWORD_HASH`
|
||||
- `400 / EMPTY_REQUESTER_SESSION_KEY`
|
||||
- `400 / BAD_REQUESTER_SESSION_KEY`
|
||||
- `400 / BAD_SESSION_TYPE`
|
||||
- `400 / BAD_PAYLOAD_TYPE`
|
||||
- `422 / PAIRING_NOT_AVAILABLE`
|
||||
- `422 / PAIRING_PASSWORD_INVALID`
|
||||
- `429 / PAIRING_RATE_LIMITED`
|
||||
|
||||
### 5.3. `ListEspPairingRequests`
|
||||
|
||||
Доступно для любой уже авторизованной доверенной сессии пользователя.
|
||||
|
||||
### Успешный ответ
|
||||
|
||||
```json
|
||||
{
|
||||
"op": "ListEspPairingRequests",
|
||||
"requestId": "esp-list-001",
|
||||
"status": 200,
|
||||
"ok": true,
|
||||
"payload": {
|
||||
"requests": [
|
||||
{
|
||||
"pairingId": "base64url",
|
||||
"state": "created",
|
||||
"requesterSessionKey": "ed25519/BASE64_PUBLIC_KEY",
|
||||
"requesterSessionType": 1,
|
||||
"requesterClientPlatform": "Android",
|
||||
"payloadType": 1,
|
||||
"shortCode": "4920709",
|
||||
"fingerprintB58": "ASvYDPQidnAroKzQjtCjTuEQE8ckktV5nmmhYRhDzGaA",
|
||||
"createdAtMs": 1781441810538,
|
||||
"expiresAtMs": 1781441990538,
|
||||
"deliveredToHomeserver": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Ошибки
|
||||
|
||||
- `463 / PAIRING_REQUIRES_AUTH_SESSION`
|
||||
|
||||
### 5.4. `ApproveEspPairing`
|
||||
|
||||
Доступно для любой уже авторизованной доверенной сессии пользователя.
|
||||
|
||||
### Запрос
|
||||
|
||||
```json
|
||||
{
|
||||
"op": "ApproveEspPairing",
|
||||
"requestId": "esp-approve-001",
|
||||
"payload": {
|
||||
"pairingId": "base64url",
|
||||
"encryptedPayload": "BASE64_OR_OTHER_OPAQUE_PAYLOAD"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Успешный ответ
|
||||
|
||||
```json
|
||||
{
|
||||
"op": "ApproveEspPairing",
|
||||
"requestId": "esp-approve-001",
|
||||
"status": 200,
|
||||
"ok": true,
|
||||
"payload": {
|
||||
"pairingId": "base64url",
|
||||
"state": "approved"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Ошибки
|
||||
|
||||
- `400 / EMPTY_PAIRING_ID`
|
||||
- `400 / EMPTY_ENCRYPTED_PAYLOAD`
|
||||
- `404 / PAIRING_NOT_FOUND`
|
||||
- `422 / PAIRING_OF_ANOTHER_USER`
|
||||
- `422 / PAIRING_NOT_PENDING`
|
||||
- `422 / PAIRING_EXPIRED`
|
||||
- `463 / PAIRING_REQUIRES_AUTH_SESSION`
|
||||
|
||||
### 5.5. `RejectEspPairing`
|
||||
|
||||
Доступно для любой уже авторизованной доверенной сессии пользователя. Похоже на approve, но переводит заявку в `state=rejected`.
|
||||
|
||||
### 5.6. `GetEspPairingStatus`
|
||||
|
||||
Операция для нового устройства.
|
||||
|
||||
### Запрос
|
||||
|
||||
```json
|
||||
{
|
||||
"op": "GetEspPairingStatus",
|
||||
"requestId": "esp-status-001",
|
||||
"payload": {
|
||||
"pairingId": "base64url"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Успешный ответ после approve
|
||||
|
||||
```json
|
||||
{
|
||||
"op": "GetEspPairingStatus",
|
||||
"requestId": "esp-status-001",
|
||||
"status": 200,
|
||||
"ok": true,
|
||||
"payload": {
|
||||
"pairingId": "base64url",
|
||||
"state": "approved",
|
||||
"shortCode": "4920709",
|
||||
"fingerprintB58": "ASvYDPQidnAroKzQjtCjTuEQE8ckktV5nmmhYRhDzGaA",
|
||||
"payloadType": 1,
|
||||
"encryptedPayload": "AQIDBA==",
|
||||
"expiresAtMs": 1781441990538
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Возможные `state`
|
||||
|
||||
- `created`
|
||||
- `approved`
|
||||
- `rejected`
|
||||
- `expired`
|
||||
|
||||
@@ -19,6 +19,12 @@
|
||||
| `CreateAuthSession` | `02_Authentication_API.md` | создание новой авторизованной сессии |
|
||||
| `SessionChallenge` | `02_Authentication_API.md` | challenge для входа в существующую сессию |
|
||||
| `SessionLogin` | `02_Authentication_API.md` | вход в существующую сессию |
|
||||
| `UpsertEspPairingSettings` | `03_Session_Management_API.md` | включение/обновление pairing-настроек доверенной сессией |
|
||||
| `StartEspPairing` | `03_Session_Management_API.md` | создание pairing-заявки для нового устройства |
|
||||
| `ListEspPairingRequests` | `03_Session_Management_API.md` | список активных pairing-заявок для доверенной сессии |
|
||||
| `ApproveEspPairing` | `03_Session_Management_API.md` | подтверждение pairing-заявки доверенной сессией |
|
||||
| `RejectEspPairing` | `03_Session_Management_API.md` | отклонение pairing-заявки доверенной сессией |
|
||||
| `GetEspPairingStatus` | `03_Session_Management_API.md` | чтение статуса и результата pairing-заявки |
|
||||
| `ListSessions` | `03_Session_Management_API.md` | список активных сессий |
|
||||
| `CloseActiveSession` | `03_Session_Management_API.md` | закрытие активной сессии |
|
||||
| `AddBlock` | `04_Add_Block_to_Blockchain_API.md` | добавление блока в блокчейн |
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
# server esp pairing
|
||||
|
||||
- краткое описание фичи:
|
||||
- на сервере добавлен отдельный pairing-сценарий для подключения нового устройства через доверенную уже авторизованную сессию пользователя без выдачи приватных ключей сервером;
|
||||
- добавлены `op`: `UpsertEspPairingSettings`, `StartEspPairing`, `ListEspPairingRequests`, `ApproveEspPairing`, `RejectEspPairing`, `GetEspPairingStatus`;
|
||||
- подтверждать pairing может любая доверенная сессия пользователя.
|
||||
|
||||
- что именно проверять:
|
||||
- любая уже авторизованная сессия пользователя включает pairing и задаёт `passwordHash`;
|
||||
- новое устройство создаёт заявку через `StartEspPairing`;
|
||||
- другая доверенная сессия пользователя видит заявку в `ListEspPairingRequests`;
|
||||
- доверенная сессия подтверждает заявку через `ApproveEspPairing`;
|
||||
- новое устройство получает `approved + encryptedPayload` через `GetEspPairingStatus`;
|
||||
- неавторизованное новое устройство не может вызывать управляющие pairing-операции.
|
||||
|
||||
- ожидаемый результат:
|
||||
- заявка создаётся со статусом `created`;
|
||||
- у заявки есть `pairingId`, `shortCode`, `fingerprintB58`, `expiresAtMs`;
|
||||
- после approve статус становится `approved`, а `encryptedPayload` возвращается новому устройству;
|
||||
- неавторизованное соединение получает отказ `463 / PAIRING_REQUIRES_AUTH_SESSION`.
|
||||
|
||||
- статус:
|
||||
- `pending`
|
||||
@@ -0,0 +1,106 @@
|
||||
# ESP Pairing и режимы подключения
|
||||
|
||||
Этот документ фиксирует текущие и новые режимы входа/подключения в SHiNE без клиентской UI-реализации. Он нужен как отдельная точка входа по сценариям подключения, чтобы не смешивать обычную авторизацию и серверный pairing через доверенное уже авторизованное устройство пользователя.
|
||||
|
||||
## 1. Текущие режимы
|
||||
|
||||
### 1. Создание новой сессии через `deviceKey`
|
||||
|
||||
Поток:
|
||||
|
||||
`AuthChallenge -> CreateAuthSession`
|
||||
|
||||
Смысл:
|
||||
|
||||
- новое устройство уже владеет приватным `deviceKey`;
|
||||
- сервер проверяет подпись `deviceKey`;
|
||||
- создаётся обычная активная сессия пользователя;
|
||||
- этот поток остаётся без изменений.
|
||||
|
||||
### 2. Повторный вход в существующую сессию через `sessionKey`
|
||||
|
||||
Поток:
|
||||
|
||||
`SessionChallenge -> SessionLogin`
|
||||
|
||||
Смысл:
|
||||
|
||||
- устройство уже владеет приватным `sessionKey`;
|
||||
- сервер проверяет подпись `sessionKey`;
|
||||
- соединение снова входит в существующую сессию;
|
||||
- этот поток тоже остаётся без изменений.
|
||||
|
||||
## 2. Новый режим: добавление сессии через доверенное устройство пользователя
|
||||
|
||||
Новый поток не заменяет обычный логин, а живёт рядом с ним.
|
||||
|
||||
Цель:
|
||||
|
||||
- новое устройство знает `login + pairing password`;
|
||||
- сервер использует пароль только как фильтр от мусора;
|
||||
- реальное доверие даёт любая уже онлайн доверенная сессия пользователя;
|
||||
- сервер не выдаёт приватные ключи сам от себя.
|
||||
|
||||
Поток версии `v1`:
|
||||
|
||||
1. Любая доверенная сессия пользователя создаёт на сервере pairing-настройку:
|
||||
`UpsertEspPairingSettings`
|
||||
2. Новое устройство создаёт pending-заявку:
|
||||
`StartEspPairing`
|
||||
3. Онлайн доверенная сессия видит список активных заявок:
|
||||
`ListEspPairingRequests`
|
||||
4. Доверенная сессия либо подтверждает заявку:
|
||||
`ApproveEspPairing`
|
||||
5. Либо отклоняет:
|
||||
`RejectEspPairing`
|
||||
6. Новое устройство читает результат:
|
||||
`GetEspPairingStatus`
|
||||
|
||||
## 3. Что именно делает сервер
|
||||
|
||||
- хранит включённость pairing и opaque `passwordHash`;
|
||||
- хранит pending/approved/rejected pairing-заявки;
|
||||
- рассчитывает короткий код `shortCode` из `7` цифр;
|
||||
- рассчитывает длинный `fingerprintB58` из `SHA-256` заявки;
|
||||
- уведомляет онлайн доверенные сессии событием `IncomingEspPairingRequest`, если такие сессии подключены;
|
||||
- хранит переданный `encryptedPayload` как непрозрачную строку и не анализирует его содержимое.
|
||||
|
||||
## 4. Чего сервер в этой версии не делает
|
||||
|
||||
- не передаёт приватный `deviceKey`;
|
||||
- не расшифровывает `encryptedPayload`;
|
||||
- не проверяет криптографию содержимого payload;
|
||||
- не делает клиентский UI;
|
||||
- не навязывает конкретную схему `Ed25519 -> X25519` в коде сервера.
|
||||
|
||||
Это намеренно: серверная версия `v1` подготавливает безопасный каркас маршрутизации и состояния, а настоящая E2E-логика упаковки ключей будет жить на клиентах и ESP-устройствах.
|
||||
|
||||
## 5. Роли и ограничения
|
||||
|
||||
- любая уже авторизованная доверенная сессия пользователя может вызывать:
|
||||
- `UpsertEspPairingSettings`
|
||||
- `ListEspPairingRequests`
|
||||
- `ApproveEspPairing`
|
||||
- `RejectEspPairing`
|
||||
- новое устройство может вызвать `StartEspPairing` и `GetEspPairingStatus` без уже существующей авторизованной сессии;
|
||||
- `payloadType` поддерживается в вариантах:
|
||||
- `1` — минимальный пакет
|
||||
- `2` — расширенный пакет
|
||||
- `3` — полный пакет
|
||||
|
||||
Сервер не интерпретирует эти три типа глубже, а только фиксирует их в состоянии заявки.
|
||||
|
||||
## 6. Статусы pairing-заявки
|
||||
|
||||
- `created` — заявка создана и ждёт решения доверенной сессии;
|
||||
- `approved` — доверенная сессия подтвердила и приложила `encryptedPayload`;
|
||||
- `rejected` — доверенная сессия отклонила заявку;
|
||||
- `expired` — TTL заявки истёк до подтверждения.
|
||||
|
||||
## 7. Практический смысл
|
||||
|
||||
Эта схема даёт нужное разделение доверия:
|
||||
|
||||
- пароль на сервере только отсеивает лишних;
|
||||
- онлайн доверенная сессия решает, добавлять ли новую сессию;
|
||||
- сервер остаётся маршрутизатором и хранилищем состояния, а не владельцем секретов.
|
||||
Reference in New Issue
Block a user