Browse Source

Initial eddsa signing implementation

tags/1.9.0
John McKay 5 years ago
parent
commit
b73d098dce
1 changed files with 135 additions and 74 deletions
  1. 135
    74
      src/libserver/dkim.c

+ 135
- 74
src/libserver/dkim.c View 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>
@@ -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;
}
}

Loading…
Cancel
Save