From: John McKay Date: Thu, 17 Jan 2019 13:02:52 +0000 (+0000) Subject: Initial eddsa signing implementation X-Git-Tag: 1.9.0~220^2~13 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b73d098dcee8d3b52da56fd7d4f02cd2fc823102;p=thirdparty%2Frspamd.git Initial eddsa signing implementation --- diff --git a/src/libserver/dkim.c b/src/libserver/dkim.c index 4b49d1e6f0..d4c54b422b 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 #include @@ -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 +}