]> git.ipfire.org Git - thirdparty/rspamd.git/commitdiff
Initial eddsa signing implementation
authorJohn McKay <adenosine3p@gmail.com>
Thu, 17 Jan 2019 13:02:52 +0000 (13:02 +0000)
committerJohn McKay <adenosine3p@gmail.com>
Sat, 2 Feb 2019 05:30:00 +0000 (05:30 +0000)
src/libserver/dkim.c

index 4b49d1e6f0fca74db5df55ffb56b5378de6149a8..d4c54b422b82387c54a4b31b7a457306605041b8 100644 (file)
@@ -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>
 /* 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
+}