Hello misc@, I'm currently working on adding ed25519 support to filter-dkimsign, but I'm getting some mixed results with the different validators.
gmail: permfail => claims the key is missing outlook: fail (signature syntax error) protonmail: 2 headers 1st: permerror (0-bit key) 2nd: pass yahoo: permfail dkimvalidator.com: ed25519 not supported rspamd: pass I'm having some mixed feelings on how to interpret these results. On the one hand I'm getting passes, so it appears to work in general. But when I read RFC6373 section 3.3.4 it states: Other algorithms MAY be defined in the future. Verifiers MUST ignore any signatures using algorithms that they do not implement. So I wouldn't expect these fails. Does anyone on this list know which validators are known to verify ed25519 correctly, or which explicitly are known to fail? If someone wants to help me test my current work, work in progress diff below. This is to be applied on top of http://imperialat.at/dev/filter-dkimsign/ Requires openssl 1.1.1 from ports. martijn@ Index: Makefile =================================================================== --- Makefile (revision 60) +++ Makefile (working copy) @@ -6,6 +6,10 @@ SRCS+= main.c mheader.c +CRYPT_CFLAGS!=pkg-config --cflags libecrypto11 +CRYPT_LDFLAGS!=pkg-config --libs-only-L libecrypto11 +CRYPT_LDADD!=pkg-config --libs-only-l libecrypto11 + CFLAGS+=-I${LOCALBASE}/include CFLAGS+=-Wall -I${.CURDIR} CFLAGS+=-Wstrict-prototypes -Wmissing-prototypes @@ -12,8 +16,10 @@ CFLAGS+=-Wmissing-declarations CFLAGS+=-Wshadow -Wpointer-arith -Wcast-qual CFLAGS+=-Wsign-compare +CFLAGS+=${CRYPT_CFLAGS} LDFLAGS+=-L${LOCALBASE}/lib -LDADD+= -lcrypto -lopensmtpd +LDFLAGS+=${CRYPT_LDFLAGS} +LDADD+= ${CRYPT_LDADD} -lopensmtpd DPADD= ${LIBCRYPTO} bindir: Index: main.c =================================================================== --- main.c (revision 60) +++ main.c (working copy) @@ -45,8 +45,7 @@ int has_body; struct dkim_signature signature; int err; - EVP_MD_CTX *b; - EVP_MD_CTX *bh; + EVP_MD_CTX *dctx; }; /* RFC 6376 section 5.4.1 */ @@ -92,6 +91,8 @@ static EVP_PKEY *pkey; static const EVP_MD *hash_md; +static int keyid = EVP_PKEY_RSA; +static int sephash = 0; #define DKIM_SIGNATURE_LINELEN 78 @@ -124,9 +125,18 @@ while ((ch = getopt(argc, argv, "a:c:d:h:k:s:tx:z")) != -1) { switch (ch) { case 'a': - if (strncmp(optarg, "rsa-", 4) != 0) - osmtpd_err(1, "invalid algorithm"); - hashalg = optarg + 4; + if (strncmp(optarg, "rsa-", 4) == 0) { + hashalg = optarg + 4; + cryptalg = "rsa"; + keyid = EVP_PKEY_RSA; + sephash = 0; + } else if (strncmp(optarg, "ed25519-", 8) == 0) { + hashalg = optarg + 8; + cryptalg = "ed25519"; + keyid = EVP_PKEY_ED25519; + sephash = 1; + } else + osmtpd_errx(1, "invalid algorithm"); break; case 'c': if (strncmp(optarg, "simple", 6) == 0) { @@ -166,8 +176,6 @@ pkey = PEM_read_PrivateKey(keyfile, NULL, NULL, NULL); if (pkey == NULL) osmtpd_errx(1, "Can't read key file"); - if (EVP_PKEY_get0_RSA(pkey) == NULL) - osmtpd_err(1, "Key is not of type rsa"); fclose(keyfile); break; case 's': @@ -190,15 +198,19 @@ } OpenSSL_add_all_digests(); - if ((hash_md = EVP_get_digestbyname(hashalg)) == NULL) - osmtpd_errx(1, "Can't find hash: %s", hashalg); if (pledge("tmppath stdio", NULL) == -1) osmtpd_err(1, "pledge"); + if ((hash_md = EVP_get_digestbyname(hashalg)) == NULL) + osmtpd_errx(1, "Can't find hash: %s", hashalg); + if (domain == NULL || selector == NULL || pkey == NULL) usage(); + if (EVP_PKEY_id(pkey) != keyid) + osmtpd_errx(1, "Key is not of type %s", cryptalg); + osmtpd_register_filter_dataline(dkim_dataline); osmtpd_register_filter_commit(dkim_commit); osmtpd_local_message(dkim_message_new, dkim_message_free); @@ -293,13 +305,11 @@ if (addheaders > 0 && !dkim_signature_printf(message, "z=")) return NULL; - if ((message->b = EVP_MD_CTX_new()) == NULL || - (message->bh = EVP_MD_CTX_new()) == NULL) { + if ((message->dctx = EVP_MD_CTX_new()) == NULL) { dkim_errx(message, "Can't create hash context"); return NULL; } - if (EVP_DigestSignInit(message->b, NULL, hash_md, NULL, pkey) <= 0 || - EVP_DigestInit_ex(message->bh, hash_md, NULL) == 0) { + if (EVP_DigestInit_ex(message->dctx, hash_md, NULL) == 0) { dkim_errx(message, "Failed to initialize hash context"); return NULL; } @@ -313,8 +323,7 @@ size_t i; fclose(message->origf); - EVP_MD_CTX_free(message->b); - EVP_MD_CTX_free(message->bh); + EVP_MD_CTX_free(message->dctx); free(message->signature.signature); for (i = 0; message->headers[i] != NULL; i++) free(message->headers[i]); @@ -514,7 +523,7 @@ } while (message->body_whitelines--) { - if (EVP_DigestUpdate(message->bh, "\r\n", 2) == 0) { + if (EVP_DigestUpdate(message->dctx, "\r\n", 2) == 0) { dkim_err(message, "Can't update hash context"); return; } @@ -522,8 +531,8 @@ message->body_whitelines = 0; message->has_body = 1; - if (EVP_DigestUpdate(message->bh, line, linelen) == 0 || - EVP_DigestUpdate(message->bh, "\r\n", 2) == 0) { + if (EVP_DigestUpdate(message->dctx, line, linelen) == 0 || + EVP_DigestUpdate(message->dctx, "\r\n", 2) == 0) { dkim_err(message, "Can't update hash context"); return; } @@ -534,8 +543,8 @@ { struct dkim_message *message = ctx->local_message; /* Use largest hash size here */ - char bbh[EVP_MAX_MD_SIZE]; - char bh[(((sizeof(bbh) + 2) / 3) * 4) + 1]; + char bdigest[EVP_MAX_MD_SIZE]; + char digest[(((sizeof(bdigest) + 2) / 3) * 4) + 1]; char *b; const char *sdomain = domain[0], *tsdomain; time_t now; @@ -542,6 +551,7 @@ ssize_t i; size_t linelen; char *tmp, *tmp2; + int ret, digestsz; if (addtime || addexpire) now = time(NULL); @@ -552,26 +562,47 @@ return; if (canonbody == CANON_SIMPLE && !message->has_body) { - if (EVP_DigestUpdate(message->bh, "\r\n", 2) <= 0) { + if (EVP_DigestUpdate(message->dctx, "\r\n", 2) <= 0) { dkim_err(message, "Can't update hash context"); return; } } - if (EVP_DigestFinal_ex(message->bh, bbh, NULL) == 0) { + if (EVP_DigestFinal_ex(message->dctx, bdigest, NULL) == 0) { dkim_err(message, "Can't finalize hash context"); return; } - EVP_EncodeBlock(bh, bbh, EVP_MD_CTX_size(message->bh)); - if (!dkim_signature_printf(message, "bh=%s; h=", bh)) + EVP_EncodeBlock(digest, bdigest, EVP_MD_CTX_size(message->dctx)); + if (!dkim_signature_printf(message, "bh=%s; h=", digest)) return; /* Reverse order for ease of use of RFC6367 section 5.4.2 */ for (i = 0; message->headers[i] != NULL; i++) continue; + EVP_MD_CTX_reset(message->dctx); + if (sephash) + ret = EVP_DigestInit_ex(message->dctx, hash_md, NULL); + else + ret = EVP_DigestSignInit(message->dctx, NULL, hash_md, NULL, + pkey); + if (ret <= 0) { + dkim_errx(message, "Failed to initialize hash context"); + return; + } + ret = 1; for (i--; i >= 0; i--) { - if (EVP_DigestSignUpdate(message->b, - message->headers[i], - strlen(message->headers[i])) <= 0 || - EVP_DigestSignUpdate(message->b, "\r\n", 2) <= 0) { + if (sephash) { + if (EVP_DigestUpdate(message->dctx, message->headers[i], + strlen(message->headers[i])) <= 0 || + EVP_DigestUpdate(message->dctx, "\r\n", 2) <= 0) + ret = 0; + } else { + if (EVP_DigestSignUpdate(message->dctx, + message->headers[i], + strlen(message->headers[i])) <= 0 || + EVP_DigestSignUpdate(message->dctx, "\r\n", + 2) <= 0) + ret = 0; + } + if (ret == 0) { dkim_errx(message, "Failed to update digest context"); return; } @@ -597,23 +628,51 @@ return; } dkim_parse_header(message, tmp, 1); - if (EVP_DigestSignUpdate(message->b, tmp, strlen(tmp)) <= 0) { + if (sephash) + ret = EVP_DigestUpdate(message->dctx, tmp, strlen(tmp)); + else + ret = EVP_DigestSignUpdate(message->dctx, tmp, strlen(tmp)); + if (ret <= 0) { dkim_err(message, "Failed to update digest context"); return; } free(tmp); - if (EVP_DigestSignFinal(message->b, NULL, &linelen) <= 0) { - dkim_err(message, "Failed to finalize digest"); - return; + if (sephash) { + if (EVP_DigestFinal_ex(message->dctx, bdigest, NULL) == 0) { + dkim_err(message, "Can't finalize hash context"); + return; + } + digestsz = EVP_MD_CTX_size(message->dctx); + EVP_MD_CTX_reset(message->dctx); + ret = EVP_DigestSignInit(message->dctx, NULL, NULL, NULL, + pkey); + if (EVP_DigestSign(message->dctx, NULL, &linelen, bdigest, + digestsz) <= 0) { + dkim_err(message, "Failed to finalize digest"); + } + if ((tmp = malloc(linelen)) == NULL) { + dkim_err(message, "Can't allocate space for digest"); + return; + } + if (EVP_DigestSign(message->dctx, tmp, &linelen, bdigest, + digestsz) <= 0) { + dkim_err(message, "Failed to finalize digest"); + return; + } + } else { + if (EVP_DigestSignFinal(message->dctx, NULL, &linelen) <= 0) { + dkim_err(message, "Failed to finalize digest"); + return; + } + if ((tmp = malloc(linelen)) == NULL) { + dkim_err(message, "Can't allocate space for digest"); + return; + } + if (EVP_DigestSignFinal(message->dctx, tmp, &linelen) <= 0) { + dkim_err(message, "Failed to finalize digest"); + return; + } } - if ((tmp = malloc(linelen)) == NULL) { - dkim_err(message, "Can't allocate space for digest"); - return; - } - if (EVP_DigestSignFinal(message->b, tmp, &linelen) <= 0) { - dkim_err(message, "Failed to finalize digest"); - return; - } if ((b = malloc((((linelen + 2) / 3) * 4) + 1)) == NULL) { dkim_err(message, "Can't create DKIM signature"); return;