DKIM is a mechanism by which messages sent by some entity can be provably
linked to a domain which that entity controls. It permits reputation to
be tracked on a per-domain basis, rather than merely upon source IP address.
-DKIM is documented in RFC 4871.
+DKIM is documented in RFC 6376.
.new
As DKIM relies on the message being unchanged in transit, messages handled
Signers MUST use RSA keys of at least 1024 bits for all keys.
Signers SHOULD use RSA keys of at least 2048 bits.
.endd
+
+Note also that the key content (the 'p=' field)
+in the DNS record is different between RSA and EC keys;
+for the former it is the base64 of the ASN.1 for the RSA public key
+(equivalent to the private-key .pem with the header/trailer stripped)
+but for EC keys it is the base64 of the pure key; no ASN.1 wrapping.
.wen
.wen
&%$dkim_selector%& expansion variables to determine the private key to use.
The result can either
.ilist
-be a valid RSA private key in ASCII armor, including line breaks.
+be a valid RSA private key in ASCII armor (.pem file), including line breaks
+.new
+.next
+with GnuTLS 3.6.0 or later, be a valid Ed25519 private key (same format as above)
+.wen
.next
start with a slash, in which case it is treated as a file that contains
-the private key.
+the private key
.next
be "0", "false" or the empty string, in which case the message will not
be signed. This case will not result in an error, even if &%dkim_strict%&
.code
Signers MUST use RSA keys of at least 1024 bits for all keys.
Signers SHOULD use RSA keys of at least 2048 bits.
+
+Support for EC keys is being developed under
+&url(https://datatracker.ietf.org/doc/draft-ietf-dcrup-dkim-crypto/).
+They are considerably smaller than RSA keys for equivalent protection.
+As they are a recent development, users should consider dual-signing
+(by setting a list of selectors, and an expansion for this option)
+for some transition period.
.endd
.wen
.vitem &%$dkim_algo%&
The algorithm used. One of 'rsa-sha1' or 'rsa-sha256'.
+.new
+If running under GnuTLS 3.6.0 or later, may also be 'ed25519-sha256'.
+.wen
.new
Note that RFC 8301 says:
8. Expansion item ${sha3:<string>} / ${sha3_<N>:<string>} now also supported
under OpenSSL version 1.1.1 or later.
+ 9. DKIM operations can now use the Ed25519 algorithm in addition to RSA, under
+ GnuTLS 3.6.0 or later.
+
Version 4.90
------------
while (len-- >0)
{
- register int x, y;
+ int x, y;
x = *clear++;
*p++ = enc64table[(x >> 2) & 63];
pdkim_signature * sig;
int rc;
gstring * g = NULL;
-const uschar * errstr;
+const uschar * errstr = NULL;
store_pool = POOL_PERM;
/* Finish DKIM operation and fetch link to signatures chain */
rc = pdkim_feed_finish(dkim_verify_ctx, &dkim_signatures, &errstr);
-if (rc != PDKIM_OK)
- {
- log_write(0, LOG_MAIN, "DKIM: validation error: %.100s%s%s", pdkim_errstr(rc),
- errstr ? ": " : "", errstr ? errstr : US"");
- goto out;
- }
+if (rc != PDKIM_OK && errstr)
+ log_write(0, LOG_MAIN, "DKIM: validation error: %s", errstr);
/* Build a colon-separated list of signing domains (and identities, if present) in dkim_signers */
#ifdef USE_GNUTLS
# include <gnutls/gnutls.h>
-# if GNUTLS_VERSION_NUMBER >= 0x30000
+# if GNUTLS_VERSION_NUMBER >= 0x030000
# define SIGN_GNUTLS
+# if GNUTLS_VERSION_NUMBER >= 0x030600
+# define SIGN_HAVE_ED25519
+# endif
# else
# define SIGN_GCRYPT
# endif
};
const uschar * pdkim_keytypes[] = {
- US"rsa"
+ [KEYTYPE_RSA] = US"rsa",
+#ifdef SIGN_HAVE_ED25519
+ [KEYTYPE_ED25519] = US"ed25519", /* Works for 3.6.0 GnuTLS */
+#endif
+
+#ifdef notyet_EC_dkim_extensions /* https://tools.ietf.org/html/draft-srose-dkim-ecc-00 */
+ US"eccp256",
+ US"eccp348",
+ US"ed448",
+#endif
};
typedef struct pdkim_combined_canon_entry {
- const uschar * str;
- int canon_headers;
- int canon_body;
+ const uschar * str;
+ int canon_headers;
+ int canon_body;
} pdkim_combined_canon_entry;
pdkim_combined_canon_entry pdkim_combined_canons[] = {
{
case PDKIM_OK: return US"OK";
case PDKIM_FAIL: return US"FAIL";
- case PDKIM_ERR_RSA_PRIVKEY: return US"RSA_PRIVKEY";
- case PDKIM_ERR_RSA_SIGNING: return US"RSA SIGNING";
- case PDKIM_ERR_LONG_LINE: return US"RSA_LONG_LINE";
+ case PDKIM_ERR_RSA_PRIVKEY: return US"PRIVKEY";
+ case PDKIM_ERR_RSA_SIGNING: return US"SIGNING";
+ case PDKIM_ERR_LONG_LINE: return US"LONG_LINE";
case PDKIM_ERR_BUFFER_TOO_SMALL: return US"BUFFER_TOO_SMALL";
case PDKIM_SIGN_PRIVKEY_WRAP: return US"PRIVKEY_WRAP";
case PDKIM_SIGN_PRIVKEY_B64D: return US"PRIVKEY_B64D";
switch (*cur_tag->s)
{
- case 'b':
+ case 'b': /* sig-data or body-hash */
switch (cur_tag->s[1])
{
case '\0': pdkim_decode_base64(cur_val->s, &sig->sighash); break;
default: break;
}
break;
- case 'v':
+ case 'v': /* version */
/* We only support version 1, and that is currently the
only version there is. */
sig->version =
Ustrcmp(cur_val->s, PDKIM_SIGNATURE_VERSION) == 0 ? 1 : -1;
break;
- case 'a':
+ case 'a': /* algorithm */
{
uschar * s = Ustrchr(cur_val->s, '-');
for(i = 0; i < nelem(pdkim_keytypes); i++)
if (Ustrncmp(cur_val->s, pdkim_keytypes[i], s - cur_val->s) == 0)
{ sig->keytype = i; break; }
+ if (sig->keytype < 0)
+ log_write(0, LOG_MAIN,
+ "DKIM: ignoring signature due to nonhandled keytype in a=%s",
+ cur_val->s);
+
for (++s, i = 0; i < nelem(pdkim_hashes); i++)
if (Ustrcmp(s, pdkim_hashes[i].dkim_hashname) == 0)
{ sig->hashtype = i; break; }
+ if (sig->hashtype < 0)
+ log_write(0, LOG_MAIN,
+ "DKIM: ignoring signature due to nonhandled hashtype in a=%s",
+ cur_val);
break;
}
- case 'c':
+ case 'c': /* canonicalization */
for (i = 0; pdkim_combined_canons[i].str; i++)
if (Ustrcmp(cur_val->s, pdkim_combined_canons[i].str) == 0)
{
break;
}
break;
- case 'q':
+ case 'q': /* Query method (for pubkey)*/
for (i = 0; pdkim_querymethods[i]; i++)
if (Ustrcmp(cur_val->s, pdkim_querymethods[i]) == 0)
{
- sig->querymethod = i;
+ sig->querymethod = i; /* we never actually use this */
break;
}
break;
- case 's':
+ case 's': /* Selector */
sig->selector = string_copyn(cur_val->s, cur_val->ptr); break;
- case 'd':
+ case 'd': /* SDID */
sig->domain = string_copyn(cur_val->s, cur_val->ptr); break;
- case 'i':
+ case 'i': /* AUID */
sig->identity = pdkim_decode_qp(cur_val->s); break;
- case 't':
+ case 't': /* Timestamp */
sig->created = strtoul(CS cur_val->s, NULL, 10); break;
- case 'x':
+ case 'x': /* Expiration */
sig->expires = strtoul(CS cur_val->s, NULL, 10); break;
- case 'l':
+ case 'l': /* Body length count */
sig->bodylength = strtol(CS cur_val->s, NULL, 10); break;
- case 'h':
+ case 'h': /* signed header fields */
sig->headernames = string_copyn(cur_val->s, cur_val->ptr); break;
- case 'z':
+ case 'z': /* Copied headfields */
sig->copiedheaders = pdkim_decode_qp(cur_val->s); break;
+/*XXX draft-ietf-dcrup-dkim-crypto-05 would need 'p' tag support
+for rsafp signatures. But later discussion is dropping those. */
default:
DEBUG(D_acl) debug_printf(" Unknown tag encountered\n");
break;
*q++ = c;
}
+if (sig->keytype < 0 || sig->hashtype < 0) /* Cannot verify this signature */
+ return NULL;
+
*q = '\0';
/* Chomp raw header. The final newline must not be added to the signature. */
while (--q > sig->rawsig_no_b_val && (*q == '\r' || *q == '\n'))
{
case 'v': pub->version = val; break;
case 'h': pub->hashes = val; break;
- case 'k': break;
+ case 'k': pub->keytype = val; break;
case 'g': pub->granularity = val; break;
case 'n': pub->notes = pdkim_decode_qp(val); break;
case 'p': pdkim_decode_base64(val, &pub->key); break;
}
if (!pub->granularity) pub->granularity = US"*";
-/*
if (!pub->keytype ) pub->keytype = US"rsa";
-*/
if (!pub->srvtype ) pub->srvtype = US"*";
/* p= is required */
"PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
/* Import public key */
-if ((*errstr = exim_dkim_verify_init(&p->key, vctx)))
+
+if ((*errstr = exim_dkim_verify_init(&p->key,
+ sig->keytype == KEYTYPE_ED25519 ? KEYFMT_ED25519_BARE : KEYFMT_DER,
+ vctx)))
{
DEBUG(D_acl) debug_printf("verify_init: %s\n", *errstr);
sig->verify_status = PDKIM_VERIFY_INVALID;
return NULL;
}
+vctx->keytype = sig->keytype;
return p;
}
{
pdkim_bodyhash * b;
pdkim_signature * sig;
+BOOL verify_pass = FALSE;
+es_ctx sctx;
/* Check if we must still flush a (partial) header. If that is the
case, the message has no body, and we must compute a body hash
DEBUG(D_acl) debug_printf(
"PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
+if (!ctx->sig)
+ {
+ DEBUG(D_acl) debug_printf("PDKIM: no signatures\n");
+ return PDKIM_OK;
+ }
+
/* Build (and/or evaluate) body hash */
pdkim_finish_bodyhash(ctx);
uschar * sig_hdr = US"";
blob hhash;
gstring * hdata = NULL;
+ es_ctx sctx;
+
+ /*XXX The hash of the headers is needed for GCrypt (for which we can do RSA
+ suging only, as it happens) and for either GnuTLS and OpenSSL when we are
+ signing with EC (specifically, Ed25519). The former is because the GCrypt
+ signing operation is pure (does not do its own hash) so we must hash. The
+ latter is because we (stupidly, but this is what the IETF draft is saying)
+ must hash with the declared hash method, then pass the result to the library
+ hash-and-sign routine (because that's all the libraries are providing. And
+ we're stuck with whatever that hidden hash method is, too). We may as well
+ do this hash incrementally.
+ We don't need the hash we're calculating here for the GnuTLS and OpenSSL
+ cases of RSA signing, since those library routines can do hash-and-sign.
+
+ Some time in the future we could easily avoid doing the hash here for those
+ cases (which will be common for a long while. We could also change from
+ the current copy-all-the-headers-into-one-block, then call the hash-and-sign
+ implementation - to a proper incremental one. Unfortunately, GnuTLS just
+ cannot do incremental - either signing or verification. Unsure about GCrypt.
+ */
+
+ /*XXX The header hash is also used (so far) by the verify operation */
if (!exim_sha_init(&hhash_ctx, pdkim_hashes[sig->hashtype].exim_hashmethod))
{
- DEBUG(D_acl)
- debug_printf("PDKIM: hash setup error, possibly nonhandled hashtype\n");
+ log_write(0, LOG_MAIN|LOG_PANIC,
+ "PDKIM: hash setup error, possibly nonhandled hashtype");
break;
}
uschar * s;
int sep = 0;
- sig->headernames = NULL; /* Collected signed header names */
+ /* Import private key, including the keytype which we need for building
+ the signature header */
+
+/*XXX extend for non-RSA algos */
+ if ((*err = exim_dkim_signing_init(US sig->privkey, &sctx)))
+ {
+ log_write(0, LOG_MAIN|LOG_PANIC, "signing_init: %s", *err);
+ return PDKIM_ERR_RSA_PRIVKEY;
+ }
+ sig->keytype = sctx.keytype;
- for (p = sig->headers; p; p = p->next)
+ for (sig->headernames = NULL, /* Collected signed header names */
+ p = sig->headers; p; p = p->next)
{
uschar * rh = p->value;
exim_sha_update(&hhash_ctx, CUS rh, Ustrlen(rh));
/* Remember headers block for signing (when the library cannot do incremental) */
+ /*XXX we could avoid doing this for all but the GnuTLS/RSA case */
hdata = exim_dkim_data_append(hdata, rh);
DEBUG(D_acl) pdkim_quoteprint(rh, Ustrlen(rh));
/* SIGNING ---------------------------------------------------------------- */
if (ctx->flags & PDKIM_MODE_SIGN)
{
- es_ctx sctx;
+ hashmethod hm = sig->keytype == KEYTYPE_ED25519
+ ? HASH_SHA2_512 : pdkim_hashes[sig->hashtype].exim_hashmethod;
- /* Import private key, including the keytype */
-/*XXX extend for non-RSA algos */
- if ((*err = exim_dkim_signing_init(US sig->privkey, &sctx)))
- {
- DEBUG(D_acl) debug_printf("signing_init: %s\n", *err);
- return PDKIM_ERR_RSA_PRIVKEY;
- }
-
- /* Do signing. With OpenSSL we are signing the hash of headers just
- calculated, with GnuTLS we have to sign an entire block of headers
- (due to available interfaces) and it recalculates the hash internally. */
+#ifdef SIGN_HAVE_ED25519
+ /* For GCrypt, and for EC, we pass the hash-of-headers to the signing
+ routine. For anything else we just pass the headers. */
-#if defined(SIGN_GNUTLS)
- hhash.data = hdata->s;
- hhash.len = hdata->ptr;
+ if (sig->keytype != KEYTYPE_ED25519)
#endif
+ {
+ hhash.data = hdata->s;
+ hhash.len = hdata->ptr;
+ }
/*XXX extend for non-RSA algos */
- if ((*err = exim_dkim_sign(&sctx,
- pdkim_hashes[sig->hashtype].exim_hashmethod,
- &hhash, &sig->sighash)))
+/*- done for GnuTLS */
+ if ((*err = exim_dkim_sign(&sctx, hm, &hhash, &sig->sighash)))
{
- DEBUG(D_acl) debug_printf("signing: %s\n", *err);
+ log_write(0, LOG_MAIN|LOG_PANIC, "signing: %s", *err);
return PDKIM_ERR_RSA_SIGNING;
}
}
if (!(sig->pubkey = pdkim_key_from_dns(ctx, sig, &vctx, err)))
+ {
+ log_write(0, LOG_MAIN, "PDKIM: %s%s %s%s [failed key import]",
+ sig->domain ? "d=" : "", sig->domain ? sig->domain : US"",
+ sig->selector ? "s=" : "", sig->selector ? sig->selector : US"");
goto NEXT_VERIFY;
+ }
/* If the pubkey limits to a list of specific hashes, ignore sigs that
do not have the hash part of the sig algorithm matching */
}
/* Check the signature */
-/*XXX needs extension for non-RSA */
+/*XXX extend for non-RSA algos */
+/*- done for GnuTLS */
if ((*err = exim_dkim_verify(&vctx,
- pdkim_hashes[sig->hashtype].exim_hashmethod,
- &hhash, &sig->sighash)))
+ pdkim_hashes[sig->hashtype].exim_hashmethod,
+ &hhash, &sig->sighash)))
{
DEBUG(D_acl) debug_printf("headers verify: %s\n", *err);
sig->verify_status = PDKIM_VERIFY_FAIL;
/* We have a winner! (if bodyhash was correct earlier) */
if (sig->verify_status == PDKIM_VERIFY_NONE)
+ {
sig->verify_status = PDKIM_VERIFY_PASS;
+ verify_pass = TRUE;
+ }
NEXT_VERIFY:
DEBUG(D_acl)
{
- debug_printf("PDKIM [%s] signature status: %s",
- sig->domain, pdkim_verify_status_str(sig->verify_status));
+ debug_printf("PDKIM [%s] %s signature status: %s",
+ sig->domain, dkim_sig_to_a_tag(sig),
+ pdkim_verify_status_str(sig->verify_status));
if (sig->verify_ext_status > 0)
debug_printf(" (%s)\n",
pdkim_verify_ext_status_str(sig->verify_ext_status));
if (return_signatures)
*return_signatures = ctx->sig;
-return PDKIM_OK;
+return ctx->flags & PDKIM_MODE_SIGN || verify_pass
+ ? PDKIM_OK : PDKIM_FAIL;
}
/* -------------------------------------------------------------------------- */
-/*XXX ? needs extension to cover non-RSA algo? */
-
DLLEXPORT pdkim_signature *
pdkim_init_sign(pdkim_ctx * ctx,
uschar * domain, uschar * selector, uschar * privkey,
sig->domain = string_copy(US domain);
sig->selector = string_copy(US selector);
sig->privkey = string_copy(US privkey);
-/*XXX no keytype yet; comes from privkey */
+sig->keytype = -1;
for (hashtype = 0; hashtype < nelem(pdkim_hashes); hashtype++)
if (Ustrcmp(hashname, pdkim_hashes[hashtype].dkim_hashname) == 0)
{ sig->hashtype = hashtype; break; }
if (hashtype >= nelem(pdkim_hashes))
{
- DEBUG(D_acl)
- debug_printf("PDKIM: unrecognised hashname '%s'\n", hashname);
+ log_write(0, LOG_MAIN|LOG_PANIC,
+ "PDKIM: unrecognised hashname '%s'", hashname);
return NULL;
}
/* Some parameter values */
#define PDKIM_QUERYMETHOD_DNS_TXT 0
+/*#define PDKIM_ALGO_RSA_SHA256 0 */
+/*#define PDKIM_ALGO_RSA_SHA1 1 */
+
#define PDKIM_CANON_SIMPLE 0
#define PDKIM_CANON_RELAXED 1
const uschar *granularity; /* g= */
const uschar * hashes; /* h= */
-#ifdef notdef
- uschar *keytype; /* k= */
-#endif
- const uschar *srvtype; /* s= */
+ const uschar * keytype; /* k= */
+ const uschar * srvtype; /* s= */
uschar *notes; /* n= */
blob key; /* p= */
/* (v=) The version, as an integer. Currently, always "1" */
int version;
+ /* (a=) The signature algorithm. Either PDKIM_ALGO_RSA_SHA256 */
int keytype; /* pdkim_keytypes index */
int hashtype; /* pdkim_hashes index */
/******************************************************************************/
#ifdef SIGN_GNUTLS
+# define EXIM_GNUTLS_LIBRARY_LOG_LEVEL 3
+
+
+/* Logging function which can be registered with
+ * gnutls_global_set_log_function()
+ * gnutls_global_set_log_level() 0..9
+ */
+#if EXIM_GNUTLS_LIBRARY_LOG_LEVEL >= 0
+static void
+exim_gnutls_logger_cb(int level, const char *message)
+{
+size_t len = strlen(message);
+if (len < 1)
+ {
+ DEBUG(D_tls) debug_printf("GnuTLS<%d> empty debug message\n", level);
+ return;
+ }
+DEBUG(D_tls) debug_printf("GnuTLS<%d>: %s%s", level, message,
+ message[len-1] == '\n' ? "" : "\n");
+}
+#endif
+
+
void
exim_dkim_init(void)
{
+#if EXIM_GNUTLS_LIBRARY_LOG_LEVEL >= 0
+DEBUG(D_tls)
+ {
+ gnutls_global_set_log_function(exim_gnutls_logger_cb);
+ /* arbitrarily chosen level; bump upto 9 for more */
+ gnutls_global_set_log_level(EXIM_GNUTLS_LIBRARY_LOG_LEVEL);
+ }
+#endif
}
const uschar *
exim_dkim_signing_init(uschar * privkey_pem, es_ctx * sign_ctx)
{
-gnutls_datum_t k;
+gnutls_datum_t k = { .data = privkey_pem, .size = Ustrlen(privkey_pem) };
+gnutls_x509_privkey_t x509_key;
int rc;
-k.data = privkey_pem;
-k.size = strlen(privkey_pem);
-
-if ( (rc = gnutls_x509_privkey_init(&sign_ctx->key)) != GNUTLS_E_SUCCESS
- || (rc = gnutls_x509_privkey_import(sign_ctx->key, &k,
- GNUTLS_X509_FMT_PEM)) != GNUTLS_E_SUCCESS
+if ( (rc = gnutls_x509_privkey_init(&x509_key))
+ || (rc = gnutls_x509_privkey_import(x509_key, &k, GNUTLS_X509_FMT_PEM))
+ || (rc = gnutls_privkey_init(&sign_ctx->key))
+ || (rc = gnutls_privkey_import_x509(sign_ctx->key, x509_key, 0))
)
- return gnutls_strerror(rc);
+ return CUS gnutls_strerror(rc);
+
+switch (rc = gnutls_privkey_get_pk_algorithm(sign_ctx->key, NULL))
+ {
+ case GNUTLS_PK_RSA: sign_ctx->keytype = KEYTYPE_RSA; break;
+#ifdef SIGN_HAVE_ED25519
+ case GNUTLS_PK_EDDSA_ED25519: sign_ctx->keytype = KEYTYPE_ED25519; break;
+#endif
+ default: return rc < 0
+ ? CUS gnutls_strerror(rc)
+ : string_sprintf("Unhandled key type: %d '%s'", rc, gnutls_pk_get_name(rc));
+ }
return NULL;
}
/* allocate mem for signature (when signing) */
-/* sign data (gnutls_only)
-OR
-sign hash.
+/* hash & sign data. No way to do incremental.
Return: NULL for success, or an error string */
const uschar *
exim_dkim_sign(es_ctx * sign_ctx, hashmethod hash, blob * data, blob * sig)
{
+gnutls_datum_t k_data = { .data = data->data, .size = data->len };
gnutls_digest_algorithm_t dig;
-gnutls_datum_t k;
-size_t sigsize = 0;
+gnutls_datum_t k_sig;
int rc;
-const uschar * ret = NULL;
switch (hash)
{
default: return US"nonhandled hash type";
}
-/* Allocate mem for signature */
-k.data = data->data;
-k.size = data->len;
-(void) gnutls_x509_privkey_sign_data(sign_ctx->key, dig,
- 0, &k, NULL, &sigsize);
+if ((rc = gnutls_privkey_sign_data(sign_ctx->key, dig, 0, &k_data, &k_sig)))
+ return CUS gnutls_strerror(rc);
-sig->data = store_get(sigsize);
-sig->len = sigsize;
-
-/* Do signing */
-if ((rc = gnutls_x509_privkey_sign_data(sign_ctx->key, dig,
- 0, &k, sig->data, &sigsize)) != GNUTLS_E_SUCCESS
- )
- ret = gnutls_strerror(rc);
+/* Don't care about deinit for the key; shortlived process */
-gnutls_x509_privkey_deinit(sign_ctx->key);
-return ret;
+sig->data = k_sig.data;
+sig->len = k_sig.size;
+return NULL;
}
-/* import public key (from DER in memory)
+/* import public key (from blob in memory)
Return: NULL for success, or an error string */
const uschar *
-exim_dkim_verify_init(blob * pubkey_der, ev_ctx * verify_ctx)
+exim_dkim_verify_init(blob * pubkey, keyformat fmt, ev_ctx * verify_ctx)
{
gnutls_datum_t k;
int rc;
const uschar * ret = NULL;
gnutls_pubkey_init(&verify_ctx->key);
+k.data = pubkey->data;
+k.size = pubkey->len;
-k.data = pubkey_der->data;
-k.size = pubkey_der->len;
-
-if ((rc = gnutls_pubkey_import(verify_ctx->key, &k, GNUTLS_X509_FMT_DER))
- != GNUTLS_E_SUCCESS)
- ret = gnutls_strerror(rc);
+switch(fmt)
+ {
+ case KEYFMT_DER:
+ if ((rc = gnutls_pubkey_import(verify_ctx->key, &k, GNUTLS_X509_FMT_DER)))
+ ret = gnutls_strerror(rc);
+ break;
+#ifdef SIGN_HAVE_ED25519
+ case KEYFMT_ED25519_BARE:
+ if ((rc = gnutls_pubkey_import_ecc_raw(verify_ctx->key,
+ GNUTLS_ECC_CURVE_ED25519, &k, NULL)))
+ ret = gnutls_strerror(rc);
+ break;
+#endif
+ default:
+ ret = US"pubkey format not handled";
+ break;
+ }
return ret;
}
-/* verify signature (of hash) (given pubkey & alleged sig)
+/* verify signature (of hash if RSA sig, of data if EC sig. No way to do incremental)
+(given pubkey & alleged sig)
Return: NULL for success, or an error string */
const uschar *
exim_dkim_verify(ev_ctx * verify_ctx, hashmethod hash, blob * data_hash, blob * sig)
{
-gnutls_sign_algorithm_t algo;
-gnutls_datum_t k, s;
+gnutls_datum_t k = { .data = data_hash->data, .size = data_hash->len };
+gnutls_datum_t s = { .data = sig->data, .size = sig->len };
int rc;
const uschar * ret = NULL;
-/*XXX needs extension for non-rsa */
-switch (hash)
+#ifdef SIGN_HAVE_ED25519
+if (verify_ctx->keytype == KEYTYPE_ED25519)
{
- case HASH_SHA1: algo = GNUTLS_SIGN_RSA_SHA1; break;
- case HASH_SHA2_256: algo = GNUTLS_SIGN_RSA_SHA256; break;
- case HASH_SHA2_512: algo = GNUTLS_SIGN_RSA_SHA512; break;
- default: return US"nonhandled hash type";
+ if ((rc = gnutls_pubkey_verify_data2(verify_ctx->key,
+ GNUTLS_SIGN_EDDSA_ED25519, 0, &k, &s)) < 0)
+ ret = gnutls_strerror(rc);
}
+else
+#endif
+ {
+ gnutls_sign_algorithm_t algo;
+ switch (hash)
+ {
+ case HASH_SHA1: algo = GNUTLS_SIGN_RSA_SHA1; break;
+ case HASH_SHA2_256: algo = GNUTLS_SIGN_RSA_SHA256; break;
+ case HASH_SHA2_512: algo = GNUTLS_SIGN_RSA_SHA512; break;
+ default: return US"nonhandled hash type";
+ }
-k.data = data_hash->data;
-k.size = data_hash->len;
-s.data = sig->data;
-s.size = sig->len;
-if ((rc = gnutls_pubkey_verify_hash2(verify_ctx->key, algo, 0, &k, &s)) < 0)
- ret = gnutls_strerror(rc);
+ if ((rc = gnutls_pubkey_verify_hash2(verify_ctx->key, algo, 0, &k, &s)) < 0)
+ ret = gnutls_strerror(rc);
+ }
gnutls_pubkey_deinit(verify_ctx->key);
return ret;
int taglen;
long tag, len;
-/* debug_printf_indent("as_tag: %02x %02x %02x %02x\n",
- der->data[0], der->data[1], der->data[2], der->data[3]); */
+debug_printf_indent("as_tag: %02x %02x %02x %02x\n",
+ der->data[0], der->data[1], der->data[2], der->data[3]);
if ((rc = asn1_get_tag_der(der->data++, der->len--, &tag_class, &taglen, &tag))
!= ASN1_SUCCESS)
int rc;
gcry_error_t gerr;
+debug_printf_indent("%s\n", __FUNCTION__);
+
/* integer; move past the header */
if ((rc = as_tag(der, 0, ASN1_TAG_INTEGER, &alen)) != ASN1_SUCCESS)
return US asn1_strerror(rc);
/* import private key from PEM string in memory.
+Only handles RSA keys.
Return: NULL for success, or an error string */
const uschar *
int rc;
/*XXX will need extension to _spot_ as well as handle a
-non-RSA key? I think... */
+non-RSA key? I think...
+So... this is not a PrivateKeyInfo - which would have a field
+identifying the keytype - PrivateKeyAlgorithmIdentifier -
+but a plain RSAPrivateKey (wrapped in PEM-headers. Can we
+use those as a type tag? What forms are there? "BEGIN EC PRIVATE KEY" (cf. ec(1ssl))
+
+How does OpenSSL PEM_read_bio_PrivateKey() deal with it?
+gnutls_x509_privkey_import() ?
+*/
/*
* RSAPrivateKey ::= SEQUENCE
* exponent2 INTEGER, -- d mod (q-1)
* coefficient INTEGER, -- (inverse of q) mod p
* otherPrimeInfos OtherPrimeInfos OPTIONAL
+
+ * ECPrivateKey ::= SEQUENCE {
+ * version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1),
+ * privateKey OCTET STRING,
+ * parameters [0] ECParameters {{ NamedCurve }} OPTIONAL,
+ * publicKey [1] BIT STRING OPTIONAL
+ * }
+ * Hmm, only 1 useful item, and not even an integer? Wonder how we might use it...
+
+- actually, gnutls_x509_privkey_import() appears to require a curve name parameter
+ value for that is an OID? a local-only integer (it's an enum in GnuTLS)?
+
+
+Useful cmds:
+ ssh-keygen -t ecdsa -f foo.privkey
+ ssh-keygen -t ecdsa -b384 -f foo.privkey
+ ssh-keygen -t ecdsa -b521 -f foo.privkey
+ ssh-keygen -t ed25519 -f foo.privkey
+
+ < foo openssl pkcs8 -in /dev/stdin -inform PEM -nocrypt -topk8 -outform DER | od -x
+
+ openssl asn1parse -in foo -inform PEM -dump
+ openssl asn1parse -in foo -inform PEM -dump -stroffset 24 (??)
+(not good for ed25519)
+
*/
if ( !(s1 = Ustrstr(CS privkey_pem, "-----BEGIN RSA PRIVATE KEY-----"))
debug_printf_indent(" QP: %s\n", s);
}
#endif
+
+sign_ctx->keytype = KEYTYPE_RSA;
return NULL;
asn_err: return US asn1_strerror(rc);
/* allocate mem for signature (when signing) */
-/* sign data (gnutls_only)
-OR
-sign hash.
+/* sign already-hashed data.
Return: NULL for success, or an error string */
}
-/* import public key (from DER in memory)
+/* import public key (from blob in memory)
Return: NULL for success, or an error string */
const uschar *
-exim_dkim_verify_init(blob * pubkey_der, ev_ctx * verify_ctx)
+exim_dkim_verify_init(blob * pubkey, keyformat fmt, ev_ctx * verify_ctx)
{
/*
in code sequence per b81207d2bfa92 rsa_parse_public_key() and asn1_get_mpi()
gcry_error_t gerr;
uschar * stage = US"S1";
+if (fmt != KEYFMT_DER) return US"pubkey format not handled";
+
/*
sequence
sequence
*/
/* sequence; just move past the header */
-if ((rc = as_tag(pubkey_der, ASN1_CLASS_STRUCTURED, ASN1_TAG_SEQUENCE, NULL))
+if ((rc = as_tag(pubkey, ASN1_CLASS_STRUCTURED, ASN1_TAG_SEQUENCE, NULL))
!= ASN1_SUCCESS) goto asn_err;
/* sequence; skip the entire thing */
DEBUG(D_acl) stage = US"S2";
-if ((rc = as_tag(pubkey_der, ASN1_CLASS_STRUCTURED, ASN1_TAG_SEQUENCE, &alen))
+if ((rc = as_tag(pubkey, ASN1_CLASS_STRUCTURED, ASN1_TAG_SEQUENCE, &alen))
!= ASN1_SUCCESS) goto asn_err;
-pubkey_der->data += alen; pubkey_der->len -= alen;
+pubkey->data += alen; pubkey->len -= alen;
/* bitstring: limit range to size of bitstring;
move over header + content wrapper */
DEBUG(D_acl) stage = US"BS";
-if ((rc = as_tag(pubkey_der, 0, ASN1_TAG_BIT_STRING, &alen)) != ASN1_SUCCESS)
+if ((rc = as_tag(pubkey, 0, ASN1_TAG_BIT_STRING, &alen)) != ASN1_SUCCESS)
goto asn_err;
-pubkey_der->len = alen;
-pubkey_der->data++; pubkey_der->len--;
+pubkey->len = alen;
+pubkey->data++; pubkey->len--;
/* sequence; just move past the header */
DEBUG(D_acl) stage = US"S3";
-if ((rc = as_tag(pubkey_der, ASN1_CLASS_STRUCTURED, ASN1_TAG_SEQUENCE, NULL))
+if ((rc = as_tag(pubkey, ASN1_CLASS_STRUCTURED, ASN1_TAG_SEQUENCE, NULL))
!= ASN1_SUCCESS) goto asn_err;
/* read two integers */
DEBUG(D_acl) stage = US"MPI";
-if ( (errstr = as_mpi(pubkey_der, &verify_ctx->n))
- || (errstr = as_mpi(pubkey_der, &verify_ctx->e))
+if ( (errstr = as_mpi(pubkey, &verify_ctx->n))
+ || (errstr = as_mpi(pubkey, &verify_ctx->e))
)
return errstr;
}
-/* verify signature (of hash) (given pubkey & alleged sig)
+/* verify signature (of hash)
+XXX though we appear to be doing a hash, too!
+(given pubkey & alleged sig)
Return: NULL for success, or an error string */
const uschar *
}
-/* accumulate data (gnutls-only) */
+/* accumulate data (was gnutls-onl but now needed for OpenSSL non-EC too
+because now using hash-and-sign interface) */
gstring *
exim_dkim_data_append(gstring * g, uschar * s)
{
-return g; /*dummy*/
+return string_cat(g, s);
}
if (!(sign_ctx->key = PEM_read_bio_PrivateKey(bp, NULL, NULL, NULL)))
return US ERR_error_string(ERR_get_error(), NULL);
+
+sign_ctx->keytype =
+#ifdef SIGN_HAVE_ED25519
+ EVP_PKEY_type(EVP_PKEY_id(sign_ctx->key)) == EVP_PKEY_EC
+ ? KEYTYPE_ED25519 : KEYTYPE_RSA;
+#else
+ KEYTYPE_RSA;
+#endif
return NULL;
}
/* allocate mem for signature (when signing) */
-/* sign data (gnutls_only)
-OR
-sign hash.
+/* hash & sign data. Could be incremental
Return: NULL for success with the signaature in the sig blob, or an error string */
exim_dkim_sign(es_ctx * sign_ctx, hashmethod hash, blob * data, blob * sig)
{
const EVP_MD * md;
-EVP_PKEY_CTX * ctx;
+EVP_MD_CTX * ctx;
size_t siglen;
switch (hash)
default: return US"nonhandled hash type";
}
-if ( (ctx = EVP_PKEY_CTX_new(sign_ctx->key, NULL))
- && EVP_PKEY_sign_init(ctx) > 0
- && EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) > 0
- && EVP_PKEY_CTX_set_signature_md(ctx, md) > 0
- && EVP_PKEY_sign(ctx, NULL, &siglen, data->data, data->len) > 0
+/* Create the Message Digest Context */
+/*XXX renamed to EVP_MD_CTX_new() in 1.1.0 */
+if( (ctx = EVP_MD_CTX_create())
+
+/* Initialise the DigestSign operation */
+ && EVP_DigestSignInit(ctx, NULL, md, NULL, sign_ctx->key) > 0
+
+ /* Call update with the message */
+ && EVP_DigestSignUpdate(ctx, data->data, data->len) > 0
+
+ /* Finalise the DigestSign operation */
+ /* First call EVP_DigestSignFinal with a NULL sig parameter to obtain the length of the
+ * signature. Length is returned in slen */
+ && EVP_DigestSignFinal(ctx, NULL, &siglen) > 0
+
+ /* Allocate memory for the signature based on size in slen */
+ && (sig->data = store_get(siglen))
+
+ /* Obtain the signature (slen could change here!) */
+ && EVP_DigestSignFinal(ctx, sig->data, &siglen) > 0
)
{
- /* Allocate mem for signature */
- sig->data = store_get(siglen);
-
- if (EVP_PKEY_sign(ctx, sig->data, &siglen, data->data, data->len) > 0)
- {
- EVP_PKEY_CTX_free(ctx);
- sig->len = siglen;
- return NULL;
- }
+ EVP_MD_CTX_destroy(ctx);
+ sig->len = siglen;
+ return NULL;
}
-if (ctx) EVP_PKEY_CTX_free(ctx);
+if (ctx) EVP_MD_CTX_destroy(ctx);
return US ERR_error_string(ERR_get_error(), NULL);
}
-/* import public key (from DER in memory)
+/* import public key (from blob in memory)
Return: NULL for success, or an error string */
const uschar *
-exim_dkim_verify_init(blob * pubkey_der, ev_ctx * verify_ctx)
+exim_dkim_verify_init(blob * pubkey, keyformat fmt, ev_ctx * verify_ctx)
{
-const uschar * s = pubkey_der->data;
+const uschar * s = pubkey->data;
+uschar * ret = NULL;
-/*XXX hmm, we never free this */
+if (fmt != KEYFMT_DER) return US"pubkey format not handled";
+switch(fmt)
+ {
+ case KEYFMT_DER:
+ /*XXX ok, this fails for EC:
+ error:0609E09C:digital envelope routines:pkey_set_type:unsupported algorithm
+ */
+
+ /*XXX hmm, we never free this */
+ if (!(verify_ctx->key = d2i_PUBKEY(NULL, &s, pubkey->len)))
+ ret = US ERR_error_string(ERR_get_error(), NULL);
+ break;
+#ifdef SIGN_HAVE_ED25519
+ case KEYFMT_ED25519_BARE:
+ {
+ BIGNUM * x;
+ EC_KEY * eck;
+ if ( !(x = BN_bin2bn(s, pubkey->len, NULL))
+ || !(eck = EC_KEY_new_by_curve_name(NID_ED25519))
+ || !EC_KEY_set_public_key_affine_coordinates(eck, x, NULL)
+ || !(verify_ctx->key = EVP_PKEY_new())
+ || !EVP_PKEY_assign_EC_KEY(verify_ctx->key, eck)
+ )
+ ret = US ERR_error_string(ERR_get_error(), NULL);
+ }
+ break;
+#endif
+ default:
+ ret = US"pubkey format not handled";
+ break;
+ }
-if ((verify_ctx->key = d2i_PUBKEY(NULL, &s, pubkey_der->len)))
- return NULL;
-return US ERR_error_string(ERR_get_error(), NULL);
+return ret;
}
-/* verify signature (of hash) (given pubkey & alleged sig)
+/* verify signature (of hash)
+(pre-EC coding; of data if "notyet" code, The latter could be incremental)
+(given pubkey & alleged sig)
Return: NULL for success, or an error string */
const uschar *
-exim_dkim_verify(ev_ctx * verify_ctx, hashmethod hash, blob * data_hash, blob * sig)
+exim_dkim_verify(ev_ctx * verify_ctx, hashmethod hash, blob * data, blob * sig)
{
const EVP_MD * md;
-EVP_PKEY_CTX * ctx;
+
+/*XXX OpenSSL does not seem to have Ed25519 support yet. Reportedly BoringSSL does,
+but that's a nonstable API and not recommended (by its owner, Google) for external use. */
switch (hash)
{
default: return US"nonhandled hash type";
}
-if ( (ctx = EVP_PKEY_CTX_new(verify_ctx->key, NULL))
- && EVP_PKEY_verify_init(ctx) > 0
- && EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) > 0
- && EVP_PKEY_CTX_set_signature_md(ctx, md) > 0
- && EVP_PKEY_verify(ctx, sig->data, sig->len,
- data_hash->data, data_hash->len) == 1
- )
- { EVP_PKEY_CTX_free(ctx); return NULL; }
+#ifdef notyet_SIGN_HAVE_ED25519
+ {
+ EVP_MD_CTX * ctx;
-if (ctx) EVP_PKEY_CTX_free(ctx);
-return US ERR_error_string(ERR_get_error(), NULL);
+ /*XXX renamed to EVP_MD_CTX_new() in 1.1.0 */
+ if (
+ (ctx = EVP_MD_CTX_create())
+
+ /* Initialize `key` with a public key */
+ && EVP_DigestVerifyInit(ctx, NULL, md, NULL, verify_ctx->key) > 0
+
+ /* add data to be hashed (call multiple times if needed) */
+
+ && EVP_DigestVerifyUpdate(ctx, data->data, data->len) > 0
+
+ /* finish off the hash and check the offered signature */
+
+ && EVP_DigestVerifyFinal(ctx, sig->data, sig->len) > 0
+ )
+ {
+ EVP_MD_CTX_destroy(ctx); /* renamed to _free in 1.1.0 */
+ return NULL;
+ }
+
+ if (ctx) EVP_MD_CTX_free(ctx);
+ return US ERR_error_string(ERR_get_error(), NULL);
+ }
+#else
+ {
+ EVP_PKEY_CTX * ctx;
+
+ if ( (ctx = EVP_PKEY_CTX_new(verify_ctx->key, NULL))
+ && EVP_PKEY_verify_init(ctx) > 0
+ && EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) > 0
+ && EVP_PKEY_CTX_set_signature_md(ctx, md) > 0
+ && EVP_PKEY_verify(ctx, sig->data, sig->len,
+ data->data, data->len) == 1
+ )
+ { EVP_PKEY_CTX_free(ctx); return NULL; }
+
+ if (ctx) EVP_PKEY_CTX_free(ctx);
+ return US ERR_error_string(ERR_get_error(), NULL);
+ }
+#endif
}
#elif defined(SIGN_GNUTLS)
# include <gnutls/gnutls.h>
# include <gnutls/x509.h>
-# include <gnutls/abstract.h>
+# include <gnutls/abstract.h>
#elif defined(SIGN_GCRYPT)
-# include <gcrypt.h>
-# include <libtasn1.h>
+# include <gcrypt.h>
+# include <libtasn1.h>
#endif
#include "../blob.h"
+typedef enum {
+ KEYTYPE_RSA,
+ KEYTYPE_ED25519
+} keytype;
+
+typedef enum {
+ KEYFMT_DER, /* an asn.1 structure */
+ KEYFMT_ED25519_BARE /* just the key */
+} keyformat;
+
#ifdef SIGN_OPENSSL
typedef struct {
- EVP_PKEY * key;
+ keytype keytype;
+ EVP_PKEY * key;
} es_ctx;
typedef struct {
- EVP_PKEY * key;
+ keytype keytype;
+ EVP_PKEY * key;
} ev_ctx;
#elif defined(SIGN_GNUTLS)
typedef struct {
- gnutls_x509_privkey_t key;
+ keytype keytype;
+ gnutls_privkey_t key;
} es_ctx;
typedef struct {
+ keytype keytype;
gnutls_pubkey_t key;
} ev_ctx;
#elif defined(SIGN_GCRYPT)
typedef struct {
- int keytype;
+ keytype keytype;
gcry_mpi_t n;
gcry_mpi_t e;
gcry_mpi_t d;
} es_ctx;
typedef struct {
- int keytype;
+ keytype keytype;
gcry_mpi_t n;
gcry_mpi_t e;
} ev_ctx;
extern const uschar * exim_dkim_signing_init(uschar *, es_ctx *);
extern const uschar * exim_dkim_sign(es_ctx *, hashmethod, blob *, blob *);
-extern const uschar * exim_dkim_verify_init(blob *, ev_ctx *);
+extern const uschar * exim_dkim_verify_init(blob *, keyformat, ev_ctx *);
extern const uschar * exim_dkim_verify(ev_ctx *, hashmethod, blob *, blob *);
#endif /*DISABLE_DKIM*/
LDFLAGS=@LDFLAGS@
CLIENT_SSL=@CLIENT_SSL@
CLIENT_GNUTLS=@CLIENT_GNUTLS@
+B64_GNUTLS=@B64_GNUTLS@
LOADED=@LOADED@
LOADED_OPT=@LOADED_OPT@
LIBS=@LIBS@
BINARIES = bin/cf bin/client $(CLIENT_SSL) $(CLIENT_GNUTLS) \
bin/checkaccess bin/fakens bin/fd bin/iefbr14 $(LOADED) \
- bin/mtpscript bin/server bin/showids bin/locate
+ bin/mtpscript bin/server bin/showids bin/locate \
+ $(B64_GNUTLS)
# List of targets
cp $(SRC)/locate.pl bin/locate
chmod 0755 bin/locate
+bin/ed25519_privkey_pem_to_pubkey_raw_b64: $(SRC)/ed25519_privkey_pem_to_pubkey_raw_b64.c Makefile
+ $(CC) $(CFLAGS) -DHAVE_GNUTLS $(LDFLAGS) -o bin/ed25519_privkey_pem_to_pubkey_raw_b64 \
+ $(SRC)/ed25519_privkey_pem_to_pubkey_raw_b64.c -lgnutls -lgcrypt $(LIBS)
+
clean:; rm -rf $(BINARIES) bin.sys
FORCE:
--- /dev/null
+-----BEGIN PRIVATE KEY-----
+MC4CAQAwBQYDK2VwBCIEIMCVDSGjt6hBzzc/Km1UBZ7nMcvLCZSqeiay3rhuQIqF
+-----END PRIVATE KEY-----
LIBOBJS
LOADED_OPT
LOADED
+B64_GNUTLS
CLIENT_GNUTLS
CLIENT_SSL
BIND_8_COMPAT
done
+for ac_header in gnutls/gnutls.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "gnutls/gnutls.h" "ac_cv_header_gnutls_gnutls_h" "$ac_includes_default"
+if test "x$ac_cv_header_gnutls_gnutls_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_GNUTLS_GNUTLS_H 1
+_ACEOF
+ B64_GNUTLS=bin/ed25519_privkey_pem_to_pubkey_raw_b64
+fi
+
+done
+
+
ac_config_files="$ac_config_files Makefile"
cat >confcache <<\_ACEOF
AC_CHECK_HEADERS(sys/socket.h)
AC_CHECK_HEADERS(openssl/crypto.h,[CLIENT_SSL=bin/client-ssl])
AC_CHECK_HEADERS(gnutls/gnutls.h,[CLIENT_GNUTLS=bin/client-gnutls])
+AC_CHECK_HEADERS(gnutls/gnutls.h,[B64_GNUTLS=bin/ed25519_privkey_pem_to_pubkey_raw_b64])
dnl The check on dynamically loaded modules requires the building of
dnl something to load. This seems to be something that varies between
AC_SUBST(BIND_8_COMPAT)
AC_SUBST(CLIENT_SSL)
AC_SUBST(CLIENT_GNUTLS)
+AC_SUBST(B64_GNUTLS)
AC_SUBST(LOADED)
AC_SUBST(LOADED_OPT)
AC_SUBST(LIBS)
--- /dev/null
+4500
\ No newline at end of file
dkim_selector = sel
.endif
- dkim_private_key = ${if match {$dkim_selector}{^ses} {DDIR/dkim512.private} \
- {${if match {$dkim_selector}{^sel} {DDIR/dkim.private} \
- {}}}}
+ dkim_private_key = ${extract {${length_3:$dkim_selector}} {\
+ ses=dkim512.private \
+ sel=dkim.private \
+ sed=dkim_ed25519.private \
+ }{DDIR/$value}}
.ifndef HEADERS_MAXSIZE
dkim_sign_headers = OPT
--- /dev/null
+4520
\ No newline at end of file
sel2._domainkey TXT "v=spf1 mx a include:spf.nl2go.com -all"
sel2._domainkey TXT "v=DKIM1; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDXRFf+VhT+lCgFhhSkinZKcFNeRzjYdW8vT29Rbb3NadvTFwAd+cVLPFwZL8H5tUD/7JbUPqNTCPxmpgIL+V5T4tEZMorHatvvUM2qfcpQ45IfsZ+YdhbIiAslHCpy4xNxIR3zylgqRUF4+Dtsaqy3a5LhwMiKCLrnzhXk1F1hxwIDAQAB"
+; EC signing, using Ed25519
+; - needs GnuTLS 3.6.0 (fedora rawhide has that)
+; certtool --generate-privkey --key-type=ed25519 --outfile=dkim_ed25519.private
+; bin/ed25519_privkey_pem_to_pubkey_raw_b64 dkim_ed25519.private
+
+sed._domainkey TXT "v=DKIM1; k=ed25519; p=sPs07Vu29FpHT/80UXUcYHFOHifD4o2ZlP2+XUh9g6E="
+
+
; End
1999-03-02 09:44:33 10HmaZ-0005vi-00 signer: test.ex bits: 1024
1999-03-02 09:44:33 10HmaZ-0005vi-00 DKIM: d=test.ex s=sel c=relaxed/simple a=rsa-sha1 b=1024 [verification succeeded]
1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@bloggs.com H=(xxx) [127.0.0.1] P=smtp S=sss DKIM=test.ex
+1999-03-02 09:44:33 10HmbA-0005vi-00 PDKIM: d=test.ex s=sel_bad [failed key import]
1999-03-02 09:44:33 10HmbA-0005vi-00 signer: test.ex bits: 1024
1999-03-02 09:44:33 10HmbA-0005vi-00 DKIM: d=test.ex s=sel_bad c=relaxed/relaxed a=rsa-sha1 b=1024 [invalid - syntax error in public key record]
1999-03-02 09:44:33 10HmbA-0005vi-00 <= CALLER@bloggs.com H=(xxx) [127.0.0.1] P=smtp S=sss id=564CFC9B.1040905@yahoo.com
******** SERVER ********
1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225
+1999-03-02 09:44:33 10HmaX-0005vi-00 DKIM: validation error: Public key signature verification has failed.
1999-03-02 09:44:33 10HmaX-0005vi-00 signer: test.ex bits: 1024
1999-03-02 09:44:33 10HmaX-0005vi-00 DKIM: d=test.ex s=sel c=simple/simple a=rsa-sha512 b=1024 [verification failed - signature did not verify (headers probably modified in transit)]
1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@bloggs.com H=(xxx) [127.0.0.1] P=smtp S=sss id=qwerty1234@disco-zombie.net
******** SERVER ********
1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225
+1999-03-02 09:44:33 10HmaX-0005vi-00 DKIM: validation error: Public key signature verification has failed.
1999-03-02 09:44:33 10HmaX-0005vi-00 signer: test.ex bits: 1024
1999-03-02 09:44:33 10HmaX-0005vi-00 DKIM: d=test.ex s=sel2 c=simple/simple a=rsa-sha512 b=1024 [verification failed - signature did not verify (headers probably modified in transit)]
1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@bloggs.com H=(xxx) [127.0.0.1] P=smtp S=sss id=qwerty1234@disco-zombie.net
--- /dev/null
+
+******** SERVER ********
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225
+1999-03-02 09:44:33 10HmaX-0005vi-00 signer: test.ex bits: 512
+1999-03-02 09:44:33 10HmaX-0005vi-00 DKIM: d=test.ex s=sed c=relaxed/relaxed a=ed25519-sha256 b=512 [verification succeeded]
+1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@bloggs.com H=(xxx) [127.0.0.1] P=smtp S=sss DKIM=test.ex id=E10HmaY-0005vi-00@myhost.test.ex
+1999-03-02 09:44:33 10HmaZ-0005vi-00 signer: kitterman.org bits: 512
+1999-03-02 09:44:33 10HmaZ-0005vi-00 DKIM: d=kitterman.org s=ed25519 c=relaxed/simple a=ed25519-sha256 b=512 i=@kitterman.org t=1517847601 [verification succeeded]
+1999-03-02 09:44:33 10HmaZ-0005vi-00 signer: @kitterman.org bits: 512
+1999-03-02 09:44:33 10HmaZ-0005vi-00 DKIM: d=kitterman.org s=ed25519 c=relaxed/simple a=ed25519-sha256 b=512 i=@kitterman.org t=1517847601 [verification succeeded]
+1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@bloggs.com H=(xxx) [127.0.0.1] P=smtp S=sss DKIM=kitterman.org id=example@example.com
1999-03-02 09:44:33 10HmbA-0005vi-00 signer: test.ex bits: 1024
1999-03-02 09:44:33 10HmbA-0005vi-00 DKIM: d=test.ex s=sel c=simple/simple a=rsa-sha1 b=1024 [verification failed - body hash mismatch (body probably modified in transit)]
1999-03-02 09:44:33 10HmbA-0005vi-00 <= CALLER@bloggs.com H=(xxx) [127.0.0.1] P=smtp S=sss id=qwerty1234@disco-zombie.net
-1999-03-02 09:44:33 10HmbB-0005vi-00 DKIM: validation error: RSA_LONG_LINE
-1999-03-02 09:44:33 10HmbB-0005vi-00 DKIM: Error during validation, disabling signature verification: RSA_LONG_LINE
+1999-03-02 09:44:33 10HmbB-0005vi-00 DKIM: validation error: LONG_LINE
+1999-03-02 09:44:33 10HmbB-0005vi-00 DKIM: Error during validation, disabling signature verification: LONG_LINE
1999-03-02 09:44:33 10HmbB-0005vi-00 <= CALLER@bloggs.com H=(xxx) [127.0.0.1] P=smtp S=sss id=qwerty1234@disco-zombie.net
1999-03-02 09:44:33 10HmbC-0005vi-00 signer: test.ex bits: 512
1999-03-02 09:44:33 10HmbC-0005vi-00 DKIM: d=test.ex s=ses_sha256 c=simple/simple a=rsa-sha1 b=512 [verification failed - unspecified reason]
1999-03-02 09:44:33 10HmbK-0005vi-00 => :blackhole: <c@test.ex> R=server_dump
1999-03-02 09:44:33 10HmbK-0005vi-00 Completed
1999-03-02 09:44:33 rcpt acl: macro: From:Sender:Reply-To:Subject:Date:Message-ID:To:Cc:MIME-Version:Content-Type:Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive
+1999-03-02 09:44:33 10HmbM-0005vi-00 PDKIM: d=test.ex s=sel_bad [failed key import]
1999-03-02 09:44:33 10HmbM-0005vi-00 dkim_acl: signer: test.ex bits: 1024 h=From
1999-03-02 09:44:33 10HmbM-0005vi-00 DKIM: d=test.ex s=sel_bad c=relaxed/relaxed a=rsa-sha256 b=1024 [invalid - syntax error in public key record]
1999-03-02 09:44:33 10HmbM-0005vi-00 data acl: dkim status invalid
--- /dev/null
+1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss
+1999-03-02 09:44:33 10HmaX-0005vi-00 => a@test.ex R=client T=send_to_server H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4] C="250 OK id=10HmaY-0005vi-00"
+1999-03-02 09:44:33 10HmaX-0005vi-00 Completed
+1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss
+1999-03-02 09:44:33 10HmaZ-0005vi-00 => b@test.ex R=client T=send_to_server H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4] C="250 OK id=10HmbA-0005vi-00"
+1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed
+
+******** SERVER ********
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225
+1999-03-02 09:44:33 rcpt acl: macro: From:Sender:Reply-To:Subject:Date:Message-ID:To:Cc:MIME-Version:Content-Type:Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive
+1999-03-02 09:44:33 10HmaY-0005vi-00 dkim_acl: signer: test.ex bits: 512 h=From:To:Subject
+1999-03-02 09:44:33 10HmaY-0005vi-00 DKIM: d=test.ex s=sed c=relaxed/relaxed a=ed25519-sha256 b=512 [verification succeeded]
+1999-03-02 09:44:33 10HmaY-0005vi-00 data acl: dkim status pass
+1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtp S=sss id=E10HmaX-0005vi-00@myhost.test.ex
+1999-03-02 09:44:33 10HmaY-0005vi-00 => :blackhole: <a@test.ex> R=server_dump
+1999-03-02 09:44:33 10HmaY-0005vi-00 Completed
+1999-03-02 09:44:33 rcpt acl: macro: From:Sender:Reply-To:Subject:Date:Message-ID:To:Cc:MIME-Version:Content-Type:Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive
+1999-03-02 09:44:33 10HmbA-0005vi-00 dkim_acl: signer: test.ex bits: 512 h=From
+1999-03-02 09:44:33 10HmbA-0005vi-00 DKIM: d=test.ex s=sed c=relaxed/relaxed a=ed25519-sha256 b=512 [verification succeeded]
+1999-03-02 09:44:33 10HmbA-0005vi-00 dkim_acl: signer: test.ex bits: 1024 h=From
+1999-03-02 09:44:33 10HmbA-0005vi-00 DKIM: d=test.ex s=sel c=relaxed/relaxed a=rsa-sha256 b=1024 [verification succeeded]
+1999-03-02 09:44:33 10HmbA-0005vi-00 data acl: dkim status pass:pass
+1999-03-02 09:44:33 10HmbA-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtp S=sss id=E10HmaZ-0005vi-00@myhost.test.ex
+1999-03-02 09:44:33 10HmbA-0005vi-00 => :blackhole: <b@test.ex> R=server_dump
+1999-03-02 09:44:33 10HmbA-0005vi-00 Completed
id 10HmaY-0005vi-00
for y@test.ex; Tue, 2 Mar 1999 09:44:33 +0000
DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=test.ex;
- s=sel; h=LIST; bh=CVpkzY75tV/NCKk5pPx4GnM3NX83xwCiT0xVwo0G1Rs=; b=TIqPqpKM5qf
- ZFlv2H8yio5RybWA3sLCtVmE6HmBhBKqW+uqLKG2grqJhVMJ3qXnvQQ3ixnMjMlJqfCpEBtxfsSR9
- MGLPP9ZMdlrBNEL6XKlgE+X8bAra5zkuLZs8gy8H3/mtEfoKPs4ltB/ZK/j2FHG2+CEx+TDTIkh9E
- wkAMrA=;
+ s=sel; h=Subject; bh=CVpkzY75tV/NCKk5pPx4GnM3NX83xwCiT0xVwo0G1Rs=; b=JTYpVY1D
+ sO37MibaZTC2CgpQAZlz/lRefFQv3Q7JM4D0aUfseT24Xg+kxv3xc5guSzKWQzycm3zie366tHape
+ lu70O4/5+Dyr0f/FKjmYxT+ALcIzuVN7Rty2JioBG07aryqJqmcR0xpmiggctb/h/2a/JGRKPcDWO
+ psj50XQNQ=;
Received: from [127.0.0.1] (helo=xxx)
by testhost.test.ex with esmtp (Exim x.yz)
(envelope-from <CALLER@bloggs.com>)
id 10HmaX-0005vi-00
for z@test.ex; Tue, 2 Mar 1999 09:44:33 +0000
DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=test.ex;
- s=sel; h=LIST; bh=CVpkzY75tV/NCKk5pPx4GnM3NX83xwCiT0xVwo0G1Rs=; b=TIqPqpKM5qf
- ZFlv2H8yio5RybWA3sLCtVmE6HmBhBKqW+uqLKG2grqJhVMJ3qXnvQQ3ixnMjMlJqfCpEBtxfsSR9
- MGLPP9ZMdlrBNEL6XKlgE+X8bAra5zkuLZs8gy8H3/mtEfoKPs4ltB/ZK/j2FHG2+CEx+TDTIkh9E
- wkAMrA=;
+ s=sel; h=Subject; bh=CVpkzY75tV/NCKk5pPx4GnM3NX83xwCiT0xVwo0G1Rs=; b=JTYpVY1D
+ sO37MibaZTC2CgpQAZlz/lRefFQv3Q7JM4D0aUfseT24Xg+kxv3xc5guSzKWQzycm3zie366tHape
+ lu70O4/5+Dyr0f/FKjmYxT+ALcIzuVN7Rty2JioBG07aryqJqmcR0xpmiggctb/h/2a/JGRKPcDWO
+ psj50XQNQ=;
Received: from [127.0.0.1] (helo=xxx)
by testhost.test.ex with esmtp (Exim x.yz)
(envelope-from <CALLER@bloggs.com>)
# openssl version variances
s/(TLS error on connection [^:]*: error:)[0-9A-F]{8}(:system library):(?:fopen|func\(4095\)):(No such file or directory)$/$1xxxxxxxx$2:fopen:$3/;
s/(DANE attempt failed.*error:)[0-9A-F]{8}(:SSL routines:)(ssl3_get_server_certificate|tls_process_server_certificate|CONNECT_CR_CERT)(?=:certificate verify failed$)/$1xxxxxxxx$2ssl3_get_server_certificate/;
+ s/(DKIM: validation error: )error:[0-9A-F]{8}:rsa routines:int_rsa_verify:bad signature$/$1Public key signature verification has failed./;
}
# ======== All files other than stderr ========
--- /dev/null
+# DKIM verify, ed25519
+#
+exim -DSERVER=server -bd -oX PORT_D
+****
+#
+# This should pass, only Mail::DKIM::Signer does not handle ed25519-sha256 yet
+#
+# Mail original (will be)in aux-fixed/4500.msg1.txt
+# Sig generated by: perl aux-fixed/dkim/sign.pl --algorithm=ed255190sha256 \
+# --method=simple/simple < aux-fixed/4500.msg1.txt
+#
+# TODO - until we have that we can only test internal consistency,
+# signing vs. verification. For now, use a message we signed with
+# the Exim GnuTLS implementation (then we can test GnuTLS vs. others)
+#
+client 127.0.0.1 PORT_D
+??? 220
+HELO xxx
+??? 250
+MAIL FROM:<CALLER@bloggs.com>
+??? 250
+RCPT TO:<a@test.ex>
+??? 250
+DATA
+??? 354
+DKIM-Signature: v=1; a=ed25519-sha256; q=dns/txt; c=relaxed/relaxed; d=test.ex
+ ; s=sed; h=From:To:Subject; bh=/Ab0giHZitYQbDhFszoqQRUkgqueaX9zatJttIU/plc=;
+ b=5fhyD3EILDrnL4DnkD4hDaeis7+GSzL9GMHrhIDZJjuJ00WD5iI8SQ1q9rDfzFL/Kdw0VIyB4R
+ Dq0a4H6HI+Bw==;
+Received: from jgh by myhost.test.ex with local (Exim x.yz)
+ envelope-from <jgh@myhost.test.ex>)
+ 1dtXln-0000YP-Hb
+ a@test.ex; Sun, 17 Sep 2017 12:29:51 +0100
+From: nobody@example.com
+Message-Id: <E1dtXln-0000YP-Hb@myhost.test.ex>
+Sender: CALLER_NAME <jgh@myhost.test.ex>
+Date: Sun, 17 Sep 2017 12:29:51 +0100
+
+content
+.
+??? 250
+QUIT
+??? 221
+****
+#
+#
+# This should pass, an independently-generated sample from Scott Kitterman.
+# I don't want to retain this longterm as it hits an external DNS record,
+# not under the testsuite.
+client 127.0.0.1 PORT_D
+??? 220
+HELO xxx
+??? 250
+MAIL FROM:<CALLER@bloggs.com>
+??? 250
+RCPT TO:<a@test.ex>
+??? 250
+DATA
+??? 354
+DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/simple; d=kitterman.org;
+ i=@kitterman.org; q=dns/txt; s=ed25519; t=1517847601;
+ h=message-id : date : from : to : subject : date : from :
+ subject; bh=wE7NXSkgnx9PGiavN4OZhJztvkqPDlemV3OGuEnLwNo=;
+ b=sEnnE99Xsjpcqa/cNf8k/KQCEgjJ/4tswIKoNvq2q0fFQL6XBORJ2fQb
+ Fvt34Tb4sOxlZtBYu01kEJlmGz4uCw==
+Authentication-Results: lists.example.org; arc=none; spf=pass smtp.mfrom=example.com; dmarc=pass
+Received: from localhost
+Message-ID: <example@example.com>
+Date: Mon, 01 Jan 2011 01:02:03 +0400
+From: Test User <test@example.com>
+To: somebody@example.com
+Subject: Testing
+
+This is a test message.
+.
+??? 250
+QUIT
+??? 221
+****
+#
+killdaemon
+no_stdout_check
+no_msglog_check
--- /dev/null
+# DKIM signing, ed25519
+#
+exim -bd -DSERVER=server -oX PORT_D
+****
+#
+# Privkey used here is: aux-fixed/dkim/dkim_ed25519.private (set in the conf)
+#
+exim -DSELECTOR=sed -DOPT=From:To:Subject -odf a@test.ex
+From: nobody@example.com
+
+content
+****
+#
+# Multiple-signing test (rsa + ed25519)
+#
+exim -DSELECTOR=sed:sel -DOPT=From: -odf b@test.ex
+From: nobody@example.com
+
+content
+****
+#
+millisleep 500
+killdaemon
+no_msglog_check
# DKIM, CHUNKING, wireformat-spoolfile
#
-exim -bd -DSERVER=server -DOPT=dkim -oX PORT_S:PORT_D
+exim -bd -DSERVER=server -DOPT=dkim -DLIST=Subject -oX PORT_S:PORT_D
****
#
# 1: non-CHUNKING injection; will not be stored as wireformat therefore
--- /dev/null
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/* Unix includes */
+
+typedef unsigned char uschar;
+
+#define CS (char *)
+#define US (unsigned char *)
+
+#define FALSE 0
+#define TRUE 1
+
+
+
+#ifdef HAVE_GNUTLS
+
+
+#include <gnutls/gnutls.h>
+#include <gnutls/abstract.h>
+#include <gnutls/x509.h>
+
+#if GNUTLS_VERSION_NUMBER >= 0x030600
+# define SIGN_HAVE_ED25519
+#endif
+
+
+
+static uschar *enc64table =
+ US"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+uschar *
+b64encode(uschar *clear, int len)
+{
+uschar *code = malloc(4*((len+2)/3) + 2);
+uschar *p = code;
+
+while (len-- >0)
+ {
+ int x, y;
+
+ x = *clear++;
+ *p++ = enc64table[(x >> 2) & 63];
+
+ if (len-- <= 0)
+ {
+ *p++ = enc64table[(x << 4) & 63];
+ *p++ = '=';
+ *p++ = '=';
+ break;
+ }
+
+ y = *clear++;
+ *p++ = enc64table[((x << 4) | ((y >> 4) & 15)) & 63];
+
+ if (len-- <= 0)
+ {
+ *p++ = enc64table[(y << 2) & 63];
+ *p++ = '=';
+ break;
+ }
+
+ x = *clear++;
+ *p++ = enc64table[((y << 2) | ((x >> 6) & 3)) & 63];
+
+ *p++ = enc64table[x & 63];
+ }
+
+*p = 0;
+
+return code;
+}
+
+/*************************************************
+* Main Program *
+*************************************************/
+
+
+int
+main(int argc, char **argv)
+{
+uschar * pemfile = argv[1];
+int fd;
+uschar buf[1024];
+int len, rc;
+gnutls_privkey_t privkey;
+gnutls_datum_t k;
+gnutls_pubkey_t pubkey;
+uschar * b64;
+
+#ifdef SIGN_HAVE_ED25519
+if ((fd = open(CS pemfile, O_RDONLY)) < 0)
+ exit(1);
+
+if ((len = read(fd, buf, sizeof(buf)-1)) < 0)
+ exit(2);
+
+k.data = buf;
+k.size = len;
+
+if ( (rc = gnutls_privkey_init(&privkey))
+ || (rc = gnutls_privkey_import_x509_raw(privkey, &k, GNUTLS_X509_FMT_PEM, NULL, GNUTLS_PKCS_PLAIN))
+ || (rc = gnutls_pubkey_init(&pubkey))
+ || (rc = gnutls_pubkey_import_privkey(pubkey, privkey, GNUTLS_KEY_DIGITAL_SIGNATURE, 0))
+ || (rc = gnutls_pubkey_export_ecc_raw2(pubkey, NULL, &k, NULL, GNUTLS_EXPORT_FLAG_NO_LZ))
+ )
+ fprintf(stderr, "%s\n", gnutls_strerror(rc));
+
+b64 = b64encode(k.data, k.size);
+
+printf("%s\n", b64);
+exit(0);
+
+#else
+fprintf(stderr, "No support for ed25519 signing in GnuTLS (version %s)\n", gnutls_check_version(NULL));
+exit(3);
+#endif
+}
+
+#endif
+
+#ifdef HAVE_OPENSSL
+int
+main(int argc, char **argv)
+{
+fprintf(stderr, "No support for ed25519 signing in OpenSSL\n");
+exit(3);
+}
+
+#endif
>>Headers added by MAIL or RCPT ACL:
X-ACL-Warn: added header line
>>
+PDKIM: no signatures
LOG: MAIN
<= ok@test3 H=[10.9.8.8] U=CALLER P=smtp S=sss
Exim version x.yz ....
(envelope-from <x@y>)
id 10HmbF-0005vi-00
for warn_empty@test.ex; Tue, 2 Mar 1999 09:44:33 +0000
+PDKIM: no signatures
calling local_scan(); timeout=300
local_scan() returned 0 NULL
LOG: MAIN
(envelope-from <x@y>)
id 10HmbG-0005vi-00
for warn_log@test.ex; Tue, 2 Mar 1999 09:44:33 +0000
+PDKIM: no signatures
calling local_scan(); timeout=300
local_scan() returned 0 NULL
LOG: MAIN
>>Headers added by MAIL or RCPT ACL:
X-ACL-Warn: warn user message
>>
+PDKIM: no signatures
calling local_scan(); timeout=300
local_scan() returned 0 NULL
LOG: MAIN
by myhost.test.ex with esmtp (Exim x.yz)
id 10HmaX-0005vi-00
for x@y; Tue, 2 Mar 1999 09:44:33 +0000
+PDKIM: no signatures
calling local_scan(); timeout=300
local_scan() returned 0 NULL
LOG: MAIN
by myhost.test.ex with esmtp (Exim x.yz)
id 10HmaY-0005vi-00
for x@y; Tue, 2 Mar 1999 09:44:33 +0000
+PDKIM: no signatures
calling local_scan(); timeout=300
local_scan() returned 0 NULL
LOG: MAIN
(envelope-from <x@y>)
id 10HmaX-0005vi-00
for x@y; Tue, 2 Mar 1999 09:44:33 +0000
+PDKIM: no signatures
using ACL "data"
processing "accept"
check set acl_m0 = $acl_m0; data
X-Warning: V4NET.11.12.13 is listed at rbl.test.ex
X-Warning: This is a test blacklisting message
>>
+PDKIM: no signatures
calling local_scan(); timeout=300
local_scan() returned 0 NULL
Writing spool header file: TESTSUITE/spool//input//hdr.pppp
X-Warning: V4NET.11.12.13 is listed at rbl.test.ex
X-Warning: This is a test blacklisting message
>>
+PDKIM: no signatures
calling local_scan(); timeout=300
local_scan() returned 0 NULL
Writing spool header file: TESTSUITE/spool//input//hdr.pppp
P Received: from CALLER by myhost.test.ex with local-smtp (Exim x.yz)
id 10HmaY-0005vi-00
for abc@domain; Tue, 2 Mar 1999 09:44:33 +0000
+PDKIM: no signatures
using ACL "check_data"
processing "accept"
check verify = header_syntax
P Received: from CALLER by myhost.test.ex with local-smtp (Exim x.yz)
id 10HmaX-0005vi-00
for abc@xyz; Tue, 2 Mar 1999 09:44:33 +0000
+PDKIM: no signatures
using ACL "check_data"
processing "accept"
check verify = header_syntax
(envelope-from <x@y>)
id 10HmaX-0005vi-00
for userx@test.ex; Tue, 2 Mar 1999 09:44:33 +0000
+PDKIM: no signatures
calling local_scan(); timeout=300
local_scan() returned 0 NULL
Writing spool header file: TESTSUITE/spool//input//hdr.pppp
(envelope-from <x@y>)
id 10HmaX-0005vi-00
for x@y; Tue, 2 Mar 1999 09:44:33 +0000
+PDKIM: no signatures
calling local_scan(); timeout=300
local_scan() returned 0 NULL
LOG: MAIN
for userx@domain.com
----------- start cutthrough headers send -----------
----------- done cutthrough headers send ------------
+PDKIM: no signatures
┌considering: ${tod_full}
├──expanding: ${tod_full}
└─────result: Tue, 2 Mar 1999 09:44:33 +0000
for usery@domain.com
----------- start cutthrough headers send -----------
----------- done cutthrough headers send ------------
+PDKIM: no signatures
┌considering: ${tod_full}
├──expanding: ${tod_full}
└─────result: Tue, 2 Mar 1999 09:44:33 +0000
for usery@domain.com
----------- start cutthrough headers send -----------
----------- done cutthrough headers send ------------
+PDKIM: no signatures
┌considering: ${tod_full}
├──expanding: ${tod_full}
└─────result: Tue, 2 Mar 1999 09:44:33 +0000
for userx@domain.com
----------- start cutthrough headers send -----------
----------- done cutthrough headers send ------------
+PDKIM: no signatures
┌considering: ${tod_full}
├──expanding: ${tod_full}
└─────result: Tue, 2 Mar 1999 09:44:33 +0000
for usery@domain.com
----------- start cutthrough headers send -----------
----------- done cutthrough headers send ------------
+PDKIM: no signatures
┌considering: ${tod_full}
├──expanding: ${tod_full}
└─────result: Tue, 2 Mar 1999 09:44:33 +0000
for usery@domain.com
----------- start cutthrough headers send -----------
----------- done cutthrough headers send ------------
+PDKIM: no signatures
┌considering: ${tod_full}
├──expanding: ${tod_full}
└─────result: Tue, 2 Mar 1999 09:44:33 +0000