aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--link/crypto-openssl.c29
-rw-r--r--link/crypto.c10
-rw-r--r--link/crypto.h4
-rw-r--r--link/seal.h7
4 files changed, 48 insertions, 2 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};
diff --git a/link/crypto.c b/link/crypto.c
index 2fa7d44..c8fb9bd 100644
--- a/link/crypto.c
+++ b/link/crypto.c
@@ -141,6 +141,16 @@ bool pure_link_secret(const PureSealKey *own, const unsigned char peer[PURE_SEAL
return c && own && c->link_secret(own, peer, out);
}
+bool pure_link_ephemeral(unsigned char private_key[32], unsigned char public_key[PURE_SEAL_KEY]) {
+ const PureCrypto *c = loaded(NULL, 0);
+ return c && c->link_ephemeral(private_key, public_key);
+}
+
+bool pure_link_agree(const unsigned char private_key[32], const unsigned char peer[PURE_SEAL_KEY], unsigned char out[32]) {
+ const PureCrypto *c = loaded(NULL, 0);
+ return c && c->link_agree(private_key, peer, out);
+}
+
bool pure_link_derive(const unsigned char secret[32], const unsigned char *info, size_t info_size, unsigned char *out,
size_t out_size) {
const PureCrypto *c = loaded(NULL, 0);
diff --git a/link/crypto.h b/link/crypto.h
index 53ac785..ede5fb9 100644
--- a/link/crypto.h
+++ b/link/crypto.h
@@ -8,7 +8,7 @@
* first time a server seals, opens a seal or connects to an https receiver
* (link/crypto.c): one that does none of these maps no OpenSSL, 2.3 MiB
* less resident and 0.6 MiB less of its own. */
-enum { PURE_CRYPTO_VERSION = 4 };
+enum { PURE_CRYPTO_VERSION = 5 };
typedef struct {
unsigned version;
@@ -35,6 +35,8 @@ typedef struct {
void (*opener_free)(PureOpener *);
/* Links, as link/seal.h says. */
bool (*link_secret)(const PureSealKey *, const unsigned char peer[PURE_SEAL_KEY], unsigned char out[32]);
+ bool (*link_ephemeral)(unsigned char private_key[32], unsigned char public_key[PURE_SEAL_KEY]);
+ bool (*link_agree)(const unsigned char private_key[32], const unsigned char peer[PURE_SEAL_KEY], unsigned char out[32]);
bool (*link_derive)(const unsigned char secret[32], const unsigned char *info, size_t info_size, unsigned char *out,
size_t out_size);
PureAead *(*aead)(const unsigned char key[PURE_AEAD_KEY]);
diff --git a/link/seal.h b/link/seal.h
index 9245f25..de1e7a5 100644
--- a/link/seal.h
+++ b/link/seal.h
@@ -65,6 +65,13 @@ void pure_opener_free(PureOpener *);
typedef struct PureAead PureAead;
enum { PURE_AEAD_KEY = 16, PURE_AEAD_NONCE = 12, PURE_AEAD_TAG = 16 };
bool pure_link_secret(const PureSealKey *own, const unsigned char peer[PURE_SEAL_KEY], unsigned char out[32]);
+/* An epoch's keys (link/link-protocol.h): a key pair made for once, raw (its
+ * holder clears the private half as soon as it has agreed: a link may hold
+ * one for long, and a node many, so they are not kept in the secure heap),
+ * and the secret it shares with another's public half (never all zeros).
+ * On a Raspberry Pi 5 the pair costs 57 µs and the secret 220. */
+bool pure_link_ephemeral(unsigned char private_key[32], unsigned char public_key[PURE_SEAL_KEY]);
+bool pure_link_agree(const unsigned char private_key[32], const unsigned char peer[PURE_SEAL_KEY], unsigned char out[32]);
bool pure_link_derive(const unsigned char secret[32], const unsigned char *info, size_t info_size, unsigned char *out,
size_t out_size);
PureAead *pure_aead(const unsigned char key[PURE_AEAD_KEY]);