[noise] Use the clamp bit of the spare key as its ready flag

A clamped X25519 private key always has bit 254 set, so byte 31 of the
slot says whether a key is present and a wiped slot reads empty; the
separate flag and its padding go, leaving the slot at exactly 64 bytes.
This commit is contained in:
J. Nick Koston
2026-09-07 12:45:25 +02:00
parent 228f8894a9
commit 55804e6e16
2 changed files with 12 additions and 15 deletions
+6 -12
View File
@@ -29,39 +29,33 @@ 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;
// Private key then public key
static uint8_t
spare_ephemeral[PRIVATE_KEY_SIZE + PUBLIC_KEY_SIZE]; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
bool spare_ephemeral_ready = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
static_assert(PRIVATE_KEY_SIZE + PUBLIC_KEY_SIZE == SPARE_EPHEMERAL_SIZE);
uint8_t spare_ephemeral[SPARE_EPHEMERAL_SIZE]; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
void prepare_spare_ephemeral() {
// A partial fill must never look ready
spare_ephemeral_ready = false;
uint8_t *private_key = spare_ephemeral;
uint8_t *public_key = spare_ephemeral + PRIVATE_KEY_SIZE;
// Same steps as noise-c's curve25519 keygen; on failure the slot stays
// empty and the handshake generates its own key
// Same steps as noise-c's curve25519 keygen; the clamp sets the ready bit,
// a failure wipes the slot so the handshake generates its own key
if (!random_bytes(private_key, PRIVATE_KEY_SIZE)) {
sodium_memzero(spare_ephemeral, sizeof(spare_ephemeral));
return;
}
private_key[0] &= 0xF8;
private_key[PRIVATE_KEY_SIZE - 1] = (private_key[PRIVATE_KEY_SIZE - 1] & 0x7F) | 0x40;
if (crypto_scalarmult_curve25519_base(public_key, private_key) != 0) {
sodium_memzero(spare_ephemeral, sizeof(spare_ephemeral));
return;
}
spare_ephemeral_ready = true;
}
int consume_spare_ephemeral(NoiseHandshakeState *state) {
if (!spare_ephemeral_ready) {
if (!has_spare_ephemeral()) {
return 0;
}
// noise-c keeps its own copy, so the slot is wiped either way
int err = noise_handshakestate_set_local_ephemeral(state, spare_ephemeral, PRIVATE_KEY_SIZE,
spare_ephemeral + PRIVATE_KEY_SIZE, PUBLIC_KEY_SIZE);
sodium_memzero(spare_ephemeral, sizeof(spare_ephemeral));
spare_ephemeral_ready = false;
return err;
}
#endif // USE_NOISE_SPARE_EPHEMERAL
+6 -3
View File
@@ -46,9 +46,12 @@ const LogString *noise_err_to_logstr(int err);
// 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.
extern bool spare_ephemeral_ready; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
// Polled every api loop tick, so it must inline
inline bool has_spare_ephemeral() { return spare_ephemeral_ready; }
// Private key then public key; zero when empty
static constexpr size_t SPARE_EPHEMERAL_SIZE = 64;
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; }
/// 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