1. Тема дня

Сегодня разбираем защищённую связь устройства с сервером:

text
TCP
→ TLS handshake
→ проверка сертификата сервера
→ optional client certificate
→ зашифрованный MQTT/HTTPS

Главная мысль: TLS защищает соединение только тогда, когда устройство проверяет не просто наличие шифрования, а подлинность сервера, hostname и срок действия сертификата.

2. Зачем это нужно в твоих проектах

Через LTE передаются phase events, fault snapshots, настройки, OTA manifest и команды управления. Без корректной проверки TLS устройство может подключиться к поддельному broker и принять вредную команду. Для OTA ошибка в CA/hostname может открыть путь к подмене образа или лишить весь парк возможности обновляться. Для STM32 с Ethernet/Wi-Fi/PPP архитектура аналогична:

text
LwIP / NetX Duo
→ Mbed TLS
→ CA store
→ optional mTLS
→ MQTT/HTTPS

3. Теория

7.3.1 3.1. Что TLS защищает

text
Конфиденциальность:
  посторонний не читает payload.
Целостность:
  пакет нельзя незаметно изменить.
Аутентификация:
  клиент проверяет, что сервер настоящий.

При mTLS сервер дополнительно проверяет сертификат устройства.

7.3.2 3.2. Обычный TLS и mTLS

Обычный TLS:

text
ESP32 → ClientHello + SNI → Broker
ESP32 ← server certificate chain ← Broker
ESP32 проверяет CA, hostname, time

mTLS:

text
устройство проверяет server certificate
сервер проверяет client certificate устройства

7.3.3 3.3. Certificate chain

text
Root CA
  └── Intermediate CA
        └── mqtt.example.com

Устройство обычно хранит Root CA или ограниченный набор CA, а не leaf-сертификат broker.

7.3.4 3.4. CA bundle или свой CA

CA bundle удобен для публичных endpoints, но доверяет большому набору CA и занимает Flash. Собственный CA сужает trust boundary, но требует защищённого CA private key, процедуры выпуска, ротации и отзыва.

7.3.5 3.5. Hostname verification и SNI

CA verification отвечает:

text
сертификат подписан доверенным CA?

Hostname verification отвечает:

text
сертификат выпущен именно для mqtt.example.com?

Отключать hostname verification в production нельзя.

7.3.6 3.6. TLS зависит от времени

X.509-сертификаты имеют Not Before и Not After. Поэтому устройство должно иметь достаточно правдоподобный UTC. Bootstrap strategy:

text
1. Загрузить last-known-good UTC из RTC/NVS.
2. Не уменьшать его ниже последнего подтверждённого значения.
3. Использовать время сети EC25 только как подсказку.
4. Проверить разумный год/диапазон.
5. Установить TLS.
6. Получить более надёжную синхронизацию.
7. Обновить last-known-good time.

7.3.7 3.7. Где завершать TLS: ESP32 или EC25

TLS на ESP32 через PPPoS:

text
EC25 → PPP → lwIP → ESP-TLS/Mbed TLS → MQTT/HTTPS

Плюсы: единый TLS-стек, лучшая диагностика, единый CA store, меньше зависимость от AT- прошивки. Минусы: расход RAM/CPU ESP32, нагрузка на UART PPP. TLS внутри EC25:

text
ESP32 → AT commands → EC25 TLS/MQTT stack → broker

Плюсы: TLS считает модем, меньше heap ESP32. Минусы: сложнее сертификаты, диагностика, ротация, зависимость от firmware модема.

7.3.8 3.8. Жизненный цикл сертификатов

c
typedef enum {
    CERT_STATE_EMPTY = 0,
    CERT_STATE_STAGED,
    CERT_STATE_ACTIVE,
    CERT_STATE_RENEWAL_REQUIRED,
    CERT_STATE_REVOKED,
    CERT_STATE_EXPIRED,
} certificate_state_t;
typedef struct {
    uint32_t generation;
    certificate_state_t state;
    int64_t not_before_utc;
    int64_t not_after_utc;
    uint8_t fingerprint_sha256[32];
    uint32_t connect_success_count;
    uint32_t connect_fail_count;
} certificate_status_t;

7.3.9 3.9. Ротация CA

Безопасно:

