mirror of
https://github.com/esphome/esphome.git
synced 2026-10-07 11:26:39 +00:00
[noise] Generate the responder ephemeral key ahead of the handshake (#19000)
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import esphome.codegen as cg
|
||||
from tests.testing_helpers import ComponentManifestOverride
|
||||
|
||||
|
||||
@@ -5,3 +6,10 @@ def override_manifest(manifest: ComponentManifestOverride) -> None:
|
||||
# to_code must run: it defines USE_NOISE and adds the noise-c library
|
||||
# the component sources under test need.
|
||||
manifest.enable_codegen()
|
||||
real_to_code = manifest.to_code
|
||||
|
||||
async def to_code_testing(config):
|
||||
await real_to_code(config)
|
||||
cg.add_define("USE_NOISE_SPARE_EPHEMERAL")
|
||||
|
||||
manifest.to_code = to_code_testing
|
||||
|
||||
@@ -157,6 +157,72 @@ TEST(NoiseResponderHandshakeTest, FullHandshakeAndTransportRoundTrip) {
|
||||
noise_cipherstate_free(recv_cipher);
|
||||
}
|
||||
|
||||
// One full NNpsk0 handshake; responder_e gets the ephemeral public key the responder put on the wire, read
|
||||
// before the initiator consumes the buffer in place
|
||||
static void run_handshake(NoiseResponderHandshake &responder, uint8_t responder_e[SPARE_EPHEMERAL_KEY_SIZE]) {
|
||||
const psk_t psk = make_psk(7);
|
||||
ASSERT_EQ(responder.init(ctx_for(psk), PROLOGUE, sizeof(PROLOGUE)), 0);
|
||||
Initiator initiator(psk, PROLOGUE, sizeof(PROLOGUE));
|
||||
uint8_t msg[MAX_HANDSHAKE_SIZE];
|
||||
size_t msg_len = initiator.write_message(msg, sizeof(msg));
|
||||
ASSERT_EQ(responder.read_message(msg, msg_len), 0);
|
||||
size_t reply_len = 0;
|
||||
ASSERT_EQ(responder.write_message(msg, sizeof(msg), reply_len), 0);
|
||||
ASSERT_GE(reply_len, SPARE_EPHEMERAL_KEY_SIZE);
|
||||
std::memcpy(responder_e, msg, SPARE_EPHEMERAL_KEY_SIZE);
|
||||
ASSERT_EQ(initiator.read_message(msg, reply_len), 0);
|
||||
ASSERT_EQ(responder.action(), Action::ACTION_SPLIT);
|
||||
}
|
||||
|
||||
TEST(SpareEphemeralTest, EmptySlotLeavesHandshakeToGenerate) {
|
||||
ASSERT_FALSE(has_spare_ephemeral());
|
||||
NoiseResponderHandshake responder;
|
||||
uint8_t responder_e[SPARE_EPHEMERAL_KEY_SIZE];
|
||||
run_handshake(responder, responder_e);
|
||||
EXPECT_FALSE(has_spare_ephemeral());
|
||||
}
|
||||
|
||||
TEST(SpareEphemeralTest, ConsumeHandsTheKeyToANewState) {
|
||||
prepare_spare_ephemeral();
|
||||
ASSERT_TRUE(has_spare_ephemeral());
|
||||
const NoiseProtocolId nid = {
|
||||
.prefix_id = NOISE_PREFIX_STANDARD,
|
||||
.pattern_id = NOISE_PATTERN_NN,
|
||||
.modifier_ids = {NOISE_MODIFIER_PSK0},
|
||||
.dh_id = NOISE_DH_CURVE25519,
|
||||
.cipher_id = NOISE_CIPHER_CHACHAPOLY,
|
||||
.hash_id = NOISE_HASH_SHA256,
|
||||
.hybrid_id = NOISE_DH_NONE,
|
||||
};
|
||||
NoiseHandshakeState *state = nullptr;
|
||||
ASSERT_EQ(noise_handshakestate_new_by_id(&state, &nid, NOISE_ROLE_RESPONDER), 0);
|
||||
const psk_t psk = make_psk(7);
|
||||
ASSERT_EQ(noise_handshakestate_set_pre_shared_key(state, psk.data(), psk.size()), 0);
|
||||
ASSERT_EQ(noise_handshakestate_set_prologue(state, PROLOGUE, sizeof(PROLOGUE)), 0);
|
||||
EXPECT_EQ(consume_spare_ephemeral(state), 0);
|
||||
EXPECT_FALSE(has_spare_ephemeral());
|
||||
noise_handshakestate_free(state);
|
||||
}
|
||||
|
||||
TEST(SpareEphemeralTest, SlotKeyIsOnTheWireAndConsumedOnce) {
|
||||
prepare_spare_ephemeral();
|
||||
ASSERT_TRUE(has_spare_ephemeral());
|
||||
uint8_t expected_pub[SPARE_EPHEMERAL_KEY_SIZE];
|
||||
std::memcpy(expected_pub, spare_ephemeral + SPARE_EPHEMERAL_KEY_SIZE, sizeof(expected_pub));
|
||||
|
||||
NoiseResponderHandshake first;
|
||||
uint8_t responder_e[SPARE_EPHEMERAL_KEY_SIZE];
|
||||
run_handshake(first, responder_e);
|
||||
// The spare, not a generated key, went out; and it went out once
|
||||
EXPECT_EQ(std::memcmp(responder_e, expected_pub, sizeof(expected_pub)), 0);
|
||||
EXPECT_FALSE(has_spare_ephemeral());
|
||||
|
||||
NoiseResponderHandshake second;
|
||||
run_handshake(second, responder_e);
|
||||
EXPECT_NE(std::memcmp(responder_e, expected_pub, sizeof(expected_pub)), 0);
|
||||
EXPECT_FALSE(has_spare_ephemeral());
|
||||
}
|
||||
|
||||
TEST(NoiseResponderHandshakeTest, ReInitRestartsHandshake) {
|
||||
// The documented retry shape: a repeated init() frees the previous state
|
||||
// and starts over. The first message under the new key authenticating
|
||||
|
||||
Reference in New Issue
Block a user