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