Разработайте протокол безопасного обмена ключами для IoT-устройств с ограниченными вычислительными ресурсами и объясните выбор криптографических примитивов, учёт отказоустойчивости и механизм обновления ключей
Кратко: предлагаю лёгкий, защищённый протокол обмена ключами с раздельной фазой комиссионирования (bootstrap) и сессий, основанный на X25519 для ECDH, Ed25519 для подписи/аттестации (по необходимости), HKDF-SHA256 (или BLAKE2s-HKDF) для вывода ключей и ChaCha20-Poly1305 для AEAD. Ниже — набор шагов, ключевые формулы, причины выбора примитивов, механизмы отказоустойчивости и обновления ключей. 1) Выбор криптопримитивов и мотивация - Ассиметрия: X25519 (компактные ключи, быстрый ECDH, низкие требования к памяти). Подписи — Ed25519 при необходимости аттестации/сертификации. - KDF/MAC: HKDF-SHA256 (широко проверен), опционально BLAKE2s для более высокой скорости на малых CPU. - Симметричное шифрование: ChaCha20-Poly1305 (быстрее AES без AESHW; малый код). - RNG: CSPRNG, seed единожды при производстве/комиссионировании. - Протокольный каркас: паттерн Noise или EDHOC-подобный обмен (облегчённая реализация для экономии кода). 2) Фазы протокола (сообщения + формулы) A. Комиссионирование (bootstrap) — один раз при вводе в сеть - Предположим у устройства D статический ключ dskd_{sk}dsk и публичный DpkD_{pk}Dpk; у сервера/контроллера S — ssk,Spks_{sk}, S_{pk}ssk,Spk. - Сообщения: 1. D -> S: IDD,Dpk,nD\text{ID}_D, D_{pk}, n_DIDD,Dpk,nD (nonce) 2. S -> D: IDS,Spk,nS,σS\text{ID}_S, S_{pk}, n_S, \sigma_SIDS,Spk,nS,σS где σS=Sign(ssk,IDD∣∣Dpk∣∣nD∣∣nS)\sigma_S=\text{Sign}(s_{sk}, \text{ID}_D||D_{pk}||n_D||n_S)σS=Sign(ssk,IDD∣∣Dpk∣∣nD∣∣nS)
- Проверка подписи S на стороне D подтверждает подлинность контроллера. - ECDH-общая секрета: Z=X25519(dsk,Spk)
Z = \text{X25519}(d_{sk}, S_{pk}) Z=X25519(dsk,Spk)
- Вывод корневого ключа: Kroot=HKDF-Extract/Expand(Z, salt=nD∣∣nS, info="IoT-root")
K_{\text{root}} = \text{HKDF-Extract/Expand}(Z,\ \text{salt}=n_D||n_S,\ \text{info}=\text{"IoT-root"}) Kroot=HKDF-Extract/Expand(Z,salt=nD∣∣nS,info="IoT-root")
- Из KrootK_{\text{root}}Kroot выводятся ключи: Kenc,Kmac,KauthK_{\text{enc}}, K_{\text{mac}}, K_{\text{auth}}Kenc,Kmac,Kauth через HKDF-Expand. B. Установка сессии (ежесессийный ключ с PFS) - Для PFS устройство генерирует эпhemeral eske_{sk}esk и отправляет EpkE_{pk}Epk и nonce nsn_{s}ns. - Стороны вычисляют: Zs=X25519(esk,Spk)(ephemeral-static)
Z_s = \text{X25519}(e_{sk}, S_{pk}) \quad\text{(ephemeral-static)} Zs=X25519(esk,Spk)(ephemeral-static)Ksession=HKDF(Kroot ∥ Zs, salt=ns, info="session")
K_{\text{session}} = \text{HKDF}(K_{\text{root}} \,\|\, Z_s,\ \text{salt}=n_s,\ \text{info}=\text{"session"}) Ksession=HKDF(Kroot∥Zs,salt=ns,info="session")
- Сессионные ключи используются только для короткой сессии и удаляются после выхода. AEAD-сообщения защищаются ChaCha20-Poly1305 с associated data = IDD∣∣IDS∣∣seq\text{ID}_D||\text{ID}_S||\text{seq}IDD∣∣IDS∣∣seq. C. Формат защищённого пакета - Ciphertext = AEAD_Encrypt(KencK_{enc}Kenc, nonce, plaintext; AD = IDD∣∣IDS∣∣seq\text{ID}_D||\text{ID}_S||\text{seq}IDD∣∣IDS∣∣seq). - seq — monotonically increasing counter (защита от реплея). 3) Обновление ключей (rekey) - Два уровня: 1. Короткоживущие сессионные ключи — обновляются каждую сессию (генерация ephemeral). 2. Корневой ключ KrootK_{\text{root}}Kroot — периодическое обновление или по команде сервера. - Рекомендуемая процедура корневого rekey (инициируется сервером либо устройством): - Сервер создаёт nonce nrn_rnr и формирует сообщение (подписанное или MAC): Содержимое: {version′,nr}\{\text{version}', n_r\}{version′,nr} зашифровано/аутентифицировано старым KrootK_{\text{root}}Kroot. - Устройство получает, проверяет MAC/подпись и вычисляет: Kroot′=HKDF(Kroot, salt=nr, info="rekey"∣∣version′)
K'_{\text{root}} = \text{HKDF}(K_{\text{root}},\ \text{salt}=n_r,\ \text{info}=\text{"rekey"}||\text{version}') Kroot′=HKDF(Kroot,salt=nr,info="rekey"∣∣version′)
- Atomic commit: писать Kroot′K'_{\text{root}}Kroot′ в «инертный» слот; после успешной записи переключить указатель активного слота и увеличить версию/счётчик. - Для отката/отказов используется двухслотовая схема (active/inactive) с флагом commit. 4) Отказоустойчивость и защита от сбоев - Atomic update: хранить ключи в двух слотах; писать новый ключ в неактивный слот, проверка целостности (MAC), затем атомарно менять pointer (в энергонезависимой памяти — одно записываемое слово). - Power-loss во время update: при загрузке читать оба слота и выбирать тот, который валиден и имеет максимальную версию; если оба невалидны — откат к заводскому ключу или инициировать secure bootstrap от сервера (см. recovery). - Анти-откат: версия/счётчик в сообщениях rekey и подпись сервера; устройство отвергает меньшие версии. - Ограничение попыток (rate-limiting) и счётчики неуспешных попыток для защиты от DoS/брутфорса. - Жёсткая проверка подписи/МАК до записи ключей. 5) Восстановление и ревокация - Ревокация: сервер хранит список отозванных ID/публичных ключей; при соединении сервер отбрасывает устройства с ревокированными ключами. При нарушении приватного ключа — пересоздание KrootK_{\text{root}}Kroot через out-of-band (физическое вмешательство) или через аутентификацию устройством (аттестация). - Recovery: предусмотреть заводской резервный ключ/attestation key или одноразовый provisioning token для повторного комиссионирования. 6) Дополнительные соображения по реализации (ограниченные ресурсы) - Минимизировать число подпись/верификаций на устройстве: подписывать сообщения сервера, а на устройстве только проверять подпись при bootstrap/rekey (не каждый пакет). - Хранение ключей: компактные форматы для ключей (32 байта для X25519/Ed25519). - Использовать CBOR/COSE или компактный TLV вместо XML/JSON. - Не использовать тяжёлые PKI-цепочки на устройстве — проверять подпись производителя или использовать короткие сертификаты/raw public key. - Включить защиту от побочных каналов и соблюдение CSPRNG правил. 7) Безопасность — что обеспечивается - Аутентификация контроллера (подпись S) в комиссионировании. - Конфиденциальность и целостность — ChaCha20-Poly1305 AEAD. - PFS — за счёт эпhemeral DH при каждой сессии. - Защита от реплея — nonce/sequence и AEAD-associated data. - Обновляемость ключей с атомарным коммитом и защитой от отката. Короткая сводка формул - ECDH: Z=X25519(dsk,Spk)Z=\text{X25519}(d_{sk},S_{pk})Z=X25519(dsk,Spk)
- Корень: Kroot=HKDF(Z, salt=nD∣∣nS, info="IoT-root")K_{\text{root}}=\text{HKDF}(Z,\ \text{salt}=n_D||n_S,\ \text{info}=\text{"IoT-root"})Kroot=HKDF(Z,salt=nD∣∣nS,info="IoT-root")
- Сессия: Zs=X25519(esk,Spk)Z_s=\text{X25519}(e_{sk},S_{pk})Zs=X25519(esk,Spk), Ksession=HKDF(Kroot ∥ Zs, salt=ns, info="session")K_{\text{session}}=\text{HKDF}(K_{\text{root}}\,\|\,Z_s,\ \text{salt}=n_s,\ \text{info}=\text{"session"})Ksession=HKDF(Kroot∥Zs,salt=ns,info="session")
- Rekey: Kroot′=HKDF(Kroot, salt=nr, info="rekey"∣∣version′)K'_{\text{root}}=\text{HKDF}(K_{\text{root}},\ \text{salt}=n_r,\ \text{info}=\text{"rekey"}||\text{version}')Kroot′=HKDF(Kroot,salt=nr,info="rekey"∣∣version′) Если нужно — могу дать компактный описание сообщений в формате CBOR/COSE или пример на основе конкретного лёгкого паттерна Noise (например, Noise_XKpsk2) для дальнейшей реализации.
1) Выбор криптопримитивов и мотивация
- Ассиметрия: X25519 (компактные ключи, быстрый ECDH, низкие требования к памяти). Подписи — Ed25519 при необходимости аттестации/сертификации.
- KDF/MAC: HKDF-SHA256 (широко проверен), опционально BLAKE2s для более высокой скорости на малых CPU.
- Симметричное шифрование: ChaCha20-Poly1305 (быстрее AES без AESHW; малый код).
- RNG: CSPRNG, seed единожды при производстве/комиссионировании.
- Протокольный каркас: паттерн Noise или EDHOC-подобный обмен (облегчённая реализация для экономии кода).
2) Фазы протокола (сообщения + формулы)
A. Комиссионирование (bootstrap) — один раз при вводе в сеть
- Предположим у устройства D статический ключ dskd_{sk}dsk и публичный DpkD_{pk}Dpk ; у сервера/контроллера S — ssk,Spks_{sk}, S_{pk}ssk ,Spk .
- Сообщения:
1. D -> S: IDD,Dpk,nD\text{ID}_D, D_{pk}, n_DIDD ,Dpk ,nD (nonce)
2. S -> D: IDS,Spk,nS,σS\text{ID}_S, S_{pk}, n_S, \sigma_SIDS ,Spk ,nS ,σS где σS=Sign(ssk,IDD∣∣Dpk∣∣nD∣∣nS)\sigma_S=\text{Sign}(s_{sk}, \text{ID}_D||D_{pk}||n_D||n_S)σS =Sign(ssk ,IDD ∣∣Dpk ∣∣nD ∣∣nS ) - Проверка подписи S на стороне D подтверждает подлинность контроллера.
- ECDH-общая секрета:
Z=X25519(dsk,Spk) Z = \text{X25519}(d_{sk}, S_{pk})
Z=X25519(dsk ,Spk ) - Вывод корневого ключа:
Kroot=HKDF-Extract/Expand(Z, salt=nD∣∣nS, info="IoT-root") K_{\text{root}} = \text{HKDF-Extract/Expand}(Z,\ \text{salt}=n_D||n_S,\ \text{info}=\text{"IoT-root"})
Kroot =HKDF-Extract/Expand(Z, salt=nD ∣∣nS , info="IoT-root") - Из KrootK_{\text{root}}Kroot выводятся ключи: Kenc,Kmac,KauthK_{\text{enc}}, K_{\text{mac}}, K_{\text{auth}}Kenc ,Kmac ,Kauth через HKDF-Expand.
B. Установка сессии (ежесессийный ключ с PFS)
- Для PFS устройство генерирует эпhemeral eske_{sk}esk и отправляет EpkE_{pk}Epk и nonce nsn_{s}ns .
- Стороны вычисляют:
Zs=X25519(esk,Spk)(ephemeral-static) Z_s = \text{X25519}(e_{sk}, S_{pk}) \quad\text{(ephemeral-static)}
Zs =X25519(esk ,Spk )(ephemeral-static) Ksession=HKDF(Kroot ∥ Zs, salt=ns, info="session") K_{\text{session}} = \text{HKDF}(K_{\text{root}} \,\|\, Z_s,\ \text{salt}=n_s,\ \text{info}=\text{"session"})
Ksession =HKDF(Kroot ∥Zs , salt=ns , info="session") - Сессионные ключи используются только для короткой сессии и удаляются после выхода. AEAD-сообщения защищаются ChaCha20-Poly1305 с associated data = IDD∣∣IDS∣∣seq\text{ID}_D||\text{ID}_S||\text{seq}IDD ∣∣IDS ∣∣seq.
C. Формат защищённого пакета
- Ciphertext = AEAD_Encrypt(KencK_{enc}Kenc , nonce, plaintext; AD = IDD∣∣IDS∣∣seq\text{ID}_D||\text{ID}_S||\text{seq}IDD ∣∣IDS ∣∣seq).
- seq — monotonically increasing counter (защита от реплея).
3) Обновление ключей (rekey)
- Два уровня:
1. Короткоживущие сессионные ключи — обновляются каждую сессию (генерация ephemeral).
2. Корневой ключ KrootK_{\text{root}}Kroot — периодическое обновление или по команде сервера.
- Рекомендуемая процедура корневого rekey (инициируется сервером либо устройством):
- Сервер создаёт nonce nrn_rnr и формирует сообщение (подписанное или MAC):
Содержимое: {version′,nr}\{\text{version}', n_r\}{version′,nr } зашифровано/аутентифицировано старым KrootK_{\text{root}}Kroot .
- Устройство получает, проверяет MAC/подпись и вычисляет:
Kroot′=HKDF(Kroot, salt=nr, info="rekey"∣∣version′) K'_{\text{root}} = \text{HKDF}(K_{\text{root}},\ \text{salt}=n_r,\ \text{info}=\text{"rekey"}||\text{version}')
Kroot′ =HKDF(Kroot , salt=nr , info="rekey"∣∣version′) - Atomic commit: писать Kroot′K'_{\text{root}}Kroot′ в «инертный» слот; после успешной записи переключить указатель активного слота и увеличить версию/счётчик.
- Для отката/отказов используется двухслотовая схема (active/inactive) с флагом commit.
4) Отказоустойчивость и защита от сбоев
- Atomic update: хранить ключи в двух слотах; писать новый ключ в неактивный слот, проверка целостности (MAC), затем атомарно менять pointer (в энергонезависимой памяти — одно записываемое слово).
- Power-loss во время update: при загрузке читать оба слота и выбирать тот, который валиден и имеет максимальную версию; если оба невалидны — откат к заводскому ключу или инициировать secure bootstrap от сервера (см. recovery).
- Анти-откат: версия/счётчик в сообщениях rekey и подпись сервера; устройство отвергает меньшие версии.
- Ограничение попыток (rate-limiting) и счётчики неуспешных попыток для защиты от DoS/брутфорса.
- Жёсткая проверка подписи/МАК до записи ключей.
5) Восстановление и ревокация
- Ревокация: сервер хранит список отозванных ID/публичных ключей; при соединении сервер отбрасывает устройства с ревокированными ключами. При нарушении приватного ключа — пересоздание KrootK_{\text{root}}Kroot через out-of-band (физическое вмешательство) или через аутентификацию устройством (аттестация).
- Recovery: предусмотреть заводской резервный ключ/attestation key или одноразовый provisioning token для повторного комиссионирования.
6) Дополнительные соображения по реализации (ограниченные ресурсы)
- Минимизировать число подпись/верификаций на устройстве: подписывать сообщения сервера, а на устройстве только проверять подпись при bootstrap/rekey (не каждый пакет).
- Хранение ключей: компактные форматы для ключей (32 байта для X25519/Ed25519).
- Использовать CBOR/COSE или компактный TLV вместо XML/JSON.
- Не использовать тяжёлые PKI-цепочки на устройстве — проверять подпись производителя или использовать короткие сертификаты/raw public key.
- Включить защиту от побочных каналов и соблюдение CSPRNG правил.
7) Безопасность — что обеспечивается
- Аутентификация контроллера (подпись S) в комиссионировании.
- Конфиденциальность и целостность — ChaCha20-Poly1305 AEAD.
- PFS — за счёт эпhemeral DH при каждой сессии.
- Защита от реплея — nonce/sequence и AEAD-associated data.
- Обновляемость ключей с атомарным коммитом и защитой от отката.
Короткая сводка формул
- ECDH: Z=X25519(dsk,Spk)Z=\text{X25519}(d_{sk},S_{pk})Z=X25519(dsk ,Spk ) - Корень: Kroot=HKDF(Z, salt=nD∣∣nS, info="IoT-root")K_{\text{root}}=\text{HKDF}(Z,\ \text{salt}=n_D||n_S,\ \text{info}=\text{"IoT-root"})Kroot =HKDF(Z, salt=nD ∣∣nS , info="IoT-root") - Сессия: Zs=X25519(esk,Spk)Z_s=\text{X25519}(e_{sk},S_{pk})Zs =X25519(esk ,Spk ), Ksession=HKDF(Kroot ∥ Zs, salt=ns, info="session")K_{\text{session}}=\text{HKDF}(K_{\text{root}}\,\|\,Z_s,\ \text{salt}=n_s,\ \text{info}=\text{"session"})Ksession =HKDF(Kroot ∥Zs , salt=ns , info="session") - Rekey: Kroot′=HKDF(Kroot, salt=nr, info="rekey"∣∣version′)K'_{\text{root}}=\text{HKDF}(K_{\text{root}},\ \text{salt}=n_r,\ \text{info}=\text{"rekey"}||\text{version}')Kroot′ =HKDF(Kroot , salt=nr , info="rekey"∣∣version′)
Если нужно — могу дать компактный описание сообщений в формате CBOR/COSE или пример на основе конкретного лёгкого паттерна Noise (например, Noise_XKpsk2) для дальнейшей реализации.