Home IO Control
ESPHome add-on for IO-Homecontrol devices
Toggle main menu visibility
Loading...
Searching...
No Matches
pairing_responder.cpp
Go to the documentation of this file.
1
/// @file pairing_responder.cpp
2
/// @brief Pure decision logic for the device-role key-extraction responder.
3
/// @ingroup hioc_hub
4
5
#include "
pairing_responder.h
"
6
7
#include "
proto_commands.h
"
8
9
#include <cstring>
10
11
namespace
esphome
{
12
namespace
home_io_control
{
13
namespace
pairing_responder
{
14
15
const
char
*
responder_stage_name
(
ResponderState
state) {
16
switch
(state) {
17
case
ResponderState::DISARMED
:
18
return
"disarmed"
;
19
case
ResponderState::ARMED_IDLE
:
20
return
"armed_idle"
;
21
case
ResponderState::SENT_DISCOVER_RESP
:
22
return
"sent_discover_resp"
;
23
case
ResponderState::SENT_CONFIRM_ACK
:
24
return
"sent_confirm_ack"
;
25
case
ResponderState::SENT_CHALLENGE
:
26
return
"sent_challenge"
;
27
case
ResponderState::EXTRACTED
:
28
return
"extracted"
;
29
case
ResponderState::SENT_NODE_VERIFY_RESP
:
30
return
"sent_node_verify_resp"
;
31
}
32
return
"disarmed"
;
33
}
34
35
bool
on_discover_request
(
ResponderContext
&ctx,
const
uint8_t hub_node_id[NODE_ID_SIZE]) {
36
if
(ctx.
state
==
ResponderState::ARMED_IDLE
|| ctx.
state
==
ResponderState::SENT_DISCOVER_RESP
) {
37
ctx.
state
=
ResponderState::SENT_DISCOVER_RESP
;
38
return
true
;
39
}
40
// EXTRACTED/SENT_NODE_VERIFY_RESP are a *completed* attempt, not an in-flight one — unlike
41
// SENT_CONFIRM_ACK above, a fresh 0x28 arriving here cannot be the same hub's redundant
42
// mid-exchange rebroadcast (the only documented real-hub case for staying silent; see the
43
// doxygen), so it is treated like ARMED_IDLE: start a new attempt. But ONLY for the hub this
44
// responder actually extracted a key from — 0x28 is a broadcast handled before the throwaway-ID
45
// dst filter, so accepting it unconditionally here would let any unrelated hub's ordinary 0x28
46
// traffic knock a live post-extraction node-verification round with the real hub back to
47
// SENT_DISCOVER_RESP. A different hub's 0x28 is silently ignored instead.
48
if
(ctx.
state
==
ResponderState::EXTRACTED
|| ctx.
state
==
ResponderState::SENT_NODE_VERIFY_RESP
) {
49
if
(memcmp(hub_node_id, ctx.
hub_node_id
, NODE_ID_SIZE) != 0)
50
return
false
;
51
ctx.
state
=
ResponderState::SENT_DISCOVER_RESP
;
52
return
true
;
53
}
54
return
false
;
55
}
56
57
bool
on_discover_confirm
(
ResponderContext
&ctx) {
58
if
(ctx.
state
!=
ResponderState::SENT_DISCOVER_RESP
&& ctx.
state
!=
ResponderState::SENT_CONFIRM_ACK
)
59
return
false
;
60
ctx.
state
=
ResponderState::SENT_CONFIRM_ACK
;
61
return
true
;
62
}
63
64
bool
on_key_init
(
ResponderContext
&ctx,
const
uint8_t challenge[HMAC_SIZE],
const
uint8_t hub_node_id[NODE_ID_SIZE]) {
65
// Both pre-key-exchange states are accepted: a hub that insists on our 0x2D arrives here from
66
// SENT_CONFIRM_ACK, one that gives up waiting for it arrives straight from SENT_DISCOVER_RESP.
67
if
(ctx.
state
==
ResponderState::SENT_DISCOVER_RESP
|| ctx.
state
==
ResponderState::SENT_CONFIRM_ACK
) {
68
memcpy(ctx.
challenge
, challenge, HMAC_SIZE);
69
memcpy(ctx.
hub_node_id
, hub_node_id, NODE_ID_SIZE);
70
ctx.
state
=
ResponderState::SENT_CHALLENGE
;
71
return
true
;
72
}
73
// Retry: the hub already got past this phase once — keep the previously-stored challenge and
74
// hub node ID, just resend the same 0x3C (see doxygen for why regenerating would be wrong).
75
return
ctx.
state
==
ResponderState::SENT_CHALLENGE
;
76
}
77
78
bool
on_key_transfer
(
ResponderContext
&ctx,
const
uint8_t transfer_payload[AES_KEY_SIZE]) {
79
if
(ctx.
state
!=
ResponderState::SENT_CHALLENGE
)
80
return
false
;
81
if
(!
recover_system_key_from_transfer
(transfer_payload, ctx.
challenge
, ctx.
recovered_key
))
82
return
false
;
83
ctx.
state
=
ResponderState::EXTRACTED
;
84
return
true
;
85
}
86
87
bool
on_node_verify_req
(
ResponderContext
&ctx) {
88
if
(ctx.
state
!=
ResponderState::EXTRACTED
&& ctx.
state
!=
ResponderState::SENT_NODE_VERIFY_RESP
)
89
return
false
;
90
ctx.
state
=
ResponderState::SENT_NODE_VERIFY_RESP
;
91
return
true
;
92
}
93
94
bool
on_node_verify_challenge
(
const
ResponderContext
&ctx) {
95
return
ctx.
state
==
ResponderState::SENT_NODE_VERIFY_RESP
;
96
}
97
98
}
// namespace pairing_responder
99
}
// namespace home_io_control
100
}
// namespace esphome
esphome::home_io_control::pairing_responder
Definition
pairing_responder.cpp:13
esphome::home_io_control::pairing_responder::on_key_transfer
bool on_key_transfer(ResponderContext &ctx, const uint8_t transfer_payload[AES_KEY_SIZE])
Decide how to react to an inbound CMD_KEY_TRANSFER (0x32) while armed.
Definition
pairing_responder.cpp:78
esphome::home_io_control::pairing_responder::responder_stage_name
const char * responder_stage_name(ResponderState state)
Get a short, log/telemetry-friendly name for a responder state.
Definition
pairing_responder.cpp:15
esphome::home_io_control::pairing_responder::on_discover_confirm
bool on_discover_confirm(ResponderContext &ctx)
Decide how to react to an inbound CMD_DISCOVER_CONFIRM (0x2C) addressed to our throwaway ID.
Definition
pairing_responder.cpp:57
esphome::home_io_control::pairing_responder::on_node_verify_req
bool on_node_verify_req(ResponderContext &ctx)
Decide how to react to an inbound CMD_NODE_VERIFY_REQ (0x36) addressed to our throwaway ID.
Definition
pairing_responder.cpp:87
esphome::home_io_control::pairing_responder::on_key_init
bool on_key_init(ResponderContext &ctx, const uint8_t challenge[HMAC_SIZE], const uint8_t hub_node_id[NODE_ID_SIZE])
Decide how to react to an inbound CMD_KEY_INIT (0x31) addressed to our throwaway ID.
Definition
pairing_responder.cpp:64
esphome::home_io_control::pairing_responder::on_node_verify_challenge
bool on_node_verify_challenge(const ResponderContext &ctx)
Decide how to react to an inbound CMD_CHALLENGE_REQ (0x3C) — issued by the hub this time,...
Definition
pairing_responder.cpp:94
esphome::home_io_control::pairing_responder::ResponderState
ResponderState
State machine for the device-role key-extraction responder.
Definition
pairing_responder.h:30
esphome::home_io_control::pairing_responder::ResponderState::ARMED_IDLE
@ ARMED_IDLE
Armed, listening for a discovery request (0x28).
Definition
pairing_responder.h:32
esphome::home_io_control::pairing_responder::ResponderState::SENT_DISCOVER_RESP
@ SENT_DISCOVER_RESP
Replied to discovery (0x29); waiting for discovery-confirm (0x2C) or key-init (0x31).
Definition
pairing_responder.h:33
esphome::home_io_control::pairing_responder::ResponderState::SENT_CONFIRM_ACK
@ SENT_CONFIRM_ACK
Acknowledged discovery-confirm (0x2D); waiting for key-init (0x31).
Definition
pairing_responder.h:34
esphome::home_io_control::pairing_responder::ResponderState::DISARMED
@ DISARMED
Not armed; 0x28/0x2C/0x31/0x32 traffic is ignored.
Definition
pairing_responder.h:31
esphome::home_io_control::pairing_responder::ResponderState::SENT_NODE_VERIFY_RESP
@ SENT_NODE_VERIFY_RESP
Answered a hub's CMD_NODE_VERIFY_REQ (0x36) with our CMD_NODE_VERIFY_RESP (0x37); waiting for the hub...
Definition
pairing_responder.h:39
esphome::home_io_control::pairing_responder::ResponderState::SENT_CHALLENGE
@ SENT_CHALLENGE
Replied to key-init with our challenge (0x3C); waiting for key-transfer (0x32).
Definition
pairing_responder.h:35
esphome::home_io_control::pairing_responder::ResponderState::EXTRACTED
@ EXTRACTED
System key recovered from a valid 0x32.
Definition
pairing_responder.h:36
esphome::home_io_control::pairing_responder::on_discover_request
bool on_discover_request(ResponderContext &ctx, const uint8_t hub_node_id[NODE_ID_SIZE])
Decide how to react to an inbound CMD_DISCOVER_REQ (0x28) while armed.
Definition
pairing_responder.cpp:35
esphome::home_io_control
Definition
device_registry.cpp:13
esphome::home_io_control::recover_system_key_from_transfer
bool recover_system_key_from_transfer(const uint8_t transfer_payload[AES_KEY_SIZE], const uint8_t challenge[HMAC_SIZE], uint8_t out_key[AES_KEY_SIZE])
Recover the system key from a CMD_KEY_TRANSFER payload.
Definition
proto_commands.cpp:753
esphome
Definition
device_registry.cpp:12
pairing_responder.h
Pure decision logic for the device-role "Accept Foreign Pairing" (system-key extraction) responder.
proto_commands.h
Command builders for the IO‑Homecontrol protocol.
esphome::home_io_control::pairing_responder::ResponderContext
Context for one key-extraction arm cycle.
Definition
pairing_responder.h:56
esphome::home_io_control::pairing_responder::ResponderContext::state
ResponderState state
Current state.
Definition
pairing_responder.h:57
esphome::home_io_control::pairing_responder::ResponderContext::hub_node_id
uint8_t hub_node_id[NODE_ID_SIZE]
Foreign hub's real node ID, captured from the 0x31's src.
Definition
pairing_responder.h:62
esphome::home_io_control::pairing_responder::ResponderContext::challenge
uint8_t challenge[HMAC_SIZE]
Our challenge, generated on the first 0x31 of an attempt.
Definition
pairing_responder.h:61
esphome::home_io_control::pairing_responder::ResponderContext::recovered_key
uint8_t recovered_key[AES_KEY_SIZE]
Recovered system key; valid once state == EXTRACTED.
Definition
pairing_responder.h:63
components
home_io_control
pairing_responder.cpp
Generated by
1.18.0