// Сессионная криптография 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 #include #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