commit b73d098: Initial eddsa signing implementation

John McKay adenosine3p at gmail.com
Mon Feb 4 14:35:07 UTC 2019


Author: John McKay
Date: 2019-01-17 13:02:52 +0000
URL: https://github.com/rspamd/rspamd/commit/b73d098dcee8d3b52da56fd7d4f02cd2fc823102

Initial eddsa signing implementation

---
 src/libserver/dkim.c | 209 +++++++++++++++++++++++++++++++++------------------
 1 file changed, 135 insertions(+), 74 deletions(-)

diff --git a/src/libserver/dkim.c b/src/libserver/dkim.c
index 4b49d1e6f..d4c54b422 100644
--- a/src/libserver/dkim.c
+++ b/src/libserver/dkim.c
@@ -21,6 +21,7 @@
 #include "utlist.h"
 #include "unix-std.h"
 #include "mempool_vars_internal.h"
+#include "libcryptobox/ed25519/ed25519.h"
 
 #include <openssl/evp.h>
 #include <openssl/rsa.h>
@@ -29,6 +30,10 @@
 /* special DNS tokens */
 #define DKIM_DNSKEYNAME     "_domainkey"
 
+/* ed25519 key lengths */
+#define ED25519_B64_BYTES	45
+#define ED25519_BYTES		32
+
 /* Canonization methods */
 #define DKIM_CANON_UNKNOWN  (-1)    /* unknown method */
 #define DKIM_CANON_SIMPLE   0   /* as specified in DKIM spec */
@@ -78,6 +83,10 @@ enum rspamd_dkim_param_type {
         rspamd_dkim_log_id, "dkim", ctx->pool->tag.uid, \
         G_STRFUNC, \
         __VA_ARGS__)
+#define msg_debug2_dkim(...)  rspamd_conditional_debug_fast (NULL, NULL, \
+        rspamd_dkim_log_id, "dkim", "", \
+        G_STRFUNC, \
+        __VA_ARGS__)
 
 INIT_LOG_MODULE(dkim)
 
