- crypto: KDF Ayla (двойной HMAC, suffix 0x30/31/32; app/dev направления),
AES-256-CBC с непрерывной цепочкой (iv обновляется mbedtls на месте),
Java-паддинг >=1 NUL; mode-latch против misuse (encrypt|decrypt);
zeroize ключей при повторном init; векторы из APK (4 сессии × 4 сообщения,
включая legacy-приём без NUL) — scripts/gen_kdf_vectors.py.
- envelope: pack/unpack {"enc","sign"}; расшифровка (движение цепочки)
ДО проверки подписи; сравнение подписи в константном времени;
extract_seq_no — depth-1 сканер без лимита токенов (OOB после escape
исправлен, регресс-тесты по ASan-репро ревьюера).
- json: Writer (фикс. буфер, стек глубин, escape, ok()=false при
переполнении) + Doc на jsmn (64 токена, unescape, overflow-guard).
- httpc: блокирующий POST/PUT для local_reg (статус 200-599, дренаж,
shutdown перед close).
- third_party/jsmn (MIT, JSMN_STATIC).
- CMake: mbedtls системный (/usr/include/mbedtls3) или FetchContent;
IDF: PRIV_REQUIRES mbedtls.
- CI: 3 конфигурации — gcc-Release, gcc-Debug+ASan/UBSan, clang-Release;
6/6 тестов стабильно; ESP-IDF esp32 build complete.
Ревью под-агентом: 3 круга (OOB-блокер + тестовые флаки закрыты), APPROVED.
71 lines
3.5 KiB
C++
71 lines
3.5 KiB
C++
// Сессионная криптография Ayla LAN (docs/PROTOCOL.md §3).
|
||
// KDF: key = HMAC-SHA256_k(HMAC-SHA256_k(msg) || msg), msg = rnd_a|rnd_b|t_a|t_b|X,
|
||
// X: 0x30 sign / 0x31 crypto / 0x32 iv(первые 16 байт).
|
||
// Направления: app (rnd1,rnd2,t1,t2) — исходящие; dev (rnd2,rnd1,t2,t1) — входящие.
|
||
// ВАЖНО: AES-CBC-цепочка непрерывна в рамках сессии (каждое сообщение
|
||
// продолжает цепочку с последнего шифроблока предыдущего).
|
||
#pragma once
|
||
|
||
#include <cstddef>
|
||
#include <cstdint>
|
||
|
||
#include "mbedtls/aes.h"
|
||
#include "mbedtls/md.h"
|
||
|
||
namespace fgl::ayla {
|
||
|
||
constexpr size_t kSha256Len = 32;
|
||
constexpr size_t kAesKeyLen = 32; // AES-256
|
||
constexpr size_t kAesBlockLen = 16;
|
||
constexpr size_t kHmacKeyLen = 32;
|
||
|
||
class DirectionCrypto {
|
||
public:
|
||
// Выводит ключи направления. rnd_a/rnd_b — 16-символьные токены,
|
||
// t_a/t_b — 64-битные «времена» (материал KDF, значение произвольно).
|
||
// app: (random_1, random_2, time_1, time_2); dev: (random_2, random_1, time_2, time_1).
|
||
bool init(const char* lanip_key, const char* rnd_a, const char* rnd_b,
|
||
int64_t t_a, int64_t t_b);
|
||
|
||
// Шифрует in (любая длина) с zero-паддингом (>=1 NUL, кратно 16).
|
||
// out_cap должен быть >= padded_len = ((in_len+1)/16+1)*16.
|
||
// Возвращает длину шифротекста или -1. Продолжает CBC-цепочку.
|
||
// ВАЖНО: у направления ОДНА цепочка; первый вызов фиксирует режим
|
||
// (шифрование либо расшифровка) — смешение режимов возвращает -1.
|
||
long encrypt(uint8_t* out, size_t out_cap, const uint8_t* in, size_t in_len);
|
||
|
||
// Расшифровывает in (длина кратна 16), снимает завершающие NUL.
|
||
// Возвращает длину открытого текста или -1. Продолжает CBC-цепочку
|
||
// (даже если далее подпись не сойдётся — состояние обязано двигаться).
|
||
long decrypt(uint8_t* out, size_t out_cap, const uint8_t* in, size_t in_len);
|
||
|
||
// HMAC-SHA256(sign_key, data) — подпись/проверка сообщений направления.
|
||
void sign(uint8_t out[kSha256Len], const uint8_t* data, size_t len) const;
|
||
|
||
bool initialized() const { return initialized_; }
|
||
|
||
private:
|
||
static bool derive(const uint8_t* key, size_t key_len, const uint8_t* msg,
|
||
size_t msg_len, uint8_t out[kSha256Len]);
|
||
|
||
bool initialized_ = false;
|
||
uint8_t mode_ = 0; // 0 не зафиксирован, 1 шифрование, 2 расшифровка
|
||
uint8_t sign_key_[kHmacKeyLen];
|
||
uint8_t aes_key_[kAesKeyLen];
|
||
uint8_t chain_iv_[kAesBlockLen]; // текущее состояние CBC-цепочки
|
||
mbedtls_aes_context aes_enc_;
|
||
mbedtls_aes_context aes_dec_;
|
||
};
|
||
|
||
// Пара направлений одной сессии.
|
||
struct SessionCrypto {
|
||
DirectionCrypto app; // приложение -> модуль
|
||
DirectionCrypto dev; // модуль -> приложение
|
||
|
||
// lanip_key — строка base64 КАК ЕСТЬ (utf-8 байты, не декодировать).
|
||
bool init(const char* lanip_key, const char* random_1, const char* random_2,
|
||
int64_t time_1, int64_t time_2);
|
||
};
|
||
|
||
} // namespace fgl::ayla
|