aboutsummaryrefslogtreecommitdiffstats
path: root/link/crypto-openssl.c
diff options
context:
space:
mode:
authoreunhhu <[email protected]>2026-09-29 13:51:07 +0100
committereunhhu <[email protected]>2026-09-29 13:51:07 +0100
commit19ebac6e3cd5e48e16a73c78342fa676dc905db9 (patch)
treeff1ca1db6aa594094e676a0bfd7a4b22ec6a0748 /link/crypto-openssl.c
parentf5ce5e2b6ff0f72cc506e464d0ebfad00bed3532 (diff)
downloadpure-19ebac6e3cd5e48e16a73c78342fa676dc905db9.tar.gz
pure-19ebac6e3cd5e48e16a73c78342fa676dc905db9.zip
Let links make an X25519 pair for once, and agree with it
An epoch's keys (docs/design/011-pure-transport.md): a pair made for the next epoch, held raw (a link may hold one for long and a node many, so not in OpenSSL's 16 KiB secure heap), and the secret its private half shares with the other side's public half, never all zeros. The crypto table's version is 5.
Diffstat (limited to 'link/crypto-openssl.c')
-rw-r--r--link/crypto-openssl.c29
1 files changed, 28 insertions, 1 deletions
diff --git a/link/crypto-openssl.c b/link/crypto-openssl.c
index 6bdf6fb..96daa82 100644
--- a/link/crypto-openssl.c
+++ b/link/crypto-openssl.c
@@ -207,6 +207,32 @@ static bool link_secret(const PureSealKey *own, const unsigned char peer[PURE_SE
return ok && any;
}
+/* A pair made for once (X25519): an epoch's half, raw. */
+static bool link_ephemeral(unsigned char private_key[32], unsigned char public_key[PURE_SEAL_KEY]) {
+ EVP_PKEY *key = EVP_PKEY_Q_keygen(NULL, NULL, "X25519");
+ size_t a = 32, b = PURE_SEAL_KEY;
+ bool ok = key && EVP_PKEY_get_raw_private_key(key, private_key, &a) == 1 &&
+ EVP_PKEY_get_raw_public_key(key, public_key, &b) == 1 && a == 32 && b == PURE_SEAL_KEY;
+ EVP_PKEY_free(key);
+ return ok;
+}
+
+/* The secret a raw private half shares with a peer's public key. */
+static bool link_agree(const unsigned char private_key[32], const unsigned char peer[PURE_SEAL_KEY], unsigned char out[32]) {
+ EVP_PKEY *mine = EVP_PKEY_new_raw_private_key(EVP_PKEY_X25519, NULL, private_key, 32);
+ EVP_PKEY *theirs = EVP_PKEY_new_raw_public_key(EVP_PKEY_X25519, NULL, peer, PURE_SEAL_KEY);
+ EVP_PKEY_CTX *context = mine && theirs ? EVP_PKEY_CTX_new(mine, NULL) : NULL;
+ size_t length = 32;
+ bool ok = context && EVP_PKEY_derive_init(context) == 1 && EVP_PKEY_derive_set_peer(context, theirs) == 1 &&
+ EVP_PKEY_derive(context, out, &length) == 1 && length == 32;
+ EVP_PKEY_CTX_free(context);
+ EVP_PKEY_free(mine);
+ EVP_PKEY_free(theirs);
+ unsigned char any = 0; /* a small-order key makes zeros: no secret */
+ for (size_t i = 0; ok && i < 32; ++i) any |= out[i];
+ return ok && any;
+}
+
/* HKDF, fetched once. */
static EVP_KDF *hkdf(void) {
static EVP_KDF *kept;
@@ -281,4 +307,5 @@ static bool aead_open(PureAead *a, const unsigned char *key, const unsigned char
const PureCrypto pure_crypto = {PURE_CRYPTO_VERSION, tls_start, tls_open, tls_read, tls_write, tls_close,
seal_key, seal_key_free, sealer, seal, sealer_free, opener, unseal, opener_free,
- link_secret, link_derive, aead, aead_seal, aead_open, aead_free};
+ link_secret, link_ephemeral, link_agree, link_derive, aead, aead_seal, aead_open,
+ aead_free};