@@ -155,10 +164,14 @@ struct rspamd_dkim_sign_context_s {
 };
 
 struct rspamd_dkim_sign_key_s {
-	enum rspamd_dkim_sign_key_type type;
+	enum rspamd_dkim_key_type type;
 	guint8 *keydata;
+	gpointer map;
 	gsize keylen;
-	RSA *key_rsa;
+	union {
+		RSA *key_rsa;
+		guchar *key_eddsa;
+	} key;
 	BIO *key_bio;
 	EVP_PKEY *key_evp;
 	time_t mtime;
@@ -1342,8 +1355,10 @@ rspamd_dkim_sign_key_free (rspamd_dkim_sign_key_t *key)
 	if (key->key_evp) {
 		EVP_PKEY_free (key->key_evp);
 	}
-	if (key->key_rsa) {
-		RSA_free (key->key_rsa);
+	if (key->type == RSPAMD_DKIM_KEY_RSA) {
+		if (key->key.key_rsa) {
+			RSA_free (key->key.key_rsa);
+		}
 	}
 	if (key->key_bio) {
 		BIO_free (key->key_bio);
@@ -1351,10 +1366,11 @@ rspamd_dkim_sign_key_free (rspamd_dkim_sign_key_t *key)
 
 	if (key->keydata && key->keylen > 0) {
 
-		if (key->type == RSPAMD_DKIM_SIGN_KEY_FILE) {
-			munmap (key->keydata, key->keylen);
+		if (key->map) {
+			munmap (key->map, key->keylen);
 		}
 		else {
+			rspamd_explicit_memzero (key->keydata, key->keylen);
 			g_free (key->keydata);
 		}
 	}
@@ -2652,6 +2668,7 @@ rspamd_dkim_sign_key_load (const gchar *what, gsize len,
 	switch (type) {
 	case RSPAMD_DKIM_SIGN_KEY_FILE:
 		(void)mlock (map, len);
+		nkey->map = map;
 		nkey->keydata = map;
 		nkey->keylen = map_len;
 		break;
@@ -2668,57 +2685,85 @@ rspamd_dkim_sign_key_load (const gchar *what, gsize len,
 		nkey->keylen = len;
 	}
 
+	msg_debug2_dkim("got public key with length %d and type %d", nkey->keylen, type);
 	(void)mlock (nkey->keydata, nkey->keylen);
-	nkey->key_bio = BIO_new_mem_buf (nkey->keydata, nkey->keylen);
+	if (type == RSPAMD_DKIM_SIGN_KEY_FILE && nkey->keylen == ED25519_B64_BYTES) {
+		unsigned char seed[32];
+		unsigned char pk[32];
+		nkey->type = RSPAMD_DKIM_KEY_EDDSA;
+		nkey->keydata = g_malloc (rspamd_cryptobox_sk_sig_bytes (RSPAMD_CRYPTOBOX_MODE_25519));
+		rspamd_cryptobox_base64_decode (nkey->map, ED25519_B64_BYTES, seed, &nkey->keylen);
+		ed25519_seed_keypair (pk, nkey->keydata, seed);
+		nkey->key.key_eddsa = nkey->keydata;
+		nkey->keylen = rspamd_cryptobox_sk_sig_bytes (RSPAMD_CRYPTOBOX_MODE_25519);
+		rspamd_explicit_memzero (seed, 32);
+		munmap (nkey->map, ED25519_B64_BYTES);
+		nkey->map = NULL;
+	}
+	else if (type == RSPAMD_DKIM_SIGN_KEY_BASE64 && nkey->keylen == ED25519_BYTES) {
+		unsigned char pk[32];
+		nkey->type = RSPAMD_DKIM_KEY_EDDSA;
+		nkey->key.key_eddsa =
+			g_malloc (rspamd_cryptobox_sk_sig_bytes (RSPAMD_CRYPTOBOX_MODE_25519));
+		ed25519_seed_keypair (pk, nkey->key.key_eddsa, nkey->keydata);
+		rspamd_explicit_memzero (nkey->keydata, nkey->keylen);
+		g_free (nkey->keydata);
+		nkey->keydata = nkey->key.key_eddsa;
+		nkey->keylen = rspamd_cryptobox_sk_sig_bytes (RSPAMD_CRYPTOBOX_MODE_25519);
+	}
+	else {
+		nkey->key_bio = BIO_new_mem_buf (nkey->keydata, nkey->keylen);
+
+		if (type == RSPAMD_DKIM_SIGN_KEY_DER || type == RSPAMD_DKIM_SIGN_KEY_BASE64) {
+			if (d2i_PrivateKey_bio (nkey->key_bio, &nkey->key_evp) == NULL) {
+				if (type == RSPAMD_DKIM_SIGN_KEY_FILE) {
+					g_set_error (err, dkim_error_quark (), DKIM_SIGERROR_KEYFAIL,
+							"cannot read private key from %*s: %s",
+							(gint)len, what,
+							ERR_error_string (ERR_get_error (), NULL));
+				}
+				else {
+					g_set_error (err, dkim_error_quark (), DKIM_SIGERROR_KEYFAIL,
+							"cannot read private key from string: %s",
+							ERR_error_string (ERR_get_error (), NULL));
+				}
 
-	if (type == RSPAMD_DKIM_SIGN_KEY_DER || type == RSPAMD_DKIM_SIGN_KEY_BASE64) {
-		if (d2i_PrivateKey_bio (nkey->key_bio, &nkey->key_evp) == NULL) {
-			if (type == RSPAMD_DKIM_SIGN_KEY_FILE) {
-				g_set_error (err, dkim_error_quark (), DKIM_SIGERROR_KEYFAIL,
-						"cannot read private key from %*s: %s",
-						(gint)len, what,
-						ERR_error_string (ERR_get_error (), NULL));
-			}
-			else {
-				g_set_error (err, dkim_error_quark (), DKIM_SIGERROR_KEYFAIL,
-						"cannot read private key from string: %s",
-						ERR_error_string (ERR_get_error (), NULL));
+				rspamd_dkim_sign_key_free (nkey);
+
+				return NULL;
 			}
+		}
+		else {
+			if (!PEM_read_bio_PrivateKey (nkey->key_bio, &nkey->key_evp, NULL, NULL)) {
+				if (type == RSPAMD_DKIM_SIGN_KEY_FILE) {
+					g_set_error (err, dkim_error_quark (), DKIM_SIGERROR_KEYFAIL,
+							"cannot read private key from %*s: %s",
+							(gint)len, what,
+							ERR_error_string (ERR_get_error (), NULL));
+				}
+				else {
+					g_set_error (err, dkim_error_quark (), DKIM_SIGERROR_KEYFAIL,
+							"cannot read private key from string: %s",
+							ERR_error_string (ERR_get_error (), NULL));
+				}
 
-			rspamd_dkim_sign_key_free (nkey);
+				rspamd_dkim_sign_key_free (nkey);
 
-			return NULL;
-		}
-	}
-	else {
-		if (!PEM_read_bio_PrivateKey (nkey->key_bio, &nkey->key_evp, NULL, NULL)) {
-			if (type == RSPAMD_DKIM_SIGN_KEY_FILE) {
-				g_set_error (err, dkim_error_quark (), DKIM_SIGERROR_KEYFAIL,
-						"cannot read private key from %*s: %s",
-						(gint)len, what,
-						ERR_error_string (ERR_get_error (), NULL));
-			}
-			else {
-				g_set_error (err, dkim_error_quark (), DKIM_SIGERROR_KEYFAIL,
-						"cannot read private key from string: %s",
-						ERR_error_string (ERR_get_error (), NULL));
+				return NULL;
 			}
 
+		}
+		nkey->key.key_rsa = EVP_PKEY_get1_RSA (nkey->key_evp);
+		if (nkey->key.key_rsa == NULL) {
+			g_set_error (err,
+					DKIM_ERROR,
+					DKIM_SIGERROR_KEYFAIL,
+					"cannot extract rsa key from evp key");
 			rspamd_dkim_sign_key_free (nkey);
 
 			return NULL;
 		}
-	}
-
-	nkey->key_rsa = EVP_PKEY_get1_RSA (nkey->key_evp);
-	if (nkey->key_rsa == NULL) {
-		g_set_error (err,
-				DKIM_ERROR,
-				DKIM_SIGERROR_KEYFAIL,
-				"cannot extract rsa key from evp key");
-		rspamd_dkim_sign_key_free (nkey);
-
-		return NULL;
+		nkey->type = RSPAMD_DKIM_KEY_RSA;
 	}
 
 	REF_INIT_RETAIN (nkey, rspamd_dkim_sign_key_free);
@@ -2784,7 +2829,7 @@ rspamd_create_dkim_sign_context (struct rspamd_task *task,
 		return NULL;
 	}
 
-	if (!priv_key || !priv_key->key_rsa) {
+	if (!priv_key || (!priv_key->key.key_rsa && !priv_key->key.key_eddsa)) {
 		g_set_error (err,
 				DKIM_ERROR,
 				DKIM_SIGERROR_KEYFAIL,
@@ -2852,8 +2897,8 @@ rspamd_dkim_sign (struct rspamd_task *task, const gchar *selector,
 	gsize dlen = 0;
 	guint i, j;
 	gchar *b64_data;
-	guchar *rsa_buf;
-	guint rsa_len;
+	guchar *sig_buf;
+	guint sig_len;
 	guint headers_len = 0, cur_len = 0;
 	union rspamd_dkim_header_stat hstat;
 
@@ -2889,7 +2934,9 @@ rspamd_dkim_sign (struct rspamd_task *task, const gchar *selector,
 	hdr = g_string_sized_new (255);
 
 	if (ctx->common.type == RSPAMD_DKIM_NORMAL) {
-		rspamd_printf_gstring (hdr, "v=1; a=rsa-sha256; c=%s/%s; d=%s; s=%s; ",
+		rspamd_printf_gstring (hdr, "v=1; a=%s; c=%s/%s; d=%s; s=%s; ",
+				ctx->key->type == RSPAMD_DKIM_KEY_RSA ?
+						"rsa-sha256" : "ed25519-sha256",
 				ctx->common.header_canon_type == DKIM_CANON_RELAXED ?
 						"relaxed" : "simple",
 				ctx->common.body_canon_type == DKIM_CANON_RELAXED ?
@@ -2897,8 +2944,10 @@ rspamd_dkim_sign (struct rspamd_task *task, const gchar *selector,
 				domain, selector);
 	}
 	else if (ctx->common.type == RSPAMD_DKIM_ARC_SIG) {
-		rspamd_printf_gstring (hdr, "i=%d; a=rsa-sha256; c=%s/%s; d=%s; s=%s; ",
+		rspamd_printf_gstring (hdr, "i=%d; a=%s; c=%s/%s; d=%s; s=%s; ",
 				idx,
+				ctx->key->type == RSPAMD_DKIM_KEY_RSA ?
+						"rsa-sha256" : "ed25519-sha256",
 				ctx->common.header_canon_type == DKIM_CANON_RELAXED ?
 						"relaxed" : "simple",
 				ctx->common.body_canon_type == DKIM_CANON_RELAXED ?
@@ -2907,8 +2956,10 @@ rspamd_dkim_sign (struct rspamd_task *task, const gchar *selector,
 	}
 	else {
 		g_assert (arc_cv != NULL);
-		rspamd_printf_gstring (hdr, "i=%d; a=rsa-sha256; c=%s/%s; d=%s; s=%s; cv=%s; ",
+		rspamd_printf_gstring (hdr, "i=%d; a=%s; c=%s/%s; d=%s; s=%s; cv=%s; ",
 				arc_cv,
+				ctx->key->type == RSPAMD_DKIM_KEY_RSA ?
+						"rsa-sha256" : "ed25519-sha256",
 				idx,
 				domain, selector);
 	}
@@ -3040,24 +3091,37 @@ rspamd_dkim_sign (struct rspamd_task *task, const gchar *selector,
 
 	dlen = EVP_MD_CTX_size (ctx->common.headers_hash);
 	EVP_DigestFinal_ex (ctx->common.headers_hash, raw_digest, NULL);
-	rsa_len = RSA_size (ctx->key->key_rsa);
-	rsa_buf = g_alloca (rsa_len);
+	if (ctx->key->type == RSPAMD_DKIM_KEY_RSA) {
+		sig_len = RSA_size (ctx->key->key.key_rsa);
+		sig_buf = g_alloca (sig_len);
+
+		if (RSA_sign (NID_sha256, raw_digest, dlen, sig_buf, &sig_len,
+				ctx->key->key.key_rsa) != 1) {
+			g_string_free (hdr, TRUE);
+			msg_err_task ("rsa sign error: %s",
+					ERR_error_string (ERR_get_error (), NULL));
 
-	if (RSA_sign (NID_sha256, raw_digest, dlen, rsa_buf, &rsa_len,
-			ctx->key->key_rsa) != 1) {
+			return NULL;
+		}
+	} else if (ctx->key->type == RSPAMD_DKIM_KEY_EDDSA) {
+		sig_len = rspamd_cryptobox_signature_bytes (RSPAMD_CRYPTOBOX_MODE_25519);
+		sig_buf = g_alloca (sig_len);
+
+		rspamd_cryptobox_sign (sig_buf, NULL, raw_digest, dlen,
+				ctx->key->key.key_eddsa, RSPAMD_CRYPTOBOX_MODE_25519);
+	} else {
 		g_string_free (hdr, TRUE);
-		msg_err_task ("rsa sign error: %s",
-				ERR_error_string (ERR_get_error (), NULL));
+		msg_err_task ("unsupported key type for signing");
 
 		return NULL;
 	}
 
 	if (task->flags & RSPAMD_TASK_FLAG_MILTER) {
-		b64_data = rspamd_encode_base64_fold (rsa_buf, rsa_len, 70, NULL,
+		b64_data = rspamd_encode_base64_fold (sig_buf, sig_len, 70, NULL,
 				RSPAMD_TASK_NEWLINES_LF);
 	}
 	else {
-		b64_data = rspamd_encode_base64_fold (rsa_buf, rsa_len, 70, NULL,
+		b64_data = rspamd_encode_base64_fold (sig_buf, sig_len, 70, NULL,
 				task->nlines_type);
 	}
 
@@ -3072,33 +3136,30 @@ rspamd_dkim_match_keys (rspamd_dkim_key_t *pk,
 								 rspamd_dkim_sign_key_t *sk,
 								 GError **err)
 {
-	const BIGNUM *n1, *n2;
-
 	if (pk == NULL || sk == NULL) {
 		g_set_error (err, dkim_error_quark (), DKIM_SIGERROR_KEYFAIL,
 				"missing public or private key");
 		return FALSE;
 	}
-
-	if (pk->type != RSPAMD_DKIM_KEY_RSA) {
+	if (pk->type != sk->type) {
 		g_set_error (err, dkim_error_quark (), DKIM_SIGERROR_KEYFAIL,
-				"pubkey is not RSA key");
+				"public and private key types do not match");
 		return FALSE;
 	}
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
-	RSA_get0_key (pk->key.key_rsa, &n1, NULL, NULL);
-	RSA_get0_key (sk->key_rsa, &n2, NULL, NULL);
-#else
-	n1 = pk->key.key_rsa->n;
-	n2 = sk->key_rsa->n;
-#endif
+	if (pk->type == RSPAMD_DKIM_KEY_EDDSA) {
+		if (memcmp(sk->key.key_eddsa + 32, pk->key.key_eddsa, 32) != 0) {
+			g_set_error (err, dkim_error_quark (), DKIM_SIGERROR_KEYHASHMISMATCH,
+					"pubkey does not match private key");
+			return FALSE;
+		}
 
-	if (BN_cmp (n1, n2) != 0) {
+	}
+	else if (EVP_PKEY_cmp (pk->key_evp, sk->key_evp) != 1) {
 		g_set_error (err, dkim_error_quark (), DKIM_SIGERROR_KEYHASHMISMATCH,
 				"pubkey does not match private key");
 		return FALSE;
 	}
 
 	return TRUE;
-}
\ No newline at end of file
+}


More information about the Commits mailing list