Merge branch '4.next'
[exim.git] / src / src / pdkim / pdkim.c
index 2a66909ba7181ff981f25428b5316772c1e1a1f0..5688c78a87796ef4c9c6a1db98e73924f35b96ea 100644 (file)
@@ -80,7 +80,7 @@ const pdkim_hashtype pdkim_hashes[] = {
 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 */
@@ -561,18 +561,18 @@ for (p = raw_hdr; ; p++)
            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);
-
-           sig->hashtype = pdkim_hashname_to_hashtype(++s, 0);
-           break;
+           const uschar * list = cur_val->s;
+           int sep = '-';
+           uschar * elem;
+
+           if ((elem = string_nextinlist(&list, &sep, NULL, 0)))
+             for(i = 0; i < nelem(pdkim_keytypes); i++)
+               if (Ustrcmp(elem, pdkim_keytypes[i]) == 0)
+                 { sig->keytype = i; break; }
+           if ((elem = string_nextinlist(&list, &sep, NULL, 0)))
+             for (i = 0; i < nelem(pdkim_hashes); i++)
+               if (Ustrcmp(elem, pdkim_hashes[i].dkim_hashname) == 0)
+                 { sig->hashtype = i; break; }
            }
 
          case 'c':                                     /* canonicalization */
@@ -792,7 +792,7 @@ pdkim_signature * sig;
 
 for (b = ctx->bodyhash; b; b = b->next)                /* Finish hashes */
   {
-  DEBUG(D_acl) debug_printf("PDKIM: finish bodyhash %d/%d/%d len %ld\n",
+  DEBUG(D_acl) debug_printf("PDKIM: 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);
   }
@@ -1336,6 +1336,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("PDKIM: 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)
@@ -1390,6 +1412,27 @@ DEBUG(D_acl) debug_printf(
 
 /* 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)
+  {
+  int i;
+  for(i = 0; i < nelem(pdkim_keytypes); i++)
+    if (Ustrcmp(p->keytype, pdkim_keytypes[i]) == 0)
+      { sig->keytype = i; goto k_ok; }
+  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;
+  }
+k_ok:
+
+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)))
@@ -1662,7 +1705,12 @@ for (sig = ctx->sig; sig; sig = sig->next)
   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
@@ -1675,8 +1723,6 @@ for (sig = ctx->sig; sig; sig = sig->next)
       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);
@@ -1696,6 +1742,7 @@ for (sig = ctx->sig; sig; sig = sig->next)
   else
     {
     ev_ctx vctx;
+    hashmethod hm;
 
     /* Make sure we have all required signature tags */
     if (!(  sig->domain        && *sig->domain
@@ -1772,12 +1819,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 */
-/*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;