diff options
| author | eunhhu <[email protected]> | 2026-09-28 19:30:44 +0100 |
|---|---|---|
| committer | eunhhu <[email protected]> | 2026-09-28 19:30:44 +0100 |
| commit | a83d663404e030a8734fddc8827fb4afe309a1b9 (patch) | |
| tree | d94c2eb3c63cddd5418e8ffeb1896747abc4ac96 /serve/crypto-openssl.c | |
| parent | 09b754f2490dec5582ba79c9cde55b32bac12bcf (diff) | |
| download | pure-a83d663404e030a8734fddc8827fb4afe309a1b9.tar.gz pure-a83d663404e030a8734fddc8827fb4afe309a1b9.zip | |
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.
Diffstat (limited to 'serve/crypto-openssl.c')
| -rw-r--r-- | serve/crypto-openssl.c | 30 |
1 files changed, 22 insertions, 8 deletions
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 && |
