consistent fork-time debug
[exim.git] / src / src / pdkim / pdkim.c
index 464516f0ac293c693acf30265ae407f1964985d7..7fcfbc76a46aa4c46d2ee3080080be898e0a3609 100644 (file)
@@ -26,8 +26,8 @@
 
 #ifndef DISABLE_DKIM   /* entire file */
 
 
 #ifndef DISABLE_DKIM   /* entire file */
 
-#ifndef SUPPORT_TLS
-# error Need SUPPORT_TLS for DKIM
+#ifdef DISABLE_TLS
+# error Must not DISABLE_TLS, for DKIM
 #endif
 
 #include "crypt_ver.h"
 #endif
 
 #include "crypt_ver.h"
@@ -71,11 +71,7 @@ const uschar * pdkim_canons[] = {
   NULL
 };
 
   NULL
 };
 
-typedef struct {
-  const uschar * dkim_hashname;
-  hashmethod    exim_hashmethod;
-} pdkim_hashtype;
-static const pdkim_hashtype pdkim_hashes[] = {
+const pdkim_hashtype pdkim_hashes[] = {
   { US"sha1",   HASH_SHA1 },
   { US"sha256", HASH_SHA2_256 },
   { US"sha512", HASH_SHA2_512 }
   { US"sha1",   HASH_SHA1 },
   { US"sha256", HASH_SHA2_256 },
   { US"sha512", HASH_SHA2_512 }
@@ -84,7 +80,7 @@ static const pdkim_hashtype pdkim_hashes[] = {
 const uschar * pdkim_keytypes[] = {
   [KEYTYPE_RSA] =      US"rsa",
 #ifdef SIGN_HAVE_ED25519
 const uschar * pdkim_keytypes[] = {
   [KEYTYPE_RSA] =      US"rsa",
 #ifdef SIGN_HAVE_ED25519
-  [KEYTYPE_ED25519] =  US"ed25519",            /* Works for 3.6.0 GnuTLS */
+  [KEYTYPE_ED25519] =  US"ed25519",            /* Works for 3.6.0 GnuTLS, OpenSSL 1.1.1 */
 #endif
 
 #ifdef notyet_EC_dkim_extensions       /* https://tools.ietf.org/html/draft-srose-dkim-ecc-00 */
 #endif
 
 #ifdef notyet_EC_dkim_extensions       /* https://tools.ietf.org/html/draft-srose-dkim-ecc-00 */
@@ -125,6 +121,40 @@ return string_sprintf("%s-%s",
 }
 
 
 }
 
 
+static int
+pdkim_keyname_to_keytype(const uschar * s)
+{
+for (int i = 0; i < nelem(pdkim_keytypes); i++)
+  if (Ustrcmp(s, pdkim_keytypes[i]) == 0) return i;
+return -1;
+}
+
+int
+pdkim_hashname_to_hashtype(const uschar * s, unsigned len)
+{
+if (!len) len = Ustrlen(s);
+for (int i = 0; i < nelem(pdkim_hashes); i++)
+  if (Ustrncmp(s, pdkim_hashes[i].dkim_hashname, len) == 0)
+    return i;
+return -1;
+}
+
+void
+pdkim_cstring_to_canons(const uschar * s, unsigned len,
+  int * canon_head, int * canon_body)
+{
+if (!len) len = Ustrlen(s);
+for (int i = 0; pdkim_combined_canons[i].str; i++)
+  if (  Ustrncmp(s, pdkim_combined_canons[i].str, len) == 0
+     && len == Ustrlen(pdkim_combined_canons[i].str))
+    {
+    *canon_head = pdkim_combined_canons[i].canon_headers;
+    *canon_body = pdkim_combined_canons[i].canon_body;
+    break;
+    }
+}
+
+
 
 const char *
 pdkim_verify_status_str(int status)
 
 const char *
 pdkim_verify_status_str(int status)
@@ -168,6 +198,7 @@ switch(status)
   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_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_ERR_EXCESS_SIGS:  return US"EXCESS_SIGS";
   case PDKIM_SIGN_PRIVKEY_WRAP:        return US"PRIVKEY_WRAP";
   case PDKIM_SIGN_PRIVKEY_B64D:        return US"PRIVKEY_B64D";
   default: return US"(unknown)";
   case PDKIM_SIGN_PRIVKEY_WRAP:        return US"PRIVKEY_WRAP";
   case PDKIM_SIGN_PRIVKEY_B64D:        return US"PRIVKEY_B64D";
   default: return US"(unknown)";
@@ -177,11 +208,10 @@ switch(status)
 
 /* -------------------------------------------------------------------------- */
 /* Print debugging functions */
 
 /* -------------------------------------------------------------------------- */
 /* Print debugging functions */
-static void
+void
 pdkim_quoteprint(const uschar *data, int len)
 {
 pdkim_quoteprint(const uschar *data, int len)
 {
-int i;
-for (i = 0; i < len; i++)
+for (int i = 0; i < len; i++)
   {
   const int c = data[i];
   switch (c)
   {
   const int c = data[i];
   switch (c)
@@ -203,11 +233,10 @@ for (i = 0; i < len; i++)
 debug_printf("\n");
 }
 
 debug_printf("\n");
 }
 
-static void
+void
 pdkim_hexprint(const uschar *data, int len)
 {
 pdkim_hexprint(const uschar *data, int len)
 {
-int i;
-if (data) for (i = 0 ; i < len; i++) debug_printf("%02x", data[i]);
+if (data) for (int i = 0 ; i < len; i++) debug_printf("%02x", data[i]);
 else debug_printf("<NULL>");
 debug_printf("\n");
 }
 else debug_printf("<NULL>");
 debug_printf("\n");
 }
@@ -217,7 +246,7 @@ debug_printf("\n");
 static pdkim_stringlist *
 pdkim_prepend_stringlist(pdkim_stringlist * base, const uschar * str)
 {
 static pdkim_stringlist *
 pdkim_prepend_stringlist(pdkim_stringlist * base, const uschar * str)
 {
-pdkim_stringlist * new_entry = store_get(sizeof(pdkim_stringlist));
+pdkim_stringlist * new_entry = store_get(sizeof(pdkim_stringlist), FALSE);
 
 memset(new_entry, 0, sizeof(pdkim_stringlist));
 new_entry->value = string_copy(str);
 
 memset(new_entry, 0, sizeof(pdkim_stringlist));
 new_entry->value = string_copy(str);
@@ -302,16 +331,15 @@ return PDKIM_FAIL;
 /* -------------------------------------------------------------------------- */
 /* Performs "relaxed" canonicalization of a header. */
 
 /* -------------------------------------------------------------------------- */
 /* Performs "relaxed" canonicalization of a header. */
 
-static uschar *
-pdkim_relax_header(const uschar * header, BOOL append_crlf)
+uschar *
+pdkim_relax_header_n(const uschar * header, int len, BOOL append_crlf)
 {
 BOOL past_field_name = FALSE;
 BOOL seen_wsp = FALSE;
 {
 BOOL past_field_name = FALSE;
 BOOL seen_wsp = FALSE;
-const uschar * p;
-uschar * relaxed = store_get(Ustrlen(header)+3);
+uschar * relaxed = store_get(len+3, TRUE);     /* tainted */
 uschar * q = relaxed;
 
 uschar * q = relaxed;
 
-for (p = header; *p; p++)
+for (const uschar * p = header; p - header < len; p++)
   {
   uschar c = *p;
 
   {
   uschar c = *p;
 
@@ -347,6 +375,13 @@ return relaxed;
 }
 
 
 }
 
 
+uschar *
+pdkim_relax_header(const uschar * header, BOOL append_crlf)
+{
+return pdkim_relax_header_n(header, Ustrlen(header), append_crlf);
+}
+
+
 /* -------------------------------------------------------------------------- */
 #define PDKIM_QP_ERROR_DECODE -1
 
 /* -------------------------------------------------------------------------- */
 #define PDKIM_QP_ERROR_DECODE -1
 
@@ -381,7 +416,7 @@ pdkim_decode_qp(const uschar * str)
 int nchar = 0;
 uschar * q;
 const uschar * p = str;
 int nchar = 0;
 uschar * q;
 const uschar * p = str;
-uschar * n = store_get(Ustrlen(str)+1);
+uschar * n = store_get(Ustrlen(str)+1, TRUE);
 
 *n = '\0';
 q = n;
 
 *n = '\0';
 q = n;
@@ -407,19 +442,18 @@ return n;
 
 /* -------------------------------------------------------------------------- */
 
 
 /* -------------------------------------------------------------------------- */
 
-static void
+void
 pdkim_decode_base64(const uschar * str, blob * b)
 {
 pdkim_decode_base64(const uschar * str, blob * b)
 {
-int dlen;
-dlen = b64decode(str, &b->data);
+int dlen = b64decode(str, &b->data);
 if (dlen < 0) b->data = NULL;
 b->len = dlen;
 }
 
 if (dlen < 0) b->data = NULL;
 b->len = dlen;
 }
 
-static uschar *
+uschar *
 pdkim_encode_base64(blob * b)
 {
 pdkim_encode_base64(blob * b)
 {
-return b64encode(b->data, b->len);
+return b64encode(CUS b->data, b->len);
 }
 
 
 }
 
 
@@ -432,15 +466,14 @@ static pdkim_signature *
 pdkim_parse_sig_header(pdkim_ctx * ctx, uschar * raw_hdr)
 {
 pdkim_signature * sig;
 pdkim_parse_sig_header(pdkim_ctx * ctx, uschar * raw_hdr)
 {
 pdkim_signature * sig;
-uschar *p, *q;
+uschar *q;
 gstring * cur_tag = NULL;
 gstring * cur_val = NULL;
 BOOL past_hname = FALSE;
 BOOL in_b_val = FALSE;
 int where = PDKIM_HDR_LIMBO;
 gstring * cur_tag = NULL;
 gstring * cur_val = NULL;
 BOOL past_hname = FALSE;
 BOOL in_b_val = FALSE;
 int where = PDKIM_HDR_LIMBO;
-int i;
 
 
-sig = store_get(sizeof(pdkim_signature));
+sig = store_get(sizeof(pdkim_signature), FALSE);
 memset(sig, 0, sizeof(pdkim_signature));
 sig->bodylength = -1;
 
 memset(sig, 0, sizeof(pdkim_signature));
 sig->bodylength = -1;
 
@@ -449,9 +482,9 @@ sig->version = 0;
 sig->keytype = -1;
 sig->hashtype = -1;
 
 sig->keytype = -1;
 sig->hashtype = -1;
 
-q = sig->rawsig_no_b_val = store_get(Ustrlen(raw_hdr)+1);
+q = sig->rawsig_no_b_val = store_get(Ustrlen(raw_hdr)+1, TRUE);        /* tainted */
 
 
-for (p = raw_hdr; ; p++)
+for (uschar * p = raw_hdr; ; p++)
   {
   char c = *p;
 
   {
   char c = *p;
 
@@ -531,37 +564,24 @@ for (p = raw_hdr; ; p++)
            break;
          case 'a':                                     /* algorithm */
            {
            break;
          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;
+           const uschar * list = cur_val->s;
+           int sep = '-';
+           uschar * elem;
+
+           if ((elem = string_nextinlist(&list, &sep, NULL, 0)))
+             sig->keytype = pdkim_keyname_to_keytype(elem);
+           if ((elem = string_nextinlist(&list, &sep, NULL, 0)))
+             for (int i = 0; i < nelem(pdkim_hashes); i++)
+               if (Ustrcmp(elem, pdkim_hashes[i].dkim_hashname) == 0)
+                 { sig->hashtype = i; break; }
            }
 
          case 'c':                                     /* canonicalization */
            }
 
          case 'c':                                     /* canonicalization */
-           for (i = 0; pdkim_combined_canons[i].str; i++)
-             if (Ustrcmp(cur_val->s, pdkim_combined_canons[i].str) == 0)
-               {
-               sig->canon_headers = pdkim_combined_canons[i].canon_headers;
-               sig->canon_body    = pdkim_combined_canons[i].canon_body;
-               break;
-               }
+           pdkim_cstring_to_canons(cur_val->s, 0,
+                                   &sig->canon_headers, &sig->canon_body);
            break;
          case 'q':                             /* Query method (for pubkey)*/
            break;
          case 'q':                             /* Query method (for pubkey)*/
-           for (i = 0; pdkim_querymethods[i]; i++)
+           for (int i = 0; pdkim_querymethods[i]; i++)
              if (Ustrcmp(cur_val->s, pdkim_querymethods[i]) == 0)
                {
                sig->querymethod = i;   /* we never actually use this */
              if (Ustrcmp(cur_val->s, pdkim_querymethods[i]) == 0)
                {
                sig->querymethod = i;   /* we never actually use this */
@@ -618,15 +638,15 @@ while (--q > sig->rawsig_no_b_val  && (*q == '\r' || *q == '\n'))
 DEBUG(D_acl)
   {
   debug_printf(
 DEBUG(D_acl)
   {
   debug_printf(
-         "PDKIM >> Raw signature w/o b= tag value >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
+         "DKIM >> Raw signature w/o b= tag value >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
   pdkim_quoteprint(US sig->rawsig_no_b_val, Ustrlen(sig->rawsig_no_b_val));
   debug_printf(
   pdkim_quoteprint(US sig->rawsig_no_b_val, Ustrlen(sig->rawsig_no_b_val));
   debug_printf(
-         "PDKIM >> Sig size: %4u bits\n", (unsigned) sig->sighash.len*8);
+         "DKIM >> Sig size: %4u bits\n", (unsigned) sig->sighash.len*8);
   debug_printf(
   debug_printf(
-         "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
+         "DKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
   }
 
   }
 
-if (!pdkim_set_bodyhash(ctx, sig))
+if (!pdkim_set_sig_bodyhash(ctx, sig))
   return NULL;
 
 return sig;
   return NULL;
 
 return sig;
@@ -635,14 +655,14 @@ return sig;
 
 /* -------------------------------------------------------------------------- */
 
 
 /* -------------------------------------------------------------------------- */
 
-static pdkim_pubkey *
-pdkim_parse_pubkey_record(pdkim_ctx *ctx, const uschar *raw_record)
+pdkim_pubkey *
+pdkim_parse_pubkey_record(const uschar *raw_record)
 {
 const uschar * ele;
 int sep = ';';
 pdkim_pubkey * pub;
 
 {
 const uschar * ele;
 int sep = ';';
 pdkim_pubkey * pub;
 
-pub = store_get(sizeof(pdkim_pubkey));
+pub = store_get(sizeof(pdkim_pubkey), TRUE);   /* tainted */
 memset(pub, 0, sizeof(pdkim_pubkey));
 
 while ((ele = string_nextinlist(&raw_record, &sep, NULL, 0)))
 memset(pub, 0, sizeof(pdkim_pubkey));
 
 while ((ele = string_nextinlist(&raw_record, &sep, NULL, 0)))
@@ -710,7 +730,6 @@ if (b->canon_method == PDKIM_CANON_RELAXED)
   if (!relaxed_data)
     {
     BOOL seen_wsp = FALSE;
   if (!relaxed_data)
     {
     BOOL seen_wsp = FALSE;
-    const uschar * p, * r;
     int q = 0;
 
     /* We want to be able to free this else we allocate
     int q = 0;
 
     /* We want to be able to free this else we allocate
@@ -721,7 +740,7 @@ if (b->canon_method == PDKIM_CANON_RELAXED)
     relaxed_data = store_malloc(sizeof(blob) + orig_data->len+1);
     relaxed_data->data = US (relaxed_data+1);
 
     relaxed_data = store_malloc(sizeof(blob) + orig_data->len+1);
     relaxed_data->data = US (relaxed_data+1);
 
-    for (p = orig_data->data, r = p + orig_data->len; p < r; p++)
+    for (const uschar * p = orig_data->data, * r = p + orig_data->len; p < r; p++)
       {
       char c = *p;
       if (c == '\r')
       {
       char c = *p;
       if (c == '\r')
@@ -768,21 +787,22 @@ return relaxed_data;
 static void
 pdkim_finish_bodyhash(pdkim_ctx * ctx)
 {
 static void
 pdkim_finish_bodyhash(pdkim_ctx * ctx)
 {
-pdkim_bodyhash * b;
-pdkim_signature * sig;
-
-for (b = ctx->bodyhash; b; b = b->next)                /* Finish hashes */
+for (pdkim_bodyhash * b = ctx->bodyhash; b; b = b->next)     /* Finish hashes */
+  {
+  DEBUG(D_acl) debug_printf("DKIM: finish bodyhash %d/%d/%ld len %ld\n",
+           b->hashtype, b->canon_method, b->bodylength, b->signed_body_bytes);
   exim_sha_finish(&b->body_hash_ctx, &b->bh);
   exim_sha_finish(&b->body_hash_ctx, &b->bh);
+  }
 
 /* Traverse all signatures */
 
 /* Traverse all signatures */
-for (sig = ctx->sig; sig; sig = sig->next)
+for (pdkim_signature * sig = ctx->sig; sig; sig = sig->next)
   {
   {
-  b = sig->calc_body_hash;
+  pdkim_bodyhash * b = sig->calc_body_hash;
 
   DEBUG(D_acl)
     {
 
   DEBUG(D_acl)
     {
-    debug_printf("PDKIM [%s] Body bytes (%s) hashed: %lu\n"
-                "PDKIM [%s] Body %s computed: ",
+    debug_printf("DKIM [%s] Body bytes (%s) hashed: %lu\n"
+                "DKIM [%s] Body %s computed: ",
        sig->domain, pdkim_canons[b->canon_method], b->signed_body_bytes,
        sig->domain, pdkim_hashes[b->hashtype].dkim_hashname);
     pdkim_hexprint(CUS b->bh.data, b->bh.len);
        sig->domain, pdkim_canons[b->canon_method], b->signed_body_bytes,
        sig->domain, pdkim_hashes[b->hashtype].dkim_hashname);
     pdkim_hexprint(CUS b->bh.data, b->bh.len);
@@ -804,15 +824,15 @@ for (sig = ctx->sig; sig; sig = sig->next)
     if (  sig->bodyhash.data
        && memcmp(b->bh.data, sig->bodyhash.data, b->bh.len) == 0)
       {
     if (  sig->bodyhash.data
        && memcmp(b->bh.data, sig->bodyhash.data, b->bh.len) == 0)
       {
-      DEBUG(D_acl) debug_printf("PDKIM [%s] Body hash compared OK\n", sig->domain);
+      DEBUG(D_acl) debug_printf("DKIM [%s] Body hash compared OK\n", sig->domain);
       }
     else
       {
       DEBUG(D_acl)
         {
       }
     else
       {
       DEBUG(D_acl)
         {
-       debug_printf("PDKIM [%s] Body hash signature from headers: ", sig->domain);
+       debug_printf("DKIM [%s] Body hash signature from headers: ", sig->domain);
        pdkim_hexprint(sig->bodyhash.data, sig->bodyhash.len);
        pdkim_hexprint(sig->bodyhash.data, sig->bodyhash.len);
-       debug_printf("PDKIM [%s] Body hash did NOT verify\n", sig->domain);
+       debug_printf("DKIM [%s] Body hash did NOT verify\n", sig->domain);
        }
       sig->verify_status     = PDKIM_VERIFY_FAIL;
       sig->verify_ext_status = PDKIM_VERIFY_FAIL_BODY;
        }
       sig->verify_status     = PDKIM_VERIFY_FAIL;
       sig->verify_ext_status = PDKIM_VERIFY_FAIL_BODY;
@@ -825,15 +845,13 @@ for (sig = ctx->sig; sig; sig = sig->next)
 static void
 pdkim_body_complete(pdkim_ctx * ctx)
 {
 static void
 pdkim_body_complete(pdkim_ctx * ctx)
 {
-pdkim_bodyhash * b;
-
 /* In simple body mode, if any empty lines were buffered,
 replace with one. rfc 4871 3.4.3 */
 /*XXX checking the signed-body-bytes is a gross hack; I think
 it indicates that all linebreaks should be buffered, including
 the one terminating a text line */
 
 /* In simple body mode, if any empty lines were buffered,
 replace with one. rfc 4871 3.4.3 */
 /*XXX checking the signed-body-bytes is a gross hack; I think
 it indicates that all linebreaks should be buffered, including
 the one terminating a text line */
 
-for (b = ctx->bodyhash; b; b = b->next)
+for (pdkim_bodyhash * b = ctx->bodyhash; b; b = b->next)
   if (  b->canon_method == PDKIM_CANON_SIMPLE
      && b->signed_body_bytes == 0
      && b->num_buffered_blanklines > 0
   if (  b->canon_method == PDKIM_CANON_SIMPLE
      && b->signed_body_bytes == 0
      && b->num_buffered_blanklines > 0
@@ -854,7 +872,6 @@ static void
 pdkim_bodyline_complete(pdkim_ctx * ctx)
 {
 blob line = {.data = ctx->linebuf, .len = ctx->linebuf_offset};
 pdkim_bodyline_complete(pdkim_ctx * ctx)
 {
 blob line = {.data = ctx->linebuf, .len = ctx->linebuf_offset};
-pdkim_bodyhash * b;
 blob * rnl = NULL;
 blob * rline = NULL;
 
 blob * rnl = NULL;
 blob * rline = NULL;
 
@@ -878,12 +895,13 @@ if (ctx->flags & PDKIM_DOT_TERM)
 /* Empty lines need to be buffered until we find a non-empty line */
 if (memcmp(line.data, "\r\n", 2) == 0)
   {
 /* Empty lines need to be buffered until we find a non-empty line */
 if (memcmp(line.data, "\r\n", 2) == 0)
   {
-  for (b = ctx->bodyhash; b; b = b->next) b->num_buffered_blanklines++;
+  for (pdkim_bodyhash * b = ctx->bodyhash; b; b = b->next)
+    b->num_buffered_blanklines++;
   goto all_skip;
   }
 
 /* Process line for each bodyhash separately */
   goto all_skip;
   }
 
 /* Process line for each bodyhash separately */
-for (b = ctx->bodyhash; b; b = b->next)
+for (pdkim_bodyhash * b = ctx->bodyhash; b; b = b->next)
   {
   if (b->canon_method == PDKIM_CANON_RELAXED)
     {
   {
   if (b->canon_method == PDKIM_CANON_RELAXED)
     {
@@ -932,19 +950,21 @@ return;
 static int
 pdkim_header_complete(pdkim_ctx * ctx)
 {
 static int
 pdkim_header_complete(pdkim_ctx * ctx)
 {
-pdkim_signature * sig, * last_sig;
-
-/* Special case: The last header can have an extra \r appended */
 if ( (ctx->cur_header->ptr > 1) &&
      (ctx->cur_header->s[ctx->cur_header->ptr-1] == '\r') )
   --ctx->cur_header->ptr;
 (void) string_from_gstring(ctx->cur_header);
 
 if ( (ctx->cur_header->ptr > 1) &&
      (ctx->cur_header->s[ctx->cur_header->ptr-1] == '\r') )
   --ctx->cur_header->ptr;
 (void) string_from_gstring(ctx->cur_header);
 
+#ifdef EXPERIMENTAL_ARC
+/* Feed the header line to ARC processing */
+(void) arc_header_feed(ctx->cur_header, !(ctx->flags & PDKIM_MODE_SIGN));
+#endif
+
 if (++ctx->num_headers > PDKIM_MAX_HEADERS) goto BAIL;
 
 /* SIGNING -------------------------------------------------------------- */
 if (ctx->flags & PDKIM_MODE_SIGN)
 if (++ctx->num_headers > PDKIM_MAX_HEADERS) goto BAIL;
 
 /* SIGNING -------------------------------------------------------------- */
 if (ctx->flags & PDKIM_MODE_SIGN)
-  for (sig = ctx->sig; sig; sig = sig->next)                   /* Traverse all signatures */
+  for (pdkim_signature * sig = ctx->sig; sig; sig = sig->next)  /* Traverse all signatures */
 
     /* Add header to the signed headers list (in reverse order) */
     sig->headers = pdkim_prepend_stringlist(sig->headers, ctx->cur_header->s);
 
     /* Add header to the signed headers list (in reverse order) */
     sig->headers = pdkim_prepend_stringlist(sig->headers, ctx->cur_header->s);
@@ -956,7 +976,7 @@ else
 #ifdef notdef
   DEBUG(D_acl)
     {
 #ifdef notdef
   DEBUG(D_acl)
     {
-    debug_printf("PDKIM >> raw hdr: ");
+    debug_printf("DKIM >> raw hdr: ");
     pdkim_quoteprint(CUS ctx->cur_header->s, ctx->cur_header->ptr);
     }
 #endif
     pdkim_quoteprint(CUS ctx->cur_header->s, ctx->cur_header->ptr);
     }
 #endif
@@ -964,12 +984,13 @@ else
                  DKIM_SIGNATURE_HEADERNAME,
                  Ustrlen(DKIM_SIGNATURE_HEADERNAME)) == 0)
     {
                  DKIM_SIGNATURE_HEADERNAME,
                  Ustrlen(DKIM_SIGNATURE_HEADERNAME)) == 0)
     {
+    pdkim_signature * sig, * last_sig;
     /* Create and chain new signature block.  We could error-check for all
     required tags here, but prefer to create the internal sig and expicitly
     fail verification of it later. */
 
     DEBUG(D_acl) debug_printf(
     /* Create and chain new signature block.  We could error-check for all
     required tags here, but prefer to create the internal sig and expicitly
     fail verification of it later. */
 
     DEBUG(D_acl) debug_printf(
-       "PDKIM >> Found sig, trying to parse >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
+       "DKIM >> Found sig, trying to parse >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
 
     sig = pdkim_parse_sig_header(ctx, ctx->cur_header->s);
 
 
     sig = pdkim_parse_sig_header(ctx, ctx->cur_header->s);
 
@@ -980,6 +1001,13 @@ else
       while (last_sig->next) last_sig = last_sig->next;
       last_sig->next = sig;
       }
       while (last_sig->next) last_sig = last_sig->next;
       last_sig->next = sig;
       }
+
+    if (--dkim_collect_input == 0)
+      {
+      ctx->headers = pdkim_prepend_stringlist(ctx->headers, ctx->cur_header->s);
+      ctx->cur_header->s[ctx->cur_header->ptr = 0] = '\0';
+      return PDKIM_ERR_EXCESS_SIGS;
+      }
     }
 
   /* all headers are stored for signature verification */
     }
 
   /* all headers are stored for signature verification */
@@ -999,15 +1027,14 @@ return PDKIM_OK;
 DLLEXPORT int
 pdkim_feed(pdkim_ctx * ctx, uschar * data, int len)
 {
 DLLEXPORT int
 pdkim_feed(pdkim_ctx * ctx, uschar * data, int len)
 {
-int p, rc;
-
 /* Alternate EOD signal, used in non-dotstuffing mode */
 if (!data)
   pdkim_body_complete(ctx);
 
 /* Alternate EOD signal, used in non-dotstuffing mode */
 if (!data)
   pdkim_body_complete(ctx);
 
-else for (p = 0; p<len; p++)
+else for (int p = 0; p < len; p++)
   {
   uschar c = data[p];
   {
   uschar c = data[p];
+  int rc;
 
   if (ctx->flags & PDKIM_PAST_HDRS)
     {
 
   if (ctx->flags & PDKIM_PAST_HDRS)
     {
@@ -1048,7 +1075,7 @@ else for (p = 0; p<len; p++)
 
        ctx->flags = (ctx->flags & ~(PDKIM_SEEN_LF|PDKIM_SEEN_CR)) | PDKIM_PAST_HDRS;
        DEBUG(D_acl) debug_printf(
 
        ctx->flags = (ctx->flags & ~(PDKIM_SEEN_LF|PDKIM_SEEN_CR)) | PDKIM_PAST_HDRS;
        DEBUG(D_acl) debug_printf(
-           "PDKIM >> Body data for hash, canonicalized >>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
+           "DKIM >> Body data for hash, canonicalized >>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
        continue;
        }
       else
        continue;
        }
       else
@@ -1308,6 +1335,28 @@ return string_from_gstring(hdr);
 
 /* -------------------------------------------------------------------------- */
 
 
 /* -------------------------------------------------------------------------- */
 
+/* According to draft-ietf-dcrup-dkim-crypto-07 "keys are 256 bits" (referring
+to DNS, hence the pubkey).  Check for more than 32 bytes; if so assume the
+alternate possible representation (still) being discussed: a
+SubjectPublickeyInfo wrapped key - and drop all but the trailing 32-bytes (it
+should be a DER, with exactly 12 leading bytes - but we could accept a BER also,
+which could be any size).  We still rely on the crypto library for checking for
+undersize.
+
+When the RFC is published this should be re-addressed. */
+
+static void
+check_bare_ed25519_pubkey(pdkim_pubkey * p)
+{
+int excess = p->key.len - 32;
+if (excess > 0)
+  {
+  DEBUG(D_acl) debug_printf("DKIM: unexpected pubkey len %lu\n", p->key.len);
+  p->key.data += excess; p->key.len = 32;
+  }
+}
+
+
 static pdkim_pubkey *
 pdkim_key_from_dns(pdkim_ctx * ctx, pdkim_signature * sig, ev_ctx * vctx,
   const uschar ** errstr)
 static pdkim_pubkey *
 pdkim_key_from_dns(pdkim_ctx * ctx, pdkim_signature * sig, ev_ctx * vctx,
   const uschar ** errstr)
@@ -1319,7 +1368,7 @@ pdkim_pubkey * p;
 
 dns_txt_name = string_sprintf("%s._domainkey.%s.", sig->selector, sig->domain);
 
 
 dns_txt_name = string_sprintf("%s._domainkey.%s.", sig->selector, sig->domain);
 
-if (  !(dns_txt_reply = ctx->dns_txt_callback(CS dns_txt_name))
+if (  !(dns_txt_reply = ctx->dns_txt_callback(dns_txt_name))
    || dns_txt_reply[0] == '\0'
    )
   {
    || dns_txt_reply[0] == '\0'
    )
   {
@@ -1331,14 +1380,14 @@ if (  !(dns_txt_reply = ctx->dns_txt_callback(CS dns_txt_name))
 DEBUG(D_acl)
   {
   debug_printf(
 DEBUG(D_acl)
   {
   debug_printf(
-    "PDKIM >> Parsing public key record >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"
+    "DKIM >> Parsing public key record >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"
     " %s\n"
     " Raw record: ",
     dns_txt_name);
   pdkim_quoteprint(CUS dns_txt_reply, Ustrlen(dns_txt_reply));
   }
 
     " %s\n"
     " Raw record: ",
     dns_txt_name);
   pdkim_quoteprint(CUS dns_txt_reply, Ustrlen(dns_txt_reply));
   }
 
-if (  !(p = pdkim_parse_pubkey_record(ctx, CUS dns_txt_reply))
+if (  !(p = pdkim_parse_pubkey_record(CUS dns_txt_reply))
    || (Ustrcmp(p->srvtype, "*") != 0 && Ustrcmp(p->srvtype, "email") != 0)
    )
   {
    || (Ustrcmp(p->srvtype, "*") != 0 && Ustrcmp(p->srvtype, "email") != 0)
    )
   {
@@ -1352,16 +1401,33 @@ if (  !(p = pdkim_parse_pubkey_record(ctx, CUS dns_txt_reply))
     else
       debug_printf(" Error while parsing public key record\n");
     debug_printf(
     else
       debug_printf(" Error while parsing public key record\n");
     debug_printf(
-      "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
+      "DKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
     }
   return NULL;
   }
 
 DEBUG(D_acl) debug_printf(
     }
   return NULL;
   }
 
 DEBUG(D_acl) debug_printf(
-      "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
+      "DKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
 
 /* Import public key */
 
 
 /* Import public key */
 
+/* Normally we use the signature a= tag to tell us the pubkey format.
+When signing under debug we do a test-import of the pubkey, and at that
+time we do not have a signature so we must interpret the pubkey k= tag
+instead.  Assume writing on the sig is ok in that case. */
+
+if (sig->keytype < 0)
+  if ((sig->keytype = pdkim_keyname_to_keytype(p->keytype)) < 0)
+    {
+    DEBUG(D_acl) debug_printf("verify_init: unhandled keytype %s\n", p->keytype);
+    sig->verify_status =      PDKIM_VERIFY_INVALID;
+    sig->verify_ext_status =  PDKIM_VERIFY_INVALID_PUBKEY_IMPORT;
+    return NULL;
+    }
+
+if (sig->keytype == KEYTYPE_ED25519)
+  check_bare_ed25519_pubkey(p);
+
 if ((*errstr = exim_dkim_verify_init(&p->key,
            sig->keytype == KEYTYPE_ED25519 ? KEYFMT_ED25519_BARE : KEYFMT_DER,
            vctx)))
 if ((*errstr = exim_dkim_verify_init(&p->key,
            sig->keytype == KEYTYPE_ED25519 ? KEYFMT_ED25519_BARE : KEYFMT_DER,
            vctx)))
@@ -1377,16 +1443,68 @@ return p;
 }
 
 
 }
 
 
+/* -------------------------------------------------------------------------- */
+/* Sort and filter the sigs developed from the message */
+
+static pdkim_signature *
+sort_sig_methods(pdkim_signature * siglist)
+{
+pdkim_signature * yield, ** ss;
+const uschar * prefs;
+uschar * ele;
+int sep;
+
+if (!siglist) return NULL;
+
+/* first select in order of hashtypes */
+DEBUG(D_acl) debug_printf("DKIM: dkim_verify_hashes   '%s'\n", dkim_verify_hashes);
+for (prefs = dkim_verify_hashes, sep = 0, yield = NULL, ss = &yield;
+     ele = string_nextinlist(&prefs, &sep, NULL, 0); )
+  {
+  int i = pdkim_hashname_to_hashtype(CUS ele, 0);
+  for (pdkim_signature * s = siglist, * next, ** prev = &siglist; s;
+       s = next)
+    {
+    next = s->next;
+    if (s->hashtype == i)
+      { *prev = next; s->next = NULL; *ss = s; ss = &s->next; }
+    else
+      prev = &s->next;
+    }
+  }
+
+/* then in order of keytypes */
+siglist = yield;
+DEBUG(D_acl) debug_printf("DKIM: dkim_verify_keytypes '%s'\n", dkim_verify_keytypes);
+for (prefs = dkim_verify_keytypes, sep = 0, yield = NULL, ss = &yield;
+     ele = string_nextinlist(&prefs, &sep, NULL, 0); )
+  {
+  int i = pdkim_keyname_to_keytype(CUS ele);
+  for (pdkim_signature * s = siglist, * next, ** prev = &siglist; s;
+       s = next)
+    {
+    next = s->next;
+    if (s->keytype == i)
+      { *prev = next; s->next = NULL; *ss = s; ss = &s->next; }
+    else
+      prev = &s->next;
+    }
+  }
+
+DEBUG(D_acl) for (pdkim_signature * s = yield; s; s = s->next)
+  debug_printf(" retain d=%s s=%s a=%s\n",
+    s->domain, s->selector, dkim_sig_to_a_tag(s));
+return yield;
+}
+
+
 /* -------------------------------------------------------------------------- */
 
 DLLEXPORT int
 pdkim_feed_finish(pdkim_ctx * ctx, pdkim_signature ** return_signatures,
   const uschar ** err)
 {
 /* -------------------------------------------------------------------------- */
 
 DLLEXPORT int
 pdkim_feed_finish(pdkim_ctx * ctx, pdkim_signature ** return_signatures,
   const uschar ** err)
 {
-pdkim_bodyhash * b;
-pdkim_signature * sig;
 BOOL verify_pass = FALSE;
 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
 
 /* 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
@@ -1399,24 +1517,32 @@ if (ctx->cur_header && ctx->cur_header->ptr > 0)
   if ((rc = pdkim_header_complete(ctx)) != PDKIM_OK)
     return rc;
 
   if ((rc = pdkim_header_complete(ctx)) != PDKIM_OK)
     return rc;
 
-  for (b = ctx->bodyhash; b; b = b->next)
+  for (pdkim_bodyhash * b = ctx->bodyhash; b; b = b->next)
     rnl = pdkim_update_ctx_bodyhash(b, &lineending, rnl);
   if (rnl) store_free(rnl);
   }
 else
   DEBUG(D_acl) debug_printf(
     rnl = pdkim_update_ctx_bodyhash(b, &lineending, rnl);
   if (rnl) store_free(rnl);
   }
 else
   DEBUG(D_acl) debug_printf(
-      "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
+      "DKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
+
+/* Build (and/or evaluate) body hash.  Do this even if no DKIM sigs, in case we
+have a hash to do for ARC. */
+
+pdkim_finish_bodyhash(ctx);
+
+/* Sort and filter the recived signatures */
+
+if (!(ctx->flags & PDKIM_MODE_SIGN))
+  ctx->sig = sort_sig_methods(ctx->sig);
 
 if (!ctx->sig)
   {
 
 if (!ctx->sig)
   {
-  DEBUG(D_acl) debug_printf("PDKIM: no signatures\n");
+  DEBUG(D_acl) debug_printf("DKIM: no signatures\n");
+  *return_signatures = NULL;
   return PDKIM_OK;
   }
 
   return PDKIM_OK;
   }
 
-/* Build (and/or evaluate) body hash */
-pdkim_finish_bodyhash(ctx);
-
-for (sig = ctx->sig; sig; sig = sig->next)
+for (pdkim_signature * sig = ctx->sig; sig; sig = sig->next)
   {
   hctx hhash_ctx;
   uschar * sig_hdr = US"";
   {
   hctx hhash_ctx;
   uschar * sig_hdr = US"";
@@ -1424,8 +1550,16 @@ for (sig = ctx->sig; sig; sig = sig->next)
   gstring * hdata = NULL;
   es_ctx sctx;
 
   gstring * hdata = NULL;
   es_ctx sctx;
 
+  if (  !(ctx->flags & PDKIM_MODE_SIGN)
+     && sig->verify_status == PDKIM_VERIFY_FAIL)
+    {
+    DEBUG(D_acl)
+       debug_printf("DKIM: [%s] abandoning this signature\n", sig->domain);
+    continue;
+    }
+
   /*XXX The hash of the headers is needed for GCrypt (for which we can do RSA
   /*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 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)
   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)
@@ -1435,7 +1569,7 @@ for (sig = ctx->sig; sig; sig = sig->next)
   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.
   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
   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
@@ -1448,18 +1582,18 @@ for (sig = ctx->sig; sig; sig = sig->next)
   if (!exim_sha_init(&hhash_ctx, pdkim_hashes[sig->hashtype].exim_hashmethod))
     {
     log_write(0, LOG_MAIN|LOG_PANIC,
   if (!exim_sha_init(&hhash_ctx, pdkim_hashes[sig->hashtype].exim_hashmethod))
     {
     log_write(0, LOG_MAIN|LOG_PANIC,
-      "PDKIM: hash setup error, possibly nonhandled hashtype");
+      "DKIM: hash setup error, possibly nonhandled hashtype");
     break;
     }
 
   if (ctx->flags & PDKIM_MODE_SIGN)
     DEBUG(D_acl) debug_printf(
     break;
     }
 
   if (ctx->flags & PDKIM_MODE_SIGN)
     DEBUG(D_acl) debug_printf(
-       "PDKIM >> Headers to be signed:                            >>>>>>>>>>>>\n"
+       "DKIM >> Headers to be signed:                            >>>>>>>>>>>>\n"
        " %s\n",
        sig->sign_headers);
 
   DEBUG(D_acl) debug_printf(
        " %s\n",
        sig->sign_headers);
 
   DEBUG(D_acl) debug_printf(
-      "PDKIM >> Header data for hash, canonicalized (%-7s), in sequence >>\n",
+      "DKIM >> Header data for hash, canonicalized (%-7s), in sequence >>\n",
        pdkim_canons[sig->canon_headers]);
 
 
        pdkim_canons[sig->canon_headers]);
 
 
@@ -1472,7 +1606,6 @@ for (sig = ctx->sig; sig; sig = sig->next)
   if (ctx->flags & PDKIM_MODE_SIGN)
     {
     gstring * g = NULL;
   if (ctx->flags & PDKIM_MODE_SIGN)
     {
     gstring * g = NULL;
-    pdkim_stringlist *p;
     const uschar * l;
     uschar * s;
     int sep = 0;
     const uschar * l;
     uschar * s;
     int sep = 0;
@@ -1480,16 +1613,15 @@ for (sig = ctx->sig; sig; sig = sig->next)
     /* Import private key, including the keytype which we need for building
     the signature header  */
 
     /* 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)))
+    if ((*err = exim_dkim_signing_init(CUS sig->privkey, &sctx)))
       {
       log_write(0, LOG_MAIN|LOG_PANIC, "signing_init: %s", *err);
       return PDKIM_ERR_RSA_PRIVKEY;
       }
     sig->keytype = sctx.keytype;
 
       {
       log_write(0, LOG_MAIN|LOG_PANIC, "signing_init: %s", *err);
       return PDKIM_ERR_RSA_PRIVKEY;
       }
     sig->keytype = sctx.keytype;
 
-    for (sig->headernames = NULL,              /* Collected signed header names */
-         p = sig->headers; p; p = p->next)
+    sig->headernames = NULL;                   /* Collected signed header names */
+    for (pdkim_stringlist * p = sig->headers; p; p = p->next)
       {
       uschar * rh = p->value;
 
       {
       uschar * rh = p->value;
 
@@ -1536,12 +1668,11 @@ for (sig = ctx->sig; sig; sig = sig->next)
     {
     uschar * p = sig->headernames;
     uschar * q;
     {
     uschar * p = sig->headernames;
     uschar * q;
-    pdkim_stringlist * hdrs;
 
     if (p)
       {
       /* clear tags */
 
     if (p)
       {
       /* clear tags */
-      for (hdrs = ctx->headers; hdrs; hdrs = hdrs->next)
+      for (pdkim_stringlist * hdrs = ctx->headers; hdrs; hdrs = hdrs->next)
        hdrs->tag = 0;
 
       p = string_copy(p);
        hdrs->tag = 0;
 
       p = string_copy(p);
@@ -1551,7 +1682,7 @@ for (sig = ctx->sig; sig; sig = sig->next)
          *q = '\0';
 
   /*XXX walk the list of headers in same order as received. */
          *q = '\0';
 
   /*XXX walk the list of headers in same order as received. */
-       for (hdrs = ctx->headers; hdrs; hdrs = hdrs->next)
+       for (pdkim_stringlist * hdrs = ctx->headers; hdrs; hdrs = hdrs->next)
          if (  hdrs->tag == 0
             && strncasecmp(CCS hdrs->value, CCS p, Ustrlen(p)) == 0
             && (hdrs->value)[Ustrlen(p)] == ':'
          if (  hdrs->tag == 0
             && strncasecmp(CCS hdrs->value, CCS p, Ustrlen(p)) == 0
             && (hdrs->value)[Ustrlen(p)] == ':'
@@ -1580,15 +1711,15 @@ for (sig = ctx->sig; sig; sig = sig->next)
     }
 
   DEBUG(D_acl) debug_printf(
     }
 
   DEBUG(D_acl) debug_printf(
-           "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
+           "DKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
 
   DEBUG(D_acl)
     {
     debug_printf(
 
   DEBUG(D_acl)
     {
     debug_printf(
-           "PDKIM >> Signed DKIM-Signature header, pre-canonicalized >>>>>>>>>>>>>\n");
+           "DKIM >> Signed DKIM-Signature header, pre-canonicalized >>>>>>>>>>>>>\n");
     pdkim_quoteprint(CUS sig_hdr, Ustrlen(sig_hdr));
     debug_printf(
     pdkim_quoteprint(CUS sig_hdr, Ustrlen(sig_hdr));
     debug_printf(
-           "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
+           "DKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
     }
 
   /* Relax header if necessary */
     }
 
   /* Relax header if necessary */
@@ -1597,11 +1728,11 @@ for (sig = ctx->sig; sig; sig = sig->next)
 
   DEBUG(D_acl)
     {
 
   DEBUG(D_acl)
     {
-    debug_printf("PDKIM >> Signed DKIM-Signature header, canonicalized (%-7s) >>>>>>>\n",
+    debug_printf("DKIM >> Signed DKIM-Signature header, canonicalized (%-7s) >>>>>>>\n",
            pdkim_canons[sig->canon_headers]);
     pdkim_quoteprint(CUS sig_hdr, Ustrlen(sig_hdr));
     debug_printf(
            pdkim_canons[sig->canon_headers]);
     pdkim_quoteprint(CUS sig_hdr, Ustrlen(sig_hdr));
     debug_printf(
-           "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
+           "DKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
     }
 
   /* Finalize header hash */
     }
 
   /* Finalize header hash */
@@ -1610,7 +1741,7 @@ for (sig = ctx->sig; sig; sig = sig->next)
 
   DEBUG(D_acl)
     {
 
   DEBUG(D_acl)
     {
-    debug_printf("PDKIM [%s] Header %s computed: ",
+    debug_printf("DKIM [%s] Header %s computed: ",
       sig->domain, pdkim_hashes[sig->hashtype].dkim_hashname);
     pdkim_hexprint(hhash.data, hhash.len);
     }
       sig->domain, pdkim_hashes[sig->hashtype].dkim_hashname);
     pdkim_hexprint(hhash.data, hhash.len);
     }
@@ -1624,7 +1755,12 @@ for (sig = ctx->sig; sig; sig = sig->next)
   if (ctx->flags & PDKIM_MODE_SIGN)
     {
     hashmethod hm = sig->keytype == KEYTYPE_ED25519
   if (ctx->flags & PDKIM_MODE_SIGN)
     {
     hashmethod hm = sig->keytype == KEYTYPE_ED25519
-      ? HASH_SHA2_512 : pdkim_hashes[sig->hashtype].exim_hashmethod;
+#if defined(SIGN_OPENSSL)
+      ? HASH_NULL
+#else
+      ? HASH_SHA2_512
+#endif
+      : pdkim_hashes[sig->hashtype].exim_hashmethod;
 
 #ifdef SIGN_HAVE_ED25519
     /* For GCrypt, and for EC, we pass the hash-of-headers to the signing
 
 #ifdef SIGN_HAVE_ED25519
     /* For GCrypt, and for EC, we pass the hash-of-headers to the signing
@@ -1637,8 +1773,6 @@ for (sig = ctx->sig; sig; sig = sig->next)
       hhash.len = hdata->ptr;
       }
 
       hhash.len = hdata->ptr;
       }
 
-/*XXX extend for non-RSA algos */
-/*- done for GnuTLS */
     if ((*err = exim_dkim_sign(&sctx, hm, &hhash, &sig->sighash)))
       {
       log_write(0, LOG_MAIN|LOG_PANIC, "signing: %s", *err);
     if ((*err = exim_dkim_sign(&sctx, hm, &hhash, &sig->sighash)))
       {
       log_write(0, LOG_MAIN|LOG_PANIC, "signing: %s", *err);
@@ -1647,7 +1781,7 @@ for (sig = ctx->sig; sig; sig = sig->next)
 
     DEBUG(D_acl)
       {
 
     DEBUG(D_acl)
       {
-      debug_printf( "PDKIM [%s] b computed: ", sig->domain);
+      debug_printf( "DKIM [%s] b computed: ", sig->domain);
       pdkim_hexprint(sig->sighash.data, sig->sighash.len);
       }
 
       pdkim_hexprint(sig->sighash.data, sig->sighash.len);
       }
 
@@ -1658,6 +1792,7 @@ for (sig = ctx->sig; sig; sig = sig->next)
   else
     {
     ev_ctx vctx;
   else
     {
     ev_ctx vctx;
+    hashmethod hm;
 
     /* Make sure we have all required signature tags */
     if (!(  sig->domain        && *sig->domain
 
     /* Make sure we have all required signature tags */
     if (!(  sig->domain        && *sig->domain
@@ -1674,8 +1809,16 @@ for (sig = ctx->sig; sig; sig = sig->next)
       sig->verify_ext_status = PDKIM_VERIFY_INVALID_SIGNATURE_ERROR;
 
       DEBUG(D_acl) debug_printf(
       sig->verify_ext_status = PDKIM_VERIFY_INVALID_SIGNATURE_ERROR;
 
       DEBUG(D_acl) debug_printf(
-         " Error in DKIM-Signature header: tags missing or invalid\n"
-         "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
+         " Error in DKIM-Signature header: tags missing or invalid (%s)\n"
+         "DKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n",
+         !(sig->domain && *sig->domain) ? "d="
+         : !(sig->selector && *sig->selector) ? "s="
+         : !(sig->headernames && *sig->headernames) ? "h="
+         : !sig->bodyhash.data ? "bh="
+         : !sig->sighash.data ? "b="
+         : sig->keytype < 0 || sig->hashtype < 0 ? "a="
+         : "v="
+         );
       goto NEXT_VERIFY;
       }
 
       goto NEXT_VERIFY;
       }
 
@@ -1687,19 +1830,19 @@ for (sig = ctx->sig; sig; sig = sig->next)
 
       DEBUG(D_acl) debug_printf(
           " Error in DKIM-Signature header: unsupported DKIM version\n"
 
       DEBUG(D_acl) debug_printf(
           " Error in DKIM-Signature header: unsupported DKIM version\n"
-          "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
+          "DKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
       goto NEXT_VERIFY;
       }
 
     DEBUG(D_acl)
       {
       goto NEXT_VERIFY;
       }
 
     DEBUG(D_acl)
       {
-      debug_printf( "PDKIM [%s] b from mail: ", sig->domain);
+      debug_printf( "DKIM [%s] b from mail: ", sig->domain);
       pdkim_hexprint(sig->sighash.data, sig->sighash.len);
       }
 
     if (!(sig->pubkey = pdkim_key_from_dns(ctx, sig, &vctx, err)))
       {
       pdkim_hexprint(sig->sighash.data, sig->sighash.len);
       }
 
     if (!(sig->pubkey = pdkim_key_from_dns(ctx, sig, &vctx, err)))
       {
-      log_write(0, LOG_MAIN, "PDKIM: %s%s %s%s [failed key import]",
+      log_write(0, LOG_MAIN, "DKIM: %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;
        sig->domain   ? "d=" : "", sig->domain   ? sig->domain   : US"",
        sig->selector ? "s=" : "", sig->selector ? sig->selector : US"");
       goto NEXT_VERIFY;
@@ -1726,12 +1869,17 @@ for (sig = ctx->sig; sig; sig = sig->next)
        }
       }
 
        }
       }
 
+    hm = sig->keytype == KEYTYPE_ED25519
+#if defined(SIGN_OPENSSL)
+      ? HASH_NULL
+#else
+      ? HASH_SHA2_512
+#endif
+      : pdkim_hashes[sig->hashtype].exim_hashmethod;
+
     /* Check the signature */
     /* Check the signature */
-/*XXX extend for non-RSA algos */
-/*- done for GnuTLS */
-    if ((*err = exim_dkim_verify(&vctx,
-                               pdkim_hashes[sig->hashtype].exim_hashmethod,
-                               &hhash, &sig->sighash)))
+
+    if ((*err = exim_dkim_verify(&vctx, hm, &hhash, &sig->sighash)))
       {
       DEBUG(D_acl) debug_printf("headers verify: %s\n", *err);
       sig->verify_status =      PDKIM_VERIFY_FAIL;
       {
       DEBUG(D_acl) debug_printf("headers verify: %s\n", *err);
       sig->verify_status =      PDKIM_VERIFY_FAIL;
@@ -1745,13 +1893,14 @@ for (sig = ctx->sig; sig; sig = sig->next)
       {
       sig->verify_status = PDKIM_VERIFY_PASS;
       verify_pass = TRUE;
       {
       sig->verify_status = PDKIM_VERIFY_PASS;
       verify_pass = TRUE;
+      if (dkim_verify_minimal) break;
       }
 
 NEXT_VERIFY:
 
     DEBUG(D_acl)
       {
       }
 
 NEXT_VERIFY:
 
     DEBUG(D_acl)
       {
-      debug_printf("PDKIM [%s] %s signature status: %s",
+      debug_printf("DKIM [%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)
              sig->domain, dkim_sig_to_a_tag(sig),
              pdkim_verify_status_str(sig->verify_status));
       if (sig->verify_ext_status > 0)
@@ -1775,15 +1924,16 @@ return ctx->flags & PDKIM_MODE_SIGN  ||  verify_pass
 /* -------------------------------------------------------------------------- */
 
 DLLEXPORT pdkim_ctx *
 /* -------------------------------------------------------------------------- */
 
 DLLEXPORT pdkim_ctx *
-pdkim_init_verify(uschar * (*dns_txt_callback)(char *), BOOL dot_stuffing)
+pdkim_init_verify(uschar * (*dns_txt_callback)(const uschar *), BOOL dot_stuffing)
 {
 pdkim_ctx * ctx;
 
 {
 pdkim_ctx * ctx;
 
-ctx = store_get(sizeof(pdkim_ctx));
+ctx = store_get(sizeof(pdkim_ctx), FALSE);
 memset(ctx, 0, sizeof(pdkim_ctx));
 
 if (dot_stuffing) ctx->flags = PDKIM_DOT_TERM;
 memset(ctx, 0, sizeof(pdkim_ctx));
 
 if (dot_stuffing) ctx->flags = PDKIM_DOT_TERM;
-ctx->linebuf = store_get(PDKIM_MAX_BODY_LINE_LEN);
+/* The line-buffer is for message data, hence tainted */
+ctx->linebuf = store_get(PDKIM_MAX_BODY_LINE_LEN, TRUE);
 ctx->dns_txt_callback = dns_txt_callback;
 
 return ctx;
 ctx->dns_txt_callback = dns_txt_callback;
 
 return ctx;
@@ -1805,7 +1955,7 @@ if (!domain || !selector || !privkey)
 
 /* Allocate & init one signature struct */
 
 
 /* Allocate & init one signature struct */
 
-sig = store_get(sizeof(pdkim_signature));
+sig = store_get(sizeof(pdkim_signature), FALSE);
 memset(sig, 0, sizeof(pdkim_signature));
 
 sig->bodylength = -1;
 memset(sig, 0, sizeof(pdkim_signature));
 
 sig->bodylength = -1;
@@ -1821,7 +1971,7 @@ for (hashtype = 0; hashtype < nelem(pdkim_hashes); hashtype++)
 if (hashtype >= nelem(pdkim_hashes))
   {
   log_write(0, LOG_MAIN|LOG_PANIC,
 if (hashtype >= nelem(pdkim_hashes))
   {
   log_write(0, LOG_MAIN|LOG_PANIC,
-    "PDKIM: unrecognised hashname '%s'", hashname);
+    "DKIM: unrecognised hashname '%s'", hashname);
   return NULL;
   }
 
   return NULL;
   }
 
@@ -1830,10 +1980,10 @@ DEBUG(D_acl)
   pdkim_signature s = *sig;
   ev_ctx vctx;
 
   pdkim_signature s = *sig;
   ev_ctx vctx;
 
-  debug_printf("PDKIM (checking verify key)>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
+  debug_printf("DKIM (checking verify key)>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
   if (!pdkim_key_from_dns(ctx, &s, &vctx, errstr))
     debug_printf("WARNING: bad dkim key in dns\n");
   if (!pdkim_key_from_dns(ctx, &s, &vctx, errstr))
     debug_printf("WARNING: bad dkim key in dns\n");
-  debug_printf("PDKIM (finished checking verify key)<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
+  debug_printf("DKIM (finished checking verify key)<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
   }
 return sig;
 }
   }
 return sig;
 }
@@ -1869,39 +2019,60 @@ return;
 
 
 /* Set up a blob for calculating the bodyhash according to the
 
 
 /* Set up a blob for calculating the bodyhash according to the
-needs of this signature.  Use an existing one if possible, or
-create a new one.
+given needs.  Use an existing one if possible, or create a new one.
 
 
-Return: hashblob pointer, or NULL on error (only used as a boolean).
+Return: hashblob pointer, or NULL on error
 */
 pdkim_bodyhash *
 */
 pdkim_bodyhash *
-pdkim_set_bodyhash(pdkim_ctx * ctx, pdkim_signature * sig)
+pdkim_set_bodyhash(pdkim_ctx * ctx, int hashtype, int canon_method,
+       long bodylength)
 {
 pdkim_bodyhash * b;
 
 {
 pdkim_bodyhash * b;
 
+if (hashtype == -1 || canon_method == -1) return NULL;
+
 for (b = ctx->bodyhash; b; b = b->next)
 for (b = ctx->bodyhash; b; b = b->next)
-  if (  sig->hashtype == b->hashtype
-     && sig->canon_body == b->canon_method
-     && sig->bodylength == b->bodylength)
-    goto old;
+  if (  hashtype == b->hashtype
+     && canon_method == b->canon_method
+     && bodylength == b->bodylength)
+    {
+    DEBUG(D_receive) debug_printf("DKIM: using existing bodyhash %d/%d/%ld\n",
+                                 hashtype, canon_method, bodylength);
+    return b;
+    }
 
 
-b = store_get(sizeof(pdkim_bodyhash));
+DEBUG(D_receive) debug_printf("DKIM: new bodyhash %d/%d/%ld\n",
+                             hashtype, canon_method, bodylength);
+b = store_get(sizeof(pdkim_bodyhash), FALSE);
 b->next = ctx->bodyhash;
 b->next = ctx->bodyhash;
-b->hashtype = sig->hashtype;
-b->canon_method = sig->canon_body;
-b->bodylength = sig->bodylength;
+b->hashtype = hashtype;
+b->canon_method = canon_method;
+b->bodylength = bodylength;
 if (!exim_sha_init(&b->body_hash_ctx,          /*XXX hash method: extend for sha512 */
 if (!exim_sha_init(&b->body_hash_ctx,          /*XXX hash method: extend for sha512 */
-                 pdkim_hashes[sig->hashtype].exim_hashmethod))
+                 pdkim_hashes[hashtype].exim_hashmethod))
   {
   DEBUG(D_acl)
   {
   DEBUG(D_acl)
-    debug_printf("PDKIM: hash init error, possibly nonhandled hashtype\n");
+    debug_printf("DKIM: hash init error, possibly nonhandled hashtype\n");
   return NULL;
   }
 b->signed_body_bytes = 0;
 b->num_buffered_blanklines = 0;
 ctx->bodyhash = b;
   return NULL;
   }
 b->signed_body_bytes = 0;
 b->num_buffered_blanklines = 0;
 ctx->bodyhash = b;
+return b;
+}
 
 
-old:
+
+/* Set up a blob for calculating the bodyhash according to the
+needs of this signature.  Use an existing one if possible, or
+create a new one.
+
+Return: hashblob pointer, or NULL on error (only used as a boolean).
+*/
+pdkim_bodyhash *
+pdkim_set_sig_bodyhash(pdkim_ctx * ctx, pdkim_signature * sig)
+{
+pdkim_bodyhash * b = pdkim_set_bodyhash(ctx,
+                       sig->hashtype, sig->canon_body, sig->bodylength);
 sig->calc_body_hash = b;
 return b;
 }
 sig->calc_body_hash = b;
 return b;
 }
@@ -1912,11 +2083,12 @@ return b;
 
 void
 pdkim_init_context(pdkim_ctx * ctx, BOOL dot_stuffed,
 
 void
 pdkim_init_context(pdkim_ctx * ctx, BOOL dot_stuffed,
-  uschar * (*dns_txt_callback)(char *))
+  uschar * (*dns_txt_callback)(const uschar *))
 {
 memset(ctx, 0, sizeof(pdkim_ctx));
 ctx->flags = dot_stuffed ? PDKIM_MODE_SIGN | PDKIM_DOT_TERM : PDKIM_MODE_SIGN;
 {
 memset(ctx, 0, sizeof(pdkim_ctx));
 ctx->flags = dot_stuffed ? PDKIM_MODE_SIGN | PDKIM_DOT_TERM : PDKIM_MODE_SIGN;
-ctx->linebuf = store_get(PDKIM_MAX_BODY_LINE_LEN);
+/* The line buffer is for message data, hence tainted */
+ctx->linebuf = store_get(PDKIM_MAX_BODY_LINE_LEN, TRUE);
 DEBUG(D_acl) ctx->dns_txt_callback = dns_txt_callback;
 }
 
 DEBUG(D_acl) ctx->dns_txt_callback = dns_txt_callback;
 }