Home IO Control
ESPHome add-on for IO-Homecontrol devices
Loading...
Searching...
No Matches
proto_crypto.h File Reference

Cryptographic helpers for the IO‑Homecontrol protocol. More...

#include "proto_sizes.h"
Include dependency graph for proto_crypto.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Namespaces

namespace  esphome
namespace  esphome::home_io_control
namespace  esphome::home_io_control::crypto

Functions

void esphome::home_io_control::crypto::compute_checksum (uint8_t byte, uint8_t &c1, uint8_t &c2)
 Proprietary checksum algorithm used in IV construction.
void esphome::home_io_control::crypto::construct_iv (const uint8_t *data, uint8_t len, const uint8_t challenge[HMAC_SIZE], uint8_t iv[IV_SIZE])
 Construct the 16-byte initialization vector (IV) for AES encryption.
void esphome::home_io_control::crypto::construct_iv_1w_node (const uint8_t node[NODE_ID_SIZE], uint8_t iv[IV_SIZE])
 Construct the 16-byte IV for the 1W key-wrap primitive from a sender's node address alone.
void esphome::home_io_control::crypto::construct_iv_1w_sequence (const uint8_t *data, uint8_t len, uint16_t sequence, uint8_t iv[IV_SIZE])
 Construct the 16-byte IV for the 1W sequence-keyed HMAC (create_1w_hmac()).
bool esphome::home_io_control::crypto::aes128_encrypt (const uint8_t in[AES_BLOCK_SIZE], const uint8_t key[AES_KEY_SIZE], uint8_t out[AES_BLOCK_SIZE])
 AES-128 ECB encrypt a single 16-byte block.
bool esphome::home_io_control::crypto::aes128_decrypt (const uint8_t in[AES_BLOCK_SIZE], const uint8_t key[AES_KEY_SIZE], uint8_t out[AES_BLOCK_SIZE])
 AES‑128 ECB decrypt a single 16‑byte block.
bool esphome::home_io_control::crypto::create_hmac (const uint8_t *data, uint8_t len, const uint8_t challenge[HMAC_SIZE], const uint8_t key[AES_KEY_SIZE], uint8_t hmac[HMAC_SIZE])
 Create a 6-byte HMAC for authentication (proprietary IO-Homecontrol scheme).
bool esphome::home_io_control::crypto::verify_hmac (const uint8_t *data, uint8_t len, const uint8_t hmac[HMAC_SIZE], const uint8_t challenge[HMAC_SIZE], const uint8_t key[AES_KEY_SIZE])
 Verify a received HMAC using constant-time comparison.
bool esphome::home_io_control::crypto::create_1w_hmac (const uint8_t *data, uint8_t len, uint16_t sequence, const uint8_t controller_key[AES_KEY_SIZE], uint8_t hmac[HMAC_SIZE])
 Create the 6-byte authenticator for a 1W frame.
bool esphome::home_io_control::crypto::crypt_key (const uint8_t *data, uint8_t len, const uint8_t challenge[HMAC_SIZE], const uint8_t in[AES_KEY_SIZE], uint8_t out[AES_KEY_SIZE])
 Encrypt or decrypt a system key during pairing.
bool esphome::home_io_control::crypto::crypt_1w_key (const uint8_t node[NODE_ID_SIZE], const uint8_t in[AES_KEY_SIZE], uint8_t out[AES_KEY_SIZE])
 Encrypt or decrypt a 1W controller key during add-controller key adoption (CMD 0x30).
void esphome::home_io_control::crypto::generate_challenge (uint8_t out[HMAC_SIZE])
 Generate 6 random bytes for a challenge using the ESP32 hardware RNG.

Detailed Description

Cryptographic helpers for the IO‑Homecontrol protocol.

IO‑Homecontrol uses AES‑128 encryption and a proprietary 6‑byte HMAC construction derived from the original Somfy implementation. The "HMAC" here is not a standard HMAC-SHA; it is a custom construction: the IV is built from frame bytes and checksums, then encrypted with the system key via AES‑128‑ECB, and the first 6 bytes are taken.

During pairing, the system key itself is transferred to the device using an XOR‑AES obfuscation with a globally shared transfer key. crypt_key() handles both encryption and decryption; the operation is symmetric.

crypt_1w_key() is a sibling of crypt_key() for the one-way (1W) protocol: it wraps a network key inside a CMD_ONEWAY_ADD_CONTROLLER (0x30) frame with the exact same encrypt-under-TRANSFER_KEY-then-XOR core, differing only in how the IV is built (construct_iv_1w_node() vs. construct_iv()). The two share that core rather than duplicating it — see xor_with_encrypted_iv() in proto_crypto.cpp.

create_1w_hmac() is create_hmac()'s 1W sibling: it authenticates an explicit, command-specific span with a rolling sequence number in place of a random challenge, sharing construct_iv()'s checksum/pad core (construct_iv_prefix() in proto_crypto.cpp) rather than duplicating it — see construct_iv_1w_sequence(). Unlike create_hmac(), it has no default span: the caller must supply exactly the bytes the target command authenticates, which differ per command (see create_1w_hmac()'s warning below).

Warning
The transfer key (TRANSFER_KEY) is hardcoded and public—its only purpose is to obfuscate the system key during over‑the‑air transfer. The security of the installation relies entirely on keeping the system key secret.

Definition in file proto_crypto.h.