text
CA_OLD
→ firmware доверяет CA_OLD + CA_NEW
→ сервер переходит на CA_NEW
→ парк подтверждает подключение
→ удалить CA_OLD позже

Опасно:

text
сервер перешёл на CA_NEW
→ старые устройства знают только CA_OLD
→ парк offline

7.3.10 3.10. Диагностика TLS

c
typedef enum {
    TLS_STAGE_NONE = 0,
    TLS_STAGE_DNS,
    TLS_STAGE_TCP_CONNECT,
    TLS_STAGE_CLIENT_HELLO,
    TLS_STAGE_CERT_VERIFY,
    TLS_STAGE_CLIENT_AUTH,
    TLS_STAGE_ESTABLISHED,
} tls_stage_t;
typedef struct {
    tls_stage_t last_stage;
    int32_t last_tls_error;
    int32_t last_verify_flags;
    uint32_t handshake_ok;
    uint32_t handshake_failed;
    uint32_t hostname_failed;
    uint32_t time_failed;
    uint32_t client_auth_failed;
    uint32_t max_handshake_ms;
    uint32_t min_free_heap_during_handshake;
    uint32_t certificate_generation;
} tls_diag_t;

4. Типичные ошибки

text
1. Отключить server verification.
2. Проверять CA, но отключить hostname.
3. Один client certificate на весь парк.
4. Не иметь времени до TLS.
5. Шить leaf certificate сервера навсегда.
6. Переключить CA на сервере раньше OTA устройств.
7. Хранить private key в Git.
8. Печатать PEM/DER в debug log.
9. Не учитывать lifetime буферов сертификатов.
10. Перепутать TLS termination: ESP32 и EC25 одновременно.
11. Не измерять peak heap TLS handshake.

5. Практическое задание на 30-60 минут

Создай TLS_PKI_POLICY.md:

markdown
# TLS / PKI policy
1. All external MQTT and OTA connections use TLS.
2. Server certificate and hostname are always verified.
3. Insecure verification is forbidden in production.
4. Each device has a unique identity.
5. Private keys are never stored in Git or logs.
6. UTC quality is checked before certificate validation.
7. CA rotation uses an old+new overlap window.
8. Client certificate rotation is transactional.
9. TLS errors are classified, not retried blindly.
10. Certificate expiry is monitored before deployment.

Сделай inventory endpoints:

markdown
| Endpoint | Protocol | Authentication | Trust source |
|---|---|---|---|
| mqtt.example.com | MQTT/TLS | mTLS | private CA |
| ota.example.com | HTTPS | server TLS | CA bundle |
| api.example.com | HTTPS | token + TLS | CA bundle |

Добавь CLI tls probe, который проверяет DNS/TCP/TLS/hostname/time/heap и не передаёт application secrets.

6. Что почитать дальше

  • ESP-TLS, X.509 Certificate Bundle, ESP-MQTT TLS examples.
  • Quectel EC25 SSL/MQTT/HTTP(S) application notes.
  • STM32 Mbed TLS integration и STSAFE.

Короткий итог

text
external endpoint
→ DNS
→ TCP
→ TLS handshake
→ CA chain verification
→ hostname/SNI verification
→ optional client certificate
→ encrypted MQTT/HTTPS
→ monitored certificate lifecycle

Задание

Server certificate подписан trusted CA, но выдан другому hostname. Задайте connection decision и безопасные diagnostic information.

Критерии самопроверки: Сохранить hostname verification и отличать authentication failure от TCP или DNS failure.

Показать ответ автора

Отклонить connection: CA trust не подтверждает нужный hostname. Записать ограниченную verification category, endpoint identity, time quality и handshake status без private keys, PEM/DER material и application secrets.

Задание

Спланируйте переход CA_OLD к CA_NEW для устройств, которые могут быть offline при deployment. Задайте порядок сохранения authenticated update path.

Критерии самопроверки: Описать evidence каждого этапа и offline-device recovery path; не решать rotation отключением verification.

Показать ответ автора

Сначала развернуть firmware с доверием к CA_OLD и CA_NEW и проверить adoption устройств. Затем переключить server на CA_NEW и наблюдать connections; удалить CA_OLD позже по контролируемой policy с учётом устройств, пропустивших переход.