Files
fgl-aircon/src/ayla/crypto.hpp
Petr Polezhaev e91d4605b1 core(M1): ayla-криптография, конверт, JSON (jsmn), HTTP-клиент
- 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.
2026-09-22 15:46:18 +03:00

71 lines
3.5 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Сессионная криптография 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