Share the spare ephemeral key size and note the key stays in RAM until consumed

This commit is contained in:
J. Nick Koston
2026-09-07 17:02:04 +02:00
parent 5cc758dea3
commit 6aa6b682df
3 changed files with 13 additions and 13 deletions
+2 -3
View File
@@ -27,9 +27,8 @@ void NoiseContext::load_psk(psk_t &out) const {
}
#ifdef USE_NOISE_SPARE_EPHEMERAL
static constexpr size_t PRIVATE_KEY_SIZE = 32;
static constexpr size_t PUBLIC_KEY_SIZE = 32;
static_assert(PRIVATE_KEY_SIZE + PUBLIC_KEY_SIZE == SPARE_EPHEMERAL_SIZE);
static constexpr size_t PRIVATE_KEY_SIZE = SPARE_EPHEMERAL_KEY_SIZE;
static constexpr size_t PUBLIC_KEY_SIZE = SPARE_EPHEMERAL_KEY_SIZE;
uint8_t spare_ephemeral[SPARE_EPHEMERAL_SIZE]; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
void prepare_spare_ephemeral() {
+4 -3
View File
@@ -45,13 +45,14 @@ const LogString *noise_err_to_logstr(int err);
// One responder ephemeral key pair generated ahead of time (about 60 ms on
// ESP8266), refilled by the api server while idle and consumed by the next
// handshake of any noise transport; an empty slot means the handshake
// generates its own key.
// generates its own key. The private key stays in RAM until consumed.
// Private key then public key; zero when empty
static constexpr size_t SPARE_EPHEMERAL_SIZE = 64;
static constexpr size_t SPARE_EPHEMERAL_KEY_SIZE = 32;
static constexpr size_t SPARE_EPHEMERAL_SIZE = 2 * SPARE_EPHEMERAL_KEY_SIZE;
extern uint8_t spare_ephemeral[SPARE_EPHEMERAL_SIZE]; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
// Polled every api loop tick, so it must inline. A clamped X25519 private key
// always has bit 254 set, so that byte doubles as the ready flag.
inline bool has_spare_ephemeral() { return (spare_ephemeral[31] & 0x40) != 0; }
inline bool has_spare_ephemeral() { return (spare_ephemeral[SPARE_EPHEMERAL_KEY_SIZE - 1] & 0x40) != 0; }
/// Fill the slot; blocks for the base point multiply
void prepare_spare_ephemeral();
/// Hand the slot's key pair to a handshake that has not started and wipe the
@@ -161,7 +161,7 @@ TEST(NoiseResponderHandshakeTest, FullHandshakeAndTransportRoundTrip) {
// responder_e receives the ephemeral public key the responder put on the
// wire (the clear text start of its message, taken before the initiator
// consumes the buffer in place)
static void run_handshake(NoiseResponderHandshake &responder, uint8_t responder_e[32]) {
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));
@@ -170,8 +170,8 @@ static void run_handshake(NoiseResponderHandshake &responder, uint8_t responder_
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, 32u);
std::memcpy(responder_e, msg, 32);
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);
}
@@ -179,7 +179,7 @@ static void run_handshake(NoiseResponderHandshake &responder, uint8_t responder_
TEST(SpareEphemeralTest, EmptySlotLeavesHandshakeToGenerate) {
ASSERT_FALSE(has_spare_ephemeral());
NoiseResponderHandshake responder;
uint8_t responder_e[32];
uint8_t responder_e[SPARE_EPHEMERAL_KEY_SIZE];
run_handshake(responder, responder_e);
EXPECT_FALSE(has_spare_ephemeral());
}
@@ -209,11 +209,11 @@ TEST(SpareEphemeralTest, ConsumeHandsTheKeyToANewState) {
TEST(SpareEphemeralTest, SlotKeyIsOnTheWireAndConsumedOnce) {
prepare_spare_ephemeral();
ASSERT_TRUE(has_spare_ephemeral());
uint8_t expected_pub[32];
std::memcpy(expected_pub, spare_ephemeral + 32, sizeof(expected_pub));
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[32];
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);