From a83d663404e030a8734fddc8827fb4afe309a1b9 Mon Sep 17 00:00:00 2001 From: eunhhu Date: Mon, 28 Sep 2026 19:30:44 +0100 Subject: Keep a run's keys as bytes: one AES-GCM context serves the listener Each run a listener knew held two OpenSSL contexts. With 16 KiB pages, what those allocations left behind when idle senders went kept 42 MB of free heap resident after 10,000 senders had come and gone (in use: 0.6 MB). A run now keeps its keys (16 bytes each way and the nonce bases), and the listener seals and opens with one context, giving it each packet's key: about 130 ns a packet on this Pi. Two runs a sender, not four; a sender's address as an IPv6 one (an IPv4 one fits). What idle senders held goes back to the system once 1,024 have gone, or 10 s after any went (malloc_trim). HKDF is fetched once, not at every derivation. --- serve/crypto-openssl.c | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) (limited to 'serve/crypto-openssl.c') diff --git a/serve/crypto-openssl.c b/serve/crypto-openssl.c index 6ff1ad4..67ce5fd 100644 --- a/serve/crypto-openssl.c +++ b/serve/crypto-openssl.c @@ -207,10 +207,23 @@ static bool link_secret(const PureSealKey *own, const unsigned char peer[PURE_SE return ok && any; } +/* HKDF, fetched once. */ +static EVP_KDF *hkdf(void) { + static EVP_KDF *kept; + EVP_KDF *kdf = __atomic_load_n(&kept, __ATOMIC_ACQUIRE), *none = NULL; + if (kdf) return kdf; + if ((kdf = EVP_KDF_fetch(NULL, "HKDF", NULL)) && + !__atomic_compare_exchange_n(&kept, &none, kdf, false, __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE)) { + EVP_KDF_free(kdf); /* another thread's is kept */ + kdf = none; + } + return kdf; +} + static bool link_derive(const unsigned char secret[32], const unsigned char *info, size_t info_size, unsigned char *out, size_t out_size) { static const char salt[] = "pure link 0"; - EVP_KDF *kdf = EVP_KDF_fetch(NULL, "HKDF", NULL); + EVP_KDF *kdf = hkdf(); EVP_KDF_CTX *context = kdf ? EVP_KDF_CTX_new(kdf) : NULL; OSSL_PARAM params[] = {OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST, (char *)"SHA256", 0), OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY, (void *)secret, 32), @@ -219,7 +232,6 @@ static bool link_derive(const unsigned char secret[32], const unsigned char *inf OSSL_PARAM_construct_end()}; bool ok = context && EVP_KDF_derive(context, out, out_size, params) == 1; EVP_KDF_CTX_free(context); - EVP_KDF_free(kdf); return ok; } @@ -243,22 +255,24 @@ static PureAead *aead(const unsigned char key[PURE_AEAD_KEY]) { return a; } -static bool aead_seal(PureAead *a, const unsigned char nonce[PURE_AEAD_NONCE], const unsigned char *aad, - size_t aad_size, const unsigned char *plain, size_t size, unsigned char *out) { +static bool aead_seal(PureAead *a, const unsigned char *key, const unsigned char nonce[PURE_AEAD_NONCE], + const unsigned char *aad, size_t aad_size, const unsigned char *plain, size_t size, + unsigned char *out) { int n = 0, last = 0; - return size <= INT_MAX && EVP_EncryptInit_ex(a->seal, NULL, NULL, NULL, nonce) == 1 && + return size <= INT_MAX && EVP_EncryptInit_ex(a->seal, NULL, NULL, key, nonce) == 1 && (!aad_size || EVP_EncryptUpdate(a->seal, NULL, &n, aad, (int)aad_size) == 1) && (!size || EVP_EncryptUpdate(a->seal, out, &n, plain, (int)size) == 1) && EVP_EncryptFinal_ex(a->seal, out + size, &last) == 1 && EVP_CIPHER_CTX_ctrl(a->seal, EVP_CTRL_GCM_GET_TAG, PURE_AEAD_TAG, out + size) == 1; } -static bool aead_open(PureAead *a, const unsigned char nonce[PURE_AEAD_NONCE], const unsigned char *aad, - size_t aad_size, const unsigned char *sealed, size_t size, unsigned char *plain) { +static bool aead_open(PureAead *a, const unsigned char *key, const unsigned char nonce[PURE_AEAD_NONCE], + const unsigned char *aad, size_t aad_size, const unsigned char *sealed, size_t size, + unsigned char *plain) { int n = 0, last = 0; if (size < PURE_AEAD_TAG || size - PURE_AEAD_TAG > INT_MAX) return false; size_t body = size - PURE_AEAD_TAG; - return EVP_DecryptInit_ex(a->open, NULL, NULL, NULL, nonce) == 1 && + return EVP_DecryptInit_ex(a->open, NULL, NULL, key, nonce) == 1 && (!aad_size || EVP_DecryptUpdate(a->open, NULL, &n, aad, (int)aad_size) == 1) && (!body || EVP_DecryptUpdate(a->open, plain, &n, sealed, (int)body) == 1) && EVP_CIPHER_CTX_ctrl(a->open, EVP_CTRL_GCM_SET_TAG, PURE_AEAD_TAG, (void *)(sealed + body)) == 1 && -- cgit v1.2.3