diff options
| author | eunhhu <[email protected]> | 2026-09-27 19:03:34 +0100 |
|---|---|---|
| committer | eunhhu <[email protected]> | 2026-09-27 19:03:34 +0100 |
| commit | 8019260926dcebe4f4be99cb5f92f84fbc9cd6d3 (patch) | |
| tree | 06e407f4ba8606741df98bb1c6dcf3b7eed8fa12 /serve/crypto.c | |
| parent | 944ab4fe95596127ec3c517037bad8fe6102dfe4 (diff) | |
| download | pure-8019260926dcebe4f4be99cb5f92f84fbc9cd6d3.tar.gz pure-8019260926dcebe4f4be99cb5f92f84fbc9cd6d3.zip | |
Map OpenSSL only in a server that uses it
Linking OpenSSL cost every pure-serve 2.3 MiB of resident memory, 0.66
MiB of it the process's own (relocated data), though most servers never
reach an https receiver or seal a send. The code that uses OpenSSL (TLS
to https receivers, sealing) is now built as pure-crypto.so beside
pure-serve and exports one table; pure-serve maps it the first time a
wire to an https URL opens or a key is read or a seal wired
(serve/crypto.c), and a missing library is reported at startup. What
needs no OpenSSL, a key's hex and the suite this CPU seals with, stays in
pure-serve.
An idle server: RSS 4,496 -> 2,320 kB, PSS 1,639 -> 881 kB, private
dirty 1,296 -> 864 kB. A static OpenSSL was measured too and is worse
(RSS 3.8 MiB, but 2.3 MiB private: all of its tables relocated). The
checks, https webhooks and sealed sends included, pass unchanged.
Diffstat (limited to 'serve/crypto.c')
| -rw-r--r-- | serve/crypto.c | 121 |
1 files changed, 121 insertions, 0 deletions
diff --git a/serve/crypto.c b/serve/crypto.c new file mode 100644 index 0000000..8806ad2 --- /dev/null +++ b/serve/crypto.c @@ -0,0 +1,121 @@ +#define _GNU_SOURCE +/* Sealing and TLS for pure-serve (serve/seal.h, serve/tls.h), by way of + * pure-crypto.so (serve/crypto.h): mapped, and OpenSSL with it, the first + * time a server needs it. What needs no OpenSSL (a key's hex, the suite + * this machine seals with) is here. */ +#include "crypto.h" +#include <dlfcn.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#if defined(__aarch64__) && defined(__linux__) +#include <asm/hwcap.h> +#include <sys/auxv.h> +#endif + +static pthread_once_t once = PTHREAD_ONCE_INIT; +static const PureCrypto *crypto; +static char why[512]; + +/* pure-crypto.so beside the executable, or where PURE_CRYPTO names it. */ +static void load(void) { + char path[4096]; + const char *named = getenv("PURE_CRYPTO"); + if (named) snprintf(path, sizeof(path), "%s", named); + else { + ssize_t length = readlink("/proc/self/exe", path, sizeof(path) - 32); + char *slash = length > 0 ? memrchr(path, '/', (size_t)length) : NULL; + if (!slash) { snprintf(why, sizeof(why), "pure-crypto.so: cannot find pure-serve's directory"); return; } + strcpy(slash + 1, "pure-crypto.so"); + } + void *library = dlopen(path, RTLD_NOW | RTLD_LOCAL); + const PureCrypto *table = library ? dlsym(library, "pure_crypto") : NULL; + if (!table || table->version != PURE_CRYPTO_VERSION) { + snprintf(why, sizeof(why), "%s", !library ? dlerror() : "pure-crypto.so is not this pure-serve's"); + return; + } + crypto = table; +} + +static const PureCrypto *loaded(char *error, size_t size) { + pthread_once(&once, load); + if (!crypto && error) snprintf(error, size, "%s", why); + return crypto; +} + +/* ---- TLS */ + +bool pure_tls_start(const char *ca_file, char *error, size_t size) { + static int started; /* 1 made, -1 failed; by the thread that opens wires */ + const PureCrypto *c = loaded(error, size); + if (!c) return false; + if (!started) started = c->tls_start(ca_file) ? 1 : -1; + if (started < 0) snprintf(error, size, "no TLS context: %s cannot be read", ca_file ? ca_file : "the system's CAs"); + return started > 0; +} + +/* A session exists only once the table is loaded. */ +PureTls *pure_tls_open(const char *host, int fd) { return crypto ? crypto->tls_open(host, fd) : NULL; } +int pure_tls_read(PureTls *tls, void *bytes, int size) { return crypto->tls_read(tls, bytes, size); } +int pure_tls_write(PureTls *tls, const void *bytes, int size) { return crypto->tls_write(tls, bytes, size); } +void pure_tls_close(PureTls *tls) { if (tls) crypto->tls_close(tls); } + +/* ---- Sealing */ + +static const char aes_name[] = "x25519-sha256-aes128gcm", chacha_name[] = "x25519-sha256-chacha20poly1305"; + +/* AES-GCM where AES is in the CPU (two to three times ChaCha20-Poly1305 from + * a kilobyte up on a Cortex-A76, research 086); ChaCha20-Poly1305, fast in + * plain code, elsewhere. */ +static bool has_aes(void) { +#if defined(__aarch64__) && defined(__linux__) + return (getauxval(AT_HWCAP) & HWCAP_AES) && (getauxval(AT_HWCAP) & HWCAP_PMULL); +#elif defined(__x86_64__) && (defined(__GNUC__) || defined(__clang__)) + return __builtin_cpu_supports("aes") && __builtin_cpu_supports("pclmul"); +#else + return false; +#endif +} + +const char *pure_seal_suite(void) { return has_aes() ? aes_name : chacha_name; } + +static bool hex_value(char c, unsigned *v) { + if (c >= '0' && c <= '9') *v = (unsigned)(c - '0'); + else if (c >= 'a' && c <= 'f') *v = (unsigned)(c - 'a' + 10); + else if (c >= 'A' && c <= 'F') *v = (unsigned)(c - 'A' + 10); + else return false; + return true; +} + +bool pure_seal_peer(const char *hex, unsigned char out[PURE_SEAL_KEY]) { + if (strlen(hex) != 2 * PURE_SEAL_KEY) return false; + for (size_t i = 0; i < PURE_SEAL_KEY; ++i) { + unsigned high, low; + if (!hex_value(hex[2 * i], &high) || !hex_value(hex[2 * i + 1], &low)) return false; + out[i] = (unsigned char)(high << 4 | low); + } + return true; +} + +bool pure_seal_ready(char *error, size_t size) { return loaded(error, size) != NULL; } + +PureSealKey *pure_seal_key(const char *path, char public_hex[2 * PURE_SEAL_KEY + 1], char *error, size_t size) { + const PureCrypto *c = loaded(error, size); + return c ? c->seal_key(path, public_hex, error, size) : NULL; +} + +/* A key exists only once the table is loaded. */ +void pure_seal_key_free(PureSealKey *key) { if (key) crypto->seal_key_free(key); } + +bool pure_seal(const unsigned char peer[PURE_SEAL_KEY], const char *aad, const unsigned char *plain, size_t size, + unsigned char *out) { + const PureCrypto *c = loaded(NULL, 0); + return c && c->seal(pure_seal_suite(), peer, aad, plain, size, out); +} + +bool pure_unseal(const PureSealKey *key, const char *suite, const char *aad, const unsigned char *sealed, size_t size, + unsigned char *plain) { + return key && crypto->unseal(key, suite, aad, sealed, size, plain); +} |
