DKIM: replace SHA and RSA routines from gnutls, under earlier library
authorJeremy Harris <jgh146exb@wizmail.org>
Mon, 1 Feb 2016 18:18:56 +0000 (18:18 +0000)
committerJeremy Harris <jgh146exb@wizmail.org>
Mon, 1 Feb 2016 19:07:32 +0000 (19:07 +0000)
versions, using libgcrypt and libtasn1 directly.  Bug 1772

23 files changed:
src/scripts/MakeLinks
src/src/dkim.c
src/src/dkim.h
src/src/exim.c
src/src/pdkim/Makefile
src/src/pdkim/README
src/src/pdkim/blob.h [new file with mode: 0644]
src/src/pdkim/crypt_ver.h
src/src/pdkim/hash.c [new file with mode: 0644]
src/src/pdkim/hash.h [new file with mode: 0644]
src/src/pdkim/part-x509parse.c [deleted file]
src/src/pdkim/pdkim.c
src/src/pdkim/pdkim.h
src/src/pdkim/rsa.c [new file with mode: 0644]
src/src/pdkim/rsa.h [new file with mode: 0644]
src/src/pdkim/sha1.c [deleted file]
src/src/pdkim/sha2.c [deleted file]
test/aux-fixed/dkim/sign.pl
test/confs/4503
test/log/4500
test/log/4503
test/scripts/4500-Domain-Keys-Identified-Mail/4500
test/scripts/4500-Domain-Keys-Identified-Mail/4503

index 4f6747f..8862140 100755 (executable)
@@ -82,17 +82,12 @@ cd ..
 # Likewise for the code for the PDKIM library
 mkdir pdkim
 cd pdkim
-for f in README Makefile crypt_ver.h part-x509parse.c pdkim.c \
-  pdkim.h sha1.c sha2.c
+for f in README Makefile crypt_ver.h pdkim.c \
+  pdkim.h hash.c hash.h rsa.c rsa.h blob.h
 do
   ln -s ../../src/pdkim/$f $f
 done
-mkdir polarssl
-cd polarssl
-for i in `ls ../../../src/pdkim/polarssl/` ; do
-  ln -s ../../../src/pdkim/polarssl/$i $i
-done
-cd ../..
+cd ..
 
 # The basic source files for Exim and utilities. NB local_scan.h gets linked,
 # but local_scan.c does not, because its location is taken from the build-time
index 36b103e..349947a 100644 (file)
@@ -60,6 +60,13 @@ return PDKIM_FAIL;
 
 
 void
+dkim_exim_init(void)
+{
+pdkim_init();
+}
+
+
+void
 dkim_exim_verify_init(void)
 {
 /* Free previous context if there is one */
@@ -129,7 +136,7 @@ for (sig = dkim_signatures; sig; sig = sig->next)
              sig->canon_headers == PDKIM_CANON_SIMPLE ?  "simple" : "relaxed",
              sig->canon_body == PDKIM_CANON_SIMPLE ?  "simple" : "relaxed",
              sig->algo == PDKIM_ALGO_RSA_SHA256 ?  "rsa-sha256" : "rsa-sha1",
-             sig->sigdata_len * 8
+             sig->sigdata.len * 8
              ),
 
        sig->identity ? string_sprintf("i=%s ", sig->identity) : US"",
@@ -255,7 +262,7 @@ for (sig = dkim_signatures; sig; sig = sig->next)
 
     dkim_signing_domain = US sig->domain;
     dkim_signing_selector = US sig->selector;
-    dkim_key_length = sig->sigdata_len * 8;
+    dkim_key_length = sig->sigdata.len * 8;
     return;
     }
 }
@@ -340,7 +347,7 @@ switch (what)
 
   case DKIM_HEADERNAMES:
     return dkim_cur_sig->headernames
-      ?  US dkim_cur_sig->headernames : dkim_exim_expand_defaults(what);
+      ? dkim_cur_sig->headernames : dkim_exim_expand_defaults(what);
 
   case DKIM_IDENTITY:
     return dkim_cur_sig->identity
index 39a0408..1655f8e 100644 (file)
@@ -5,6 +5,7 @@
 /* Copyright (c) University of Cambridge, 1995 - 2015 */
 /* See the file NOTICE for conditions of use and distribution. */
 
+void    dkim_exim_init(void);
 uschar *dkim_exim_sign(int, uschar *, const uschar *, uschar *, uschar *, uschar *);
 void    dkim_exim_verify_init(void);
 void    dkim_exim_verify_feed(uschar *, int);
index 28617a5..ebc71dd 100644 (file)
@@ -1760,7 +1760,6 @@ regex_whitelisted_macro =
 
 for (i = 0; i < REGEX_VARS; i++) regex_vars[i] = NULL;
 
-
 /* If the program is called as "mailq" treat it as equivalent to "exim -bp";
 this seems to be a generally accepted convention, since one finds symbolic
 links called "mailq" in standard OS configurations. */
@@ -4556,6 +4555,12 @@ if (list_config)
   }
 
 
+/* Initialise subsystems as required */
+#ifndef DISABLE_DKIM
+dkim_exim_init();
+#endif
+
+
 /* Handle a request to deliver one or more messages that are already on the
 queue. Values of msg_action other than MSG_DELIVER and MSG_LOAD are dealt with
 above. MSG_LOAD is handled with -be (which is the only time it applies) below.
index ec8bb83..c72a942 100644 (file)
@@ -1,6 +1,6 @@
 # Make file for building the pdkim library.
 
-OBJ = pdkim.o
+OBJ = pdkim.o hash.o rsa.o
 
 pdkim.a:         $(OBJ)
                 @$(RM_COMMAND) -f pdkim.a
@@ -12,9 +12,8 @@ pdkim.a:         $(OBJ)
 .c.o:;           @echo "$(CC) $*.c"
                 $(FE)$(CC) -c $(CFLAGS) $(INCLUDE) -I. $*.c
 
-part-x509parse.o:   $(HDRS) crypt_ver.h part-x509parse.c
-pdkim.o:            $(HDRS) crypt_ver.h pdkim.h pdkim.c
-sha1.o:             $(HDRS) crypt_ver.h sha1.c
-sha2.o:             $(HDRS) crypt_ver.h sha2.c
+pdkim.o:            $(HDRS) crypt_ver.h hash.h blob.h pdkim.h pdkim.c
+hash.o:             $(HDRS) crypt_ver.h hash.h blob.h pdkim.h hash.c
+rsa.o:              $(HDRS) crypt_ver.h rsa.h blob.h rsa.c
 
 # End
index de04cff..953e86e 100644 (file)
@@ -2,10 +2,8 @@ PDKIM - a RFC4871 (DKIM) implementation
 http://duncanthrax.net/pdkim/
 Copyright (C) 2009      Tom Kistner <tom@duncanthrax.net>
 
-Includes code from the PolarSSL project.
-http://polarssl.org
-Copyright (C) 2009      Paul Bakker <polarssl_maintainer@polarssl.org>
-Copyright (C) 2006-2008 Christophe Devine
+No longer includes code from the PolarSSL project.
+Copyright (C) 2016 Jeremy Harris <jgh@exim.org>
 
 This copy of PDKIM is included with Exim. For a standalone distribution,
 visit http://duncanthrax.net/pdkim/.
diff --git a/src/src/pdkim/blob.h b/src/src/pdkim/blob.h
new file mode 100644 (file)
index 0000000..e1481c9
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ *  PDKIM - a RFC4871 (DKIM) implementation
+ *
+ *  Copyright (C) 2016  Exim maintainers
+ *
+ *  RSA signing/verification interface
+ */
+
+#ifndef BLOB_H /* entire file */
+#define BLOB_H
+
+typedef struct {
+  uschar * data;
+  size_t   len;
+} blob;
+
+#endif
index 602d137..2a9dde9 100644 (file)
 
 
 #ifdef USE_GNUTLS
-# define RSA_GNUTLS
-
 # include <gnutls/gnutls.h>
+
+# if GNUTLS_VERSION_NUMBER > 0x020c00
+#  define RSA_GNUTLS
+# else
+#  define RSA_GCRYPT
+# endif
+
 # if GNUTLS_VERSION_NUMBER >= 0x020a00
 #  define SHA_GNUTLS
-
 # else
-#  define SHA_POLARSSL
+#  define SHA_GCRYPT
 # endif
 
 #else
diff --git a/src/src/pdkim/hash.c b/src/src/pdkim/hash.c
new file mode 100644 (file)
index 0000000..0751683
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ *  PDKIM - a RFC4871 (DKIM) implementation
+ *
+ *  Copyright (C) 2016  Exim maintainers
+ *
+ *  Hash interface functions
+ */
+
+#include "../exim.h"
+
+#ifndef DISABLE_DKIM   /* entire file */
+
+#ifndef SUPPORT_TLS
+# error Need SUPPORT_TLS for DKIM
+#endif
+
+#include "crypt_ver.h"
+
+#ifdef RSA_OPENSSL
+# include <openssl/rsa.h>
+# include <openssl/ssl.h>
+# include <openssl/err.h>
+#elif defined(RSA_GNUTLS)
+# include <gnutls/gnutls.h>
+# include <gnutls/x509.h>
+# ifdef RSA_VERIFY_GNUTLS
+#  include <gnutls/abstract.h>
+# endif
+#endif
+
+#ifdef SHA_GNUTLS
+# include <gnutls/crypto.h>
+#endif
+
+#include "hash.h"
+
+
+/******************************************************************************/
+#ifdef SHA_OPENSSL
+
+void
+exim_sha_init(hctx * h, BOOL sha1)
+{
+h->sha1 = sha1;
+h->hashlen = sha1 ? 20 : 32;
+if (h->sha1)
+  SHA1_Init  (&h->u.sha1);
+else
+  SHA256_Init(&h->u.sha2);
+}
+
+
+void
+exim_sha_update(hctx * h, const char * data, int len)
+{
+if (h->sha1)
+  SHA1_Update  (&h->u.sha1, data, len);
+else
+  SHA256_Update(&h->u.sha2, data, len);
+}
+
+
+void
+exim_sha_finish(hctx * h, blob * b)
+{
+b->data = store_get(b->len = h->hashlen);
+
+if (h->sha1)
+  SHA1_Final  (b->data, &h->u.sha1);
+else
+  SHA256_Final(b->data, &h->u.sha2);
+}
+
+
+
+#elif defined(SHA_GNUTLS)
+/******************************************************************************/
+
+void
+exim_sha_init(hctx * h, BOOL sha1)
+{
+h->sha1 = sha1;
+h->hashlen = sha1 ? 20 : 32;
+gnutls_hash_init(&h->sha, sha1 ? GNUTLS_DIG_SHA1 : GNUTLS_DIG_SHA256);
+}
+
+
+void
+exim_sha_update(hctx * h, const char * data, int len)
+{
+gnutls_hash(h->sha, data, len);
+}
+
+
+void
+exim_sha_finish(hctx * h, blob * b)
+{
+b->data = store_get(b->len = h->hashlen);
+gnutls_hash_output(h->sha, b->data);
+}
+
+
+
+#elif defined(SHA_GCRYPT)
+/******************************************************************************/
+
+void
+exim_sha_init(hctx * h, BOOL sha1)
+{
+h->sha1 = sha1;
+h->hashlen = sha1 ? 20 : 32;
+gcry_md_open(&h->sha, sha1 ? GCRY_MD_SHA1 : GCRY_MD_SHA256, 0);
+}
+
+
+void
+exim_sha_update(hctx * h, const char * data, int len)
+{
+gcry_md_write(h->sha, data, len);
+}
+
+
+void
+exim_sha_finish(hctx * h, blob * b)
+{
+b->data = store_get(b->len = h->hashlen);
+memcpy(b->data, gcry_md_read(h->sha, 0), h->hashlen);
+}
+
+
+
+
+#elif defined(SHA_POLARSSL)
+/******************************************************************************/
+
+void
+exim_sha_init(hctx * h, BOOL sha1)
+{
+h->sha1 = sha1;
+h->hashlen = sha1 ? 20 : 32;
+if (h->sha1)
+  sha1_starts(&h->u.sha1);
+else
+  sha2_starts(&h->u.sha2, 0);
+}
+
+
+void
+exim_sha_update(hctx * h, const char * data, int len)
+{
+if (h->sha1)
+  sha1_update(h->u.sha1, US data, len);
+else
+  sha2_update(h->u.sha2, US data, len);
+}
+
+
+void
+exim_sha_finish(hctx * h, blob * b)
+{
+b->data = store_get(b->len = h->hashlen);
+
+if (h->sha1)
+  sha1_finish(h->u.sha1, b->data);
+else
+  sha2_finish(h->u.sha2, b->data);
+}
+
+#endif
+/******************************************************************************/
+
+/* Common to all library versions */
+int
+exim_sha_hashlen(hctx * h)
+{
+return h->sha1 ? 20 : 32;
+}
+
+
+#endif /*DISABLE_DKIM*/
+/* End of File */
diff --git a/src/src/pdkim/hash.h b/src/src/pdkim/hash.h
new file mode 100644 (file)
index 0000000..afd7ea6
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ *  PDKIM - a RFC4871 (DKIM) implementation
+ *
+ *  Copyright (C) 2016  Exim maintainers
+ *
+ *  Hash interface functions
+ */
+
+#include "../exim.h"
+
+#if !defined(DISABLE_DKIM) && !defined(PDKIM_HASH_H)   /* entire file */
+#define PDKIM_HASH_H
+
+#ifndef SUPPORT_TLS
+# error Need SUPPORT_TLS for DKIM
+#endif
+
+#include "crypt_ver.h"
+#include "blob.h"
+
+#ifdef RSA_OPENSSL
+# include <openssl/rsa.h>
+# include <openssl/ssl.h>
+# include <openssl/err.h>
+#elif defined(RSA_GNUTLS)
+# include <gnutls/gnutls.h>
+# include <gnutls/x509.h>
+#endif
+
+#ifdef SHA_GNUTLS
+# include <gnutls/crypto.h>
+#elif defined(SHA_GCRYPT)
+# include <gcrypt.h>
+#elif defined(SHA_POLARSSL)
+# include "pdkim.h"
+# include "polarssl/sha1.h"
+# include "polarssl/sha2.h"
+#endif
+
+/* Hash context */
+typedef struct {
+  int sha1;
+  int hashlen;
+
+#ifdef SHA_OPENSSL
+  union {
+    SHA_CTX      sha1;       /* SHA1 block                                */
+    SHA256_CTX   sha2;       /* SHA256 block                              */
+  } u;
+
+#elif defined(SHA_GNUTLS)
+  gnutls_hash_hd_t sha;      /* Either SHA1 or SHA256 block               */
+
+#elif defined(SHA_GCRYPT)
+  gcry_md_hd_t sha;          /* Either SHA1 or SHA256 block               */
+
+#elif defined(SHA_POLARSSL)
+  union {
+    sha1_context sha1;       /* SHA1 block                                */
+    sha2_context sha2;       /* SHA256 block                              */
+  } u;
+#endif
+
+} hctx;
+
+#if defined(SHA_OPENSSL)
+# include "pdkim.h"
+#elif defined(SHA_GCRYPT)
+# include "pdkim.h"
+#endif
+
+
+extern void     exim_sha_init(hctx *, BOOL);
+extern void     exim_sha_update(hctx *, const char *a, int);
+extern void     exim_sha_finish(hctx *, blob *);
+extern int      exim_sha_hashlen(hctx *);
+
+#endif /*DISABLE_DKIM*/
+/* End of File */
diff --git a/src/src/pdkim/part-x509parse.c b/src/src/pdkim/part-x509parse.c
deleted file mode 100644 (file)
index 5788777..0000000
+++ /dev/null
@@ -1,153 +0,0 @@
-#include "crypt_ver.h"
-
-#ifdef SHA_POLARSSL    /* remainder of file */
-
-#include "polarssl/bignum.h"
-#include "polarssl/part-x509.h"
-#include "polarssl/private-x509parse_c.h"
-
-/* all calls are from src/pdkim/pdkim-rsa.c */
-
-/* *************** begin copy from x509parse.c ********************/
-/*
- *  X.509 certificate and private key decoding
- *
- *  Copyright (C) 2006-2010, Brainspark B.V.
- *
- *  This file is part of PolarSSL (http://www.polarssl.org)
- *  Lead Maintainer: Paul Bakker <polarssl_maintainer at polarssl.org>
- *
- *  All rights reserved.
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License along
- *  with this program; if not, write to the Free Software Foundation, Inc.,
- *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-/*
- *  The ITU-T X.509 standard defines a certificat format for PKI.
- *
- *  http://www.ietf.org/rfc/rfc2459.txt
- *  http://www.ietf.org/rfc/rfc3279.txt
- *
- *  ftp://ftp.rsasecurity.com/pub/pkcs/ascii/pkcs-1v2.asc
- *
- *  http://www.itu.int/ITU-T/studygroups/com17/languages/X.680-0207.pdf
- *  http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf
- */
-
-
-/*
- * ASN.1 DER decoding routines
- */
-static int asn1_get_len( unsigned char **p,
-                         const unsigned char *end,
-                         int *len )
-{
-    if( ( end - *p ) < 1 )
-        return( POLARSSL_ERR_ASN1_OUT_OF_DATA );
-
-    if( ( **p & 0x80 ) == 0 )
-        *len = *(*p)++;
-    else
-    {
-        switch( **p & 0x7F )
-        {
-        case 1:
-            if( ( end - *p ) < 2 )
-                return( POLARSSL_ERR_ASN1_OUT_OF_DATA );
-
-            *len = (*p)[1];
-            (*p) += 2;
-            break;
-
-        case 2:
-            if( ( end - *p ) < 3 )
-                return( POLARSSL_ERR_ASN1_OUT_OF_DATA );
-
-            *len = ( (*p)[1] << 8 ) | (*p)[2];
-            (*p) += 3;
-            break;
-
-        default:
-            return( POLARSSL_ERR_ASN1_INVALID_LENGTH );
-            break;
-        }
-    }
-
-    if( *len > (int) ( end - *p ) )
-        return( POLARSSL_ERR_ASN1_OUT_OF_DATA );
-
-    return( 0 );
-}
-
-/* This function is not exported by PolarSSL 0.14.2
- * static */
-int asn1_get_tag( unsigned char **p,
-                         const unsigned char *end,
-                         int *len, int tag )
-{
-    if( ( end - *p ) < 1 )
-        return( POLARSSL_ERR_ASN1_OUT_OF_DATA );
-
-    if( **p != tag )
-        return( POLARSSL_ERR_ASN1_UNEXPECTED_TAG );
-
-    (*p)++;
-
-    return( asn1_get_len( p, end, len ) );
-}
-
-/* This function is not exported by PolarSSL 0.14.2
- * static */
-int asn1_get_int( unsigned char **p,
-                         const unsigned char *end,
-                         int *val )
-{
-    int ret, len;
-
-    if( ( ret = asn1_get_tag( p, end, &len, ASN1_INTEGER ) ) != 0 )
-        return( ret );
-
-    if( len > (int) sizeof( int ) || ( **p & 0x80 ) != 0 )
-        return( POLARSSL_ERR_ASN1_INVALID_LENGTH );
-
-    *val = 0;
-
-    while( len-- > 0 )
-    {
-        *val = ( *val << 8 ) | **p;
-        (*p)++;
-    }
-
-    return( 0 );
-}
-
-/* This function is not exported by PolarSSL 0.14.2
- * static */
-int asn1_get_mpi( unsigned char **p,
-                         const unsigned char *end,
-                         mpi *X )
-{
-    int ret, len;
-
-    if( ( ret = asn1_get_tag( p, end, &len, ASN1_INTEGER ) ) != 0 )
-        return( ret );
-
-    ret = mpi_read_binary( X, *p, len );
-
-    *p += len;
-
-    return( ret );
-}
-/* ***************   end copy from x509parse.c ********************/
-#endif
index 6e471a6..789d650 100644 (file)
 #elif defined(RSA_GNUTLS)
 # include <gnutls/gnutls.h>
 # include <gnutls/x509.h>
-# include <gnutls/abstract.h>
-#endif
-
-#ifdef SHA_GNUTLS
-# include <gnutls/crypto.h>
-#elif defined(SHA_POLARSSL)
-# include "polarssl/sha1.h"
-# include "polarssl/sha2.h"
 #endif
 
 #include "pdkim.h"
+#include "rsa.h"
 
 #define PDKIM_SIGNATURE_VERSION     "1"
 #define PDKIM_PUB_RECORD_VERSION    "DKIM1"
@@ -155,8 +148,8 @@ pdkim_verify_ext_status_str(int ext_status)
 
 /* -------------------------------------------------------------------------- */
 /* Print debugging functions */
-void
-pdkim_quoteprint(const char *data, int len, int lf)
+static void
+pdkim_quoteprint(const char *data, int len)
 {
 int i;
 const unsigned char *p = (const unsigned char *)data;
@@ -180,20 +173,18 @@ for (i = 0; i < len; i++)
       break;
     }
   }
-if (lf)
-  debug_printf("\n");
+debug_printf("\n");
 }
 
-void
-pdkim_hexprint(const char *data, int len, int lf)
+static void
+pdkim_hexprint(const char *data, int len)
 {
 int i;
 const unsigned char *p = (const unsigned char *)data;
 
 for (i = 0 ; i < len; i++)
   debug_printf("%02x", p[i]);
-if (lf)
-  debug_printf("\n");
+debug_printf("\n");
 }
 
 
@@ -318,7 +309,6 @@ if (pub)
   if (pub->keytype    ) free(pub->keytype);
   if (pub->srvtype    ) free(pub->srvtype);
   if (pub->notes      ) free(pub->notes);
-/*  if (pub->key        ) free(pub->key); */
   free(pub);
   }
 }
@@ -345,7 +335,6 @@ if (sig)
   if (sig->selector        ) free(sig->selector);
   if (sig->domain          ) free(sig->domain);
   if (sig->identity        ) free(sig->identity);
-  if (sig->headernames     ) free(sig->headernames);
   if (sig->copiedheaders   ) free(sig->copiedheaders);
   if (sig->rsa_privkey     ) free(sig->rsa_privkey);
   if (sig->sign_headers    ) free(sig->sign_headers);
@@ -560,37 +549,26 @@ return n;
 
 /* -------------------------------------------------------------------------- */
 
-static char *
-pdkim_decode_base64(char *str, int *num_decoded)
+static void
+pdkim_decode_base64(uschar *str, blob * b)
 {
-int dlen = 0;
+int dlen;
 char *res;
-int old_pool = store_pool;
-
-/* There is a store-reset between header & body reception
-so cannot use the main pool */
-
-store_pool = POOL_PERM;
-dlen = b64decode(US str, USS &res);
-store_pool = old_pool;
-
-if (dlen < 0) return NULL;
-
-if (num_decoded) *num_decoded = dlen;
-return res;
+dlen = b64decode(str, &b->data);
+if (dlen < 0) b->data = NULL;
+b->len = dlen;
 }
 
-
 /* -------------------------------------------------------------------------- */
 
 static char *
-pdkim_encode_base64(char *str, int num)
+pdkim_encode_base64(blob * b)
 {
 char * ret;
 int old_pool = store_pool;
 
 store_pool = POOL_PERM;
-ret = CS b64encode(US str, num);
+ret = CS b64encode(b->data, b->len);
 store_pool = old_pool;
 return ret;
 }
@@ -612,6 +590,13 @@ BOOL past_hname = FALSE;
 BOOL in_b_val = FALSE;
 int where = PDKIM_HDR_LIMBO;
 int i;
+int old_pool = store_pool;
+
+/* There is a store-reset between header & body reception
+so cannot use the main pool. Any allocs done by Exim
+memory-handling must use the perm pool. */
+
+store_pool = POOL_PERM;
 
 if (!(sig = malloc(sizeof(pdkim_signature)))) return NULL;
 memset(sig, 0, sizeof(pdkim_signature));
@@ -689,11 +674,9 @@ for (p = raw_hdr; ; p++)
          {
          case 'b':
            if (cur_tag->str[1] == 'h')
-             sig->bodyhash = pdkim_decode_base64(cur_val->str,
-                 &sig->bodyhash_len);
+             pdkim_decode_base64(US cur_val->str, &sig->bodyhash);
            else
-             sig->sigdata = pdkim_decode_base64(cur_val->str,
-                 &sig->sigdata_len);
+             pdkim_decode_base64(US cur_val->str, &sig->sigdata);
            break;
          case 'v':
              /* We only support version 1, and that is currently the
@@ -739,7 +722,7 @@ for (p = raw_hdr; ; p++)
          case 'l':
            sig->bodylength = strtol(cur_val->str, NULL, 10); break;
          case 'h':
-           sig->headernames = strdup(cur_val->str); break;
+           sig->headernames = string_copy(cur_val->str); break;
          case 'z':
            sig->copiedheaders = pdkim_decode_qp(cur_val->str); break;
          default:
@@ -764,12 +747,12 @@ NEXT_CHAR:
     *q++ = c;
   }
 
+store_pool = old_pool;
+
 /* Make sure the most important bits are there. */
 if (!(sig->domain      && (*(sig->domain)      != '\0') &&
       sig->selector    && (*(sig->selector)    != '\0') &&
       sig->headernames && (*(sig->headernames) != '\0') &&
-      sig->bodyhash    &&
-      sig->sigdata     &&
       sig->version))
   {
   pdkim_free_sig(sig);
@@ -786,38 +769,14 @@ DEBUG(D_acl)
   {
   debug_printf(
          "PDKIM >> Raw signature w/o b= tag value >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
-  pdkim_quoteprint(sig->rawsig_no_b_val, strlen(sig->rawsig_no_b_val), 1);
+  pdkim_quoteprint(sig->rawsig_no_b_val, strlen(sig->rawsig_no_b_val));
   debug_printf(
-         "PDKIM >> Sig size: %4d bits\n", sig->sigdata_len*8);
+         "PDKIM >> Sig size: %4d bits\n", sig->sigdata.len*8);
   debug_printf(
          "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
   }
 
-#ifdef SHA_OPENSSL
-
-SHA1_Init  (&sig->sha1_body);
-SHA256_Init(&sig->sha2_body);
-
-#elif defined(SHA_GNUTLS)
-
-gnutls_hash_init(&sig->sha_body,
-  sig->algo == PDKIM_ALGO_RSA_SHA1 ? GNUTLS_DIG_SHA1 : GNUTLS_DIG_SHA256);
-
-#elif defined(SHA_POLARSSL)
-
-if (  !(sig->sha1_body = malloc(sizeof(sha1_context)))
-   || !(sig->sha2_body = malloc(sizeof(sha2_context)))
-   )
-  {
-  pdkim_free_sig(sig);
-  return NULL;
-  }
-
-sha1_starts(sig->sha1_body);
-sha2_starts(sig->sha2_body, 0);
-
-#endif /* SHA impl */
-
+exim_sha_init(&sig->body_hash, sig->algo == PDKIM_ALGO_RSA_SHA1);
 return sig;
 }
 
@@ -898,7 +857,8 @@ for (p = raw_record; ; p++)
          case 'n':
            pub->notes = pdkim_decode_qp(cur_val->str); break;
          case 'p':
-           pub->key = pdkim_decode_base64(cur_val->str, &(pub->key_len)); break;
+           pdkim_decode_base64(US cur_val->str, &pub->key);
+            break;
          case 'k':
            pub->hashes = strdup(cur_val->str); break;
          case 's':
@@ -931,7 +891,7 @@ if (!pub->keytype    ) pub->keytype     = strdup("rsa");
 if (!pub->srvtype    ) pub->srvtype     = strdup("*");
 
 /* p= is required */
-if (pub->key)
+if (pub->key.data)
   return pub;
 
 pdkim_free_pubkey(pub);
@@ -1002,23 +962,9 @@ while (sig)
 
   if (canon_len > 0)
     {
-#ifdef SHA_GNUTLS
-    gnutls_hash(sig->sha_body, canon_data, canon_len);
-#else
-    if (sig->algo == PDKIM_ALGO_RSA_SHA1)
-# ifdef SHA_OPENSSL
-      SHA1_Update  (&sig->sha1_body, canon_data, canon_len);
-    else
-      SHA256_Update(&sig->sha2_body, canon_data, canon_len);
-# elif defined(SHA_POLARSSL)
-      sha1_update(sig->sha1_body, US canon_data, canon_len);
-    else
-      sha2_update(sig->sha2_body, US canon_data, canon_len);
-# endif
-#endif
-
+    exim_sha_update(&sig->body_hash, canon_data, canon_len);
     sig->signed_body_bytes += canon_len;
-    DEBUG(D_acl) pdkim_quoteprint(canon_data, canon_len, 1);
+    DEBUG(D_acl) pdkim_quoteprint(canon_data, canon_len);
     }
 
   sig = sig->next;
@@ -1039,36 +985,22 @@ pdkim_signature *sig;
 /* Traverse all signatures */
 for (sig = ctx->sig; sig; sig = sig->next)
   {                                    /* Finish hashes */
-  uschar bh[32]; /* SHA-256 = 32 Bytes,  SHA-1 = 20 Bytes */
-
-#ifdef SHA_GNUTLS
-  gnutls_hash_output(sig->sha_body, bh);
-#else
-  if (sig->algo == PDKIM_ALGO_RSA_SHA1)
-# ifdef SHA_OPENSSL
-    SHA1_Final  (bh, &sig->sha1_body);
-  else
-    SHA256_Final(bh, &sig->sha2_body);
-# elif defined(SHA_POLARSSL)
-    sha1_finish(sig->sha1_body, bh);
-  else
-    sha2_finish(sig->sha2_body, bh);
-# endif
-#endif
+  blob bh;
+
+  exim_sha_finish(&sig->body_hash, &bh);
 
   DEBUG(D_acl)
     {
     debug_printf("PDKIM [%s] Body bytes hashed: %lu\n"
                 "PDKIM [%s] bh  computed: ",
                sig->domain, sig->signed_body_bytes, sig->domain);
-    pdkim_hexprint((char *)bh, sig->algo == PDKIM_ALGO_RSA_SHA1 ? 20 : 32, 1);
+    pdkim_hexprint(CS bh.data, bh.len);
     }
 
   /* SIGNING -------------------------------------------------------------- */
   if (ctx->mode == PDKIM_MODE_SIGN)
     {
-    sig->bodyhash_len = sig->algo == PDKIM_ALGO_RSA_SHA1 ? 20:32;
-    sig->bodyhash = CS string_copyn(US bh, sig->bodyhash_len);
+    sig->bodyhash = bh;
 
     /* If bodylength limit is set, and we have received less bytes
        than the requested amount, effectively remove the limit tag. */
@@ -1080,8 +1012,7 @@ for (sig = ctx->sig; sig; sig = sig->next)
   else
     {
     /* Compare bodyhash */
-    if (memcmp(bh, sig->bodyhash,
-              (sig->algo == PDKIM_ALGO_RSA_SHA1)?20:32) == 0)
+    if (memcmp(bh.data, sig->bodyhash.data, bh.len) == 0)
       {
       DEBUG(D_acl) debug_printf("PDKIM [%s] Body hash verified OK\n", sig->domain);
       }
@@ -1090,8 +1021,8 @@ for (sig = ctx->sig; sig; sig = sig->next)
       DEBUG(D_acl)
         {
        debug_printf("PDKIM [%s] bh signature: ", sig->domain);
-       pdkim_hexprint(sig->bodyhash,
-                        sig->algo == PDKIM_ALGO_RSA_SHA1 ? 20 : 32, 1);
+       pdkim_hexprint(sig->bodyhash.data,
+                        exim_sha_hashlen(&sig->body_hash));
        debug_printf("PDKIM [%s] Body hash did NOT verify\n", sig->domain);
        }
       sig->verify_status     = PDKIM_VERIFY_FAIL;
@@ -1466,7 +1397,7 @@ return str->str;
 /* -------------------------------------------------------------------------- */
 
 static char *
-pdkim_create_header(pdkim_signature *sig, int final)
+pdkim_create_header(pdkim_signature *sig, BOOL final)
 {
 char *rc = NULL;
 char *base64_bh = NULL;
@@ -1481,7 +1412,7 @@ if (!(hdr = pdkim_strnew("DKIM-Signature: v="PDKIM_SIGNATURE_VERSION)))
 if (!(canon_all = pdkim_strnew(pdkim_canons[sig->canon_headers])))
   goto BAIL;
 
-if (!(base64_bh = pdkim_encode_base64(sig->bodyhash, sig->bodyhash_len)))
+if (!(base64_bh = pdkim_encode_base64(&sig->bodyhash)))
   goto BAIL;
 
 col = strlen(hdr->str);
@@ -1496,9 +1427,9 @@ if (  pdkim_headcat(&col, hdr, ";", "a=", pdkim_algos[sig->algo])
    && pdkim_headcat(&col, hdr, ";", "s=", sig->selector)
    )
   {
-  /* list of eader names can be split between items. */
+  /* list of header names can be split between items. */
     {
-    char *n = strdup(sig->headernames);
+    char *n = CS string_copy(sig->headernames);
     char *f = n;
     char *i = "h=";
     char *s = ";";
@@ -1513,13 +1444,11 @@ if (  pdkim_headcat(&col, hdr, ";", "a=", pdkim_algos[sig->algo])
       if (!i)
        if (!pdkim_headcat(&col, hdr, NULL, NULL, ":"))
          {
-         free(f);
          goto BAIL;
          }
 
       if (!pdkim_headcat(&col, hdr, s, i, n))
        {
-       free(f);
        goto BAIL;
        }
 
@@ -1530,7 +1459,6 @@ if (  pdkim_headcat(&col, hdr, ";", "a=", pdkim_algos[sig->algo])
       s = NULL;
       i = NULL;
       }
-    free(f);
     }
 
   if(!pdkim_headcat(&col, hdr, ";", "bh=", base64_bh))
@@ -1571,7 +1499,7 @@ if (  pdkim_headcat(&col, hdr, ";", "a=", pdkim_algos[sig->algo])
   /* Preliminary or final version? */
   if (final)
     {
-    if (!(base64_b = pdkim_encode_base64(sig->sigdata, sig->sigdata_len)))
+    if (!(base64_b = pdkim_encode_base64(&sig->sigdata)))
       goto BAIL;
     if (!pdkim_headcat(&col, hdr, ";", "b=", base64_b))
       goto BAIL;
@@ -1627,40 +1555,17 @@ if (ctx->mode == PDKIM_MODE_SIGN)
 
 while (sig)
   {
-#ifdef SHA_OPENSSL
-  SHA_CTX    sha1_headers;
-  SHA256_CTX sha2_headers;
-#elif defined(SHA_GNUTLS)
-  gnutls_hash_hd_t sha_headers;
-#elif defined(SHA_POLARSSL)
-  sha1_context sha1_headers;
-  sha2_context sha2_headers;
-#endif
-
-  char *sig_hdr;
-  char headerhash[32];
-
-#ifdef RSA_GNUTLS
-  uschar * hdata = NULL;
+  BOOL is_sha1 = sig->algo == PDKIM_ALGO_RSA_SHA1;
+  hctx hhash_ctx;
+  char * sig_hdr;
+  blob hhash;
+  blob hdata;
   int hdata_alloc = 0;
-  int hdata_size = 0;
-#endif
 
-#ifdef SHA_GNUTLS
-  gnutls_hash_init(&sha_headers,
-    sig->algo == PDKIM_ALGO_RSA_SHA1 ? GNUTLS_DIG_SHA1 : GNUTLS_DIG_SHA256);
-#else
-  if (sig->algo == PDKIM_ALGO_RSA_SHA1)
-# ifdef SHA_OPENSSL
-    SHA1_Init(&sha1_headers);
-  else
-    SHA256_Init(&sha2_headers);
-# elif defined(SHA_POLARSSL)
-    sha1_starts(&sha1_headers);
-  else
-    sha2_starts(&sha2_headers, 0);
-# endif
-#endif
+  hdata.data = NULL;
+  hdata.len = 0;
+
+  exim_sha_init(&hhash_ctx, is_sha1);
 
   DEBUG(D_acl) debug_printf(
       "PDKIM >> Hashed header data, canonicalized, in sequence >>>>>>>>>>>>>>\n");
@@ -1675,43 +1580,27 @@ while (sig)
 
     for (p = sig->headers; p; p = p->next)
       {
-      char *rh = NULL;
+      uschar * rh;
       /* Collect header names (Note: colon presence is guaranteed here) */
-      char *q = strchr(p->value, ':');
+      uschar * q = Ustrchr(p->value, ':');
 
       if (!(pdkim_strncat(headernames, p->value,
-                       (q-(p->value)) + (p->next ? 1 : 0))))
+                       (q-US (p->value)) + (p->next ? 1 : 0))))
        return PDKIM_ERR_OOM;
 
       rh = sig->canon_headers == PDKIM_CANON_RELAXED
-       ? pdkim_relax_header(p->value, 1) /* cook header for relaxed canon */
-       : strdup(p->value);              /* just copy it for simple canon */
+       ? US pdkim_relax_header(p->value, 1) /* cook header for relaxed canon */
+       : string_copy(p->value);             /* just copy it for simple canon */
       if (!rh)
        return PDKIM_ERR_OOM;
 
       /* Feed header to the hash algorithm */
-#ifdef SHA_GNUTLS
-      gnutls_hash(sha_headers, rh, strlen(rh));
-#else
-      if (sig->algo == PDKIM_ALGO_RSA_SHA1)
-# ifdef SHA_OPENSSL
-       SHA1_Update  (&sha1_headers, rh, strlen(rh));
-      else
-       SHA256_Update(&sha2_headers, rh, strlen(rh));
-# elif defined(SHA_POLARSSL)
-       sha1_update(&sha1_headers, US rh, strlen(rh));
-      else
-       sha2_update(&sha2_headers, US rh, strlen(rh));
-# endif
-#endif
+      exim_sha_update(&hhash_ctx, rh, strlen(rh));
 
-#ifdef RSA_GNUTLS
-      /* Remember headers block for signing */
-      hdata = string_append(hdata, &hdata_alloc, &hdata_size, 1, rh);
-#endif
+      /* Remember headers block for signing (when the library cannot do incremental)  */
+      (void) exim_rsa_data_append(&hdata, &hdata_alloc, rh);
 
-      DEBUG(D_acl) pdkim_quoteprint(rh, strlen(rh), 1);
-      free(rh);
+      DEBUG(D_acl) pdkim_quoteprint(rh, Ustrlen(rh));
       }
     }
 
@@ -1720,10 +1609,10 @@ while (sig)
      add the headers to the hash in that order. */
   else
     {
-    char *b = strdup(sig->headernames);
-    char *p = b;
-    char *q = NULL;
-    pdkim_stringlist *hdrs;
+    uschar * b = string_copy(sig->headernames);
+    uschar * p = b;
+    uschar * q;
+    pdkim_stringlist * hdrs;
 
     if (!b) return PDKIM_ERR_OOM;
 
@@ -1733,41 +1622,25 @@ while (sig)
 
     while(1)
       {
-      if ((q = strchr(p, ':')))
+      if ((q = Ustrchr(p, ':')))
        *q = '\0';
 
       for (hdrs = ctx->headers; hdrs; hdrs = hdrs->next)
        if (  hdrs->tag == 0
-          && strncasecmp(hdrs->value, p, strlen(p)) == 0
-          && (hdrs->value)[strlen(p)] == ':'
+          && strncasecmp(hdrs->value, CS p, Ustrlen(p)) == 0
+          && (hdrs->value)[Ustrlen(p)] == ':'
           )
          {
-         char *rh;
-
-         rh = sig->canon_headers == PDKIM_CANON_RELAXED
-           ? pdkim_relax_header(hdrs->value, 1) /* cook header for relaxed canon */
-           : strdup(hdrs->value);               /* just copy it for simple canon */
+         uschar * rh = sig->canon_headers == PDKIM_CANON_RELAXED
+           ? US pdkim_relax_header(hdrs->value, 1) /* cook header for relaxed canon */
+           : string_copy(hdrs->value);             /* just copy it for simple canon */
          if (!rh)
            return PDKIM_ERR_OOM;
 
          /* Feed header to the hash algorithm */
-#ifdef SHA_GNUTLS
-         gnutls_hash(sha_headers, rh, strlen(rh));
-#else
-         if (sig->algo == PDKIM_ALGO_RSA_SHA1)
-# ifdef SHA_OPENSSL
-           SHA1_Update  (&sha1_headers, rh, strlen(rh));
-         else
-           SHA256_Update(&sha2_headers, rh, strlen(rh));
-# elif defined(SHA_POLARSSL)
-           sha1_update(&sha1_headers, US rh, strlen(rh));
-         else
-           sha2_update(&sha2_headers, US rh, strlen(rh));
-# endif
-#endif
+         exim_sha_update(&hhash_ctx, rh, strlen(rh));
 
-         DEBUG(D_acl) pdkim_quoteprint(rh, strlen(rh), 1);
-         free(rh);
+         DEBUG(D_acl) pdkim_quoteprint(rh, Ustrlen(rh));
          hdrs->tag = 1;
          break;
          }
@@ -1775,7 +1648,6 @@ while (sig)
       if (!q) break;
       p = q+1;
       }
-    free(b);
     }
 
   DEBUG(D_acl) debug_printf(
@@ -1785,11 +1657,11 @@ while (sig)
   if (ctx->mode == PDKIM_MODE_SIGN)
     {
     /* Copy headernames to signature struct */
-    sig->headernames = strdup(headernames->str);
+    sig->headernames = string_copy(US headernames->str);
     pdkim_strfree(headernames);
 
     /* Create signature header with b= omitted */
-    sig_hdr = pdkim_create_header(ctx->sig, 0);
+    sig_hdr = pdkim_create_header(ctx->sig, FALSE);
     }
 
   /* VERIFICATION ----------------------------------------------------------- */
@@ -1815,172 +1687,71 @@ while (sig)
     {
     debug_printf(
            "PDKIM >> Signed DKIM-Signature header, canonicalized >>>>>>>>>>>>>>>>>\n");
-    pdkim_quoteprint(sig_hdr, strlen(sig_hdr), 1);
+    pdkim_quoteprint(sig_hdr, strlen(sig_hdr));
     debug_printf(
            "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
     }
 
   /* Finalize header hash */
-#ifdef SHA_GNUTLS
-  gnutls_hash(sha_headers, sig_hdr, strlen(sig_hdr));
-  gnutls_hash_output(sha_headers, headerhash);
-#else
-  if (sig->algo == PDKIM_ALGO_RSA_SHA1)
-# ifdef SHA_OPENSSL
-    {
-    SHA1_Update(&sha1_headers, sig_hdr, strlen(sig_hdr));
-    SHA1_Final(US headerhash, &sha1_headers);
-    }
-  else
-    {
-    SHA256_Update(&sha2_headers, sig_hdr, strlen(sig_hdr));
-    SHA256_Final(US headerhash, &sha2_headers);
-    }
-# elif defined(SHA_POLARSSL)
-    {
-    sha1_update(&sha1_headers, US sig_hdr, strlen(sig_hdr));
-    sha1_finish(&sha1_headers, US headerhash);
-    }
-  else
-    {
-    sha2_update(&sha2_headers, US sig_hdr, strlen(sig_hdr));
-    sha2_finish(&sha2_headers, US headerhash);
-    }
-# endif
-#endif
+  exim_sha_update(&hhash_ctx, sig_hdr, strlen(sig_hdr));
+  exim_sha_finish(&hhash_ctx, &hhash);
 
   DEBUG(D_acl)
     {
     debug_printf("PDKIM [%s] hh computed: ", sig->domain);
-    pdkim_hexprint(headerhash, sig->algo == PDKIM_ALGO_RSA_SHA1 ? 20:32, 1);
+    pdkim_hexprint(hhash.data, hhash.len);
     }
 
-#ifdef RSA_GNUTLS
+  /* Remember headers block for signing (when the library cannot do incremental)  */
   if (ctx->mode == PDKIM_MODE_SIGN)
-    hdata = string_append(hdata, &hdata_alloc, &hdata_size, 1, sig_hdr);
-#endif
+    (void) exim_rsa_data_append(&hdata, &hdata_alloc, sig_hdr);
 
   free(sig_hdr);
 
   /* SIGNING ---------------------------------------------------------------- */
   if (ctx->mode == PDKIM_MODE_SIGN)
     {
-#ifdef RSA_OPENSSL
-    RSA * rsa;
-    uschar * p, * q;
-    int len;
-#elif defined(RSA_GNUTLS)
-    gnutls_x509_privkey_t rsa;
-    gnutls_datum_t k;
-    int rc;
-    size_t sigsize;
-#endif
+    es_ctx sctx;
+    const uschar * errstr;
 
     /* Import private key */
-#ifdef RSA_OPENSSL
-
-    if (  !(p = Ustrstr(sig->rsa_privkey, "-----BEGIN RSA PRIVATE KEY-----"))
-       || !(q = Ustrstr(p+=31, "-----END RSA PRIVATE KEY-----"))
-       )
-      return PDKIM_SIGN_PRIVKEY_WRAP;
-    *q = '\0';
-    if ((len = b64decode(p, &p)) < 0)
-      {
-      DEBUG(D_acl) debug_printf("b64decode failed\n");
-      return PDKIM_SIGN_PRIVKEY_B64D;
-      }
-    if (!(rsa = d2i_RSAPrivateKey(NULL, CUSS &p, len)))
-      {
-      DEBUG(D_acl)
-       {
-       char ssl_errstring[256];
-       ERR_error_string(ERR_get_error(), ssl_errstring);
-       debug_printf("d2i_RSAPrivateKey: %s\n", ssl_errstring);
-       }
-      return PDKIM_ERR_RSA_PRIVKEY;
-      }
-
-#elif defined(RSA_GNUTLS)
-
-    k.data = sig->rsa_privkey;
-    k.size = strlen(sig->rsa_privkey);
-    if (  (rc = gnutls_x509_privkey_init(&rsa)) != GNUTLS_E_SUCCESS
-       || (rc = gnutls_x509_privkey_import2(rsa, &k,
-             GNUTLS_X509_FMT_PEM, NULL, GNUTLS_PKCS_PLAIN)) != GNUTLS_E_SUCCESS
-       )
+    if ((errstr = exim_rsa_signing_init(sig->rsa_privkey, &sctx)))
       {
-      DEBUG(D_acl) debug_printf("gnutls_x509_privkey_import2: %s\n",
-       gnutls_strerror(rc));
+      DEBUG(D_acl) debug_printf("signing_init: %s\n", errstr);
       return PDKIM_ERR_RSA_PRIVKEY;
       }
 
-#endif
-
+    /* 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. */
 
-    /* Allocate mem for signature */
-#ifdef RSA_OPENSSL
-    sig->sigdata = store_get(RSA_size(rsa));
-#elif defined(RSA_GNUTLS)
-    k.data = hdata;
-    k.size = hdata_size;
-    (void) gnutls_x509_privkey_sign_data(rsa,
-      sig->algo == PDKIM_ALGO_RSA_SHA1 ? GNUTLS_DIG_SHA1 : GNUTLS_DIG_SHA256,
-      0, &k, NULL, &sigsize);
-    sig->sigdata = store_get(sig->sigdata_len = sigsize);
+#if defined(RSA_OPENSSL) || defined(RSA_GCRYPT)
+    hdata = hhash;
 #endif
 
-    /* Do signing */
-#ifdef RSA_OPENSSL
-
-    if (RSA_sign(sig->algo == PDKIM_ALGO_RSA_SHA1 ? NID_sha1 : NID_sha256,
-         CUS headerhash, sig->algo == PDKIM_ALGO_RSA_SHA1 ? 20 : 32,
-         US sig->sigdata, (unsigned int *)&sig->sigdata_len,
-         rsa) != 1)
-      return PDKIM_ERR_RSA_SIGNING;
-    RSA_free(rsa);
-
-#elif defined(RSA_GNUTLS)
-
-    if ((rc = gnutls_x509_privkey_sign_data(rsa,
-         sig->algo == PDKIM_ALGO_RSA_SHA1 ? GNUTLS_DIG_SHA1 : GNUTLS_DIG_SHA256,
-         0, &k, sig->sigdata, &sigsize)) != GNUTLS_E_SUCCESS
-       )
+    if ((errstr = exim_rsa_sign(&sctx, is_sha1, &hdata, &sig->sigdata)))
       {
-      DEBUG(D_acl) debug_printf("gnutls_x509_privkey_sign_data: %s\n",
-       gnutls_strerror(rc));
+      DEBUG(D_acl) debug_printf("signing: %s\n", errstr);
       return PDKIM_ERR_RSA_SIGNING;
       }
-    gnutls_x509_privkey_deinit(rsa);
-
-#endif
-
 
     DEBUG(D_acl)
       {
       debug_printf( "PDKIM [%s] b computed: ", sig->domain);
-      pdkim_hexprint(sig->sigdata, sig->sigdata_len, 1);
+      pdkim_hexprint(sig->sigdata.data, sig->sigdata.len);
       }
 
-    if (!(sig->signature_header = pdkim_create_header(ctx->sig, 1)))
+    if (!(sig->signature_header = pdkim_create_header(ctx->sig, TRUE)))
       return PDKIM_ERR_OOM;
     }
 
   /* VERIFICATION ----------------------------------------------------------- */
   else
     {
-#ifdef RSA_OPENSSL
-    RSA * rsa;
-    const unsigned char * p;
-#elif defined(RSA_GNUTLS)
-    gnutls_pubkey_t rsa;
-    gnutls_datum_t k, s;
-    int rc;
-#endif
-    char *dns_txt_name, *dns_txt_reply;
+    ev_ctx vctx;
+    const uschar * errstr;
 
-#ifdef RSA_GNUTLS
-    gnutls_pubkey_init(&rsa);
-#endif
+    char *dns_txt_name, *dns_txt_reply;
 
     /* Fetch public key for signing domain, from DNS */
 
@@ -2016,9 +1787,9 @@ while (sig)
     DEBUG(D_acl)
       {
       debug_printf(
-             "PDKIM >> Parsing public key record >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"
-             " Raw record: ");
-      pdkim_quoteprint(dns_txt_reply, strlen(dns_txt_reply), 1);
+          "PDKIM >> Parsing public key record >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"
+          " Raw record: ");
+      pdkim_quoteprint(dns_txt_reply, strlen(dns_txt_reply));
       }
 
     if (!(sig->pubkey = pdkim_parse_pubkey_record(ctx, dns_txt_reply)))
@@ -2033,69 +1804,27 @@ while (sig)
       }
 
     DEBUG(D_acl) debug_printf(
-       "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
+         "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
 
     /* Import public key */
-#ifdef RSA_OPENSSL
-
-    p = CUS sig->pubkey->key;
-    if (!(rsa = d2i_RSA_PUBKEY(NULL, &p, (long) sig->pubkey->key_len)))
-
-#elif defined(RSA_GNUTLS)
-
-    k.data = sig->pubkey->key;
-    k.size = sig->pubkey->key_len;
-    if ((rc = gnutls_pubkey_import(rsa, &k, GNUTLS_X509_FMT_DER))
-       != GNUTLS_E_SUCCESS)
-
-#endif
+    if ((errstr = exim_rsa_verify_init(&sig->pubkey->key, &vctx)))
       {
-      DEBUG(D_acl)
-       {
-#ifdef RSA_OPENSSL
-       long e;
-       ERR_load_crypto_strings();      /*XXX move to a startup routine */
-       while ((e = ERR_get_error()))
-         debug_printf("Az: %.120s\n", ERR_error_string(e, NULL));
-#elif defined(RSA_GNUTLS)
-       debug_printf("gnutls_pubkey_import: %s\n", gnutls_strerror(rc));
-#endif
-       }
-
+      DEBUG(D_acl) debug_printf("verify_init: %s\n", errstr);
       sig->verify_status =      PDKIM_VERIFY_INVALID;
       sig->verify_ext_status =  PDKIM_VERIFY_INVALID_PUBKEY_IMPORT;
       goto NEXT_VERIFY;
       }
 
     /* Check the signature */
-#ifdef RSA_OPENSSL
-
-    if (RSA_verify(sig->algo == PDKIM_ALGO_RSA_SHA1 ? NID_sha1 : NID_sha256,
-         CUS headerhash, sig->algo == PDKIM_ALGO_RSA_SHA1 ? 20 : 32,
-         US sig->sigdata, (unsigned int)sig->sigdata_len,
-         rsa) != 1)
-
-#elif defined(RSA_GNUTLS)
-
-    k.data = headerhash;
-    k.size = sig->algo == PDKIM_ALGO_RSA_SHA1 ? 20 : 32;
-    s.data = sig->sigdata;
-    s.size = sig->sigdata_len;
-    if ((rc = gnutls_pubkey_verify_hash2(rsa,
-               sig->algo == PDKIM_ALGO_RSA_SHA1
-                 ? GNUTLS_SIGN_RSA_SHA1 : GNUTLS_SIGN_RSA_SHA256,
-               0, &k, &s)) < 0)
-
-#endif
+    if ((errstr = exim_rsa_verify(&vctx, is_sha1, &hhash, &sig->sigdata)))
       {
-#if defined(RSA_GNUTLS)
-      debug_printf("gnutls_pubkey_verify_hash2: %s\n", gnutls_strerror(rc));
-#endif
+      DEBUG(D_acl) debug_printf("headers verify: %s\n", errstr);
       sig->verify_status =      PDKIM_VERIFY_FAIL;
       sig->verify_ext_status =  PDKIM_VERIFY_FAIL_MESSAGE;
       goto NEXT_VERIFY;
       }
 
+
     /* We have a winner! (if bodydhash was correct earlier) */
     if (sig->verify_status == PDKIM_VERIFY_NONE)
       sig->verify_status = PDKIM_VERIFY_PASS;
@@ -2113,9 +1842,6 @@ NEXT_VERIFY:
        debug_printf("\n");
       }
 
-#ifdef RSA_GNUTLS
-    gnutls_pubkey_deinit(rsa);
-#endif
     free(dns_txt_name);
     free(dns_txt_reply);
     }
@@ -2189,33 +1915,15 @@ sig->bodylength = -1;
 ctx->mode = PDKIM_MODE_SIGN;
 ctx->sig = sig;
 
-ctx->sig->domain = strdup(domain);
-ctx->sig->selector = strdup(selector);
-ctx->sig->rsa_privkey = strdup(rsa_privkey);
-ctx->sig->algo = algo;
-
-if (!ctx->sig->domain || !ctx->sig->selector || !ctx->sig->rsa_privkey)
-  goto BAIL;
-
-#ifdef SHA_OPENSSL
-SHA1_Init  (&ctx->sig->sha1_body);
-SHA256_Init(&ctx->sig->sha2_body);
-
-#elif defined(SHA_GNUTLS)
-gnutls_hash_init(&ctx->sig->sha_body,
-    algo == PDKIM_ALGO_RSA_SHA1 ? GNUTLS_DIG_SHA1 : GNUTLS_DIG_SHA256);
-
-#elif defined(SHA_POLARSSL)
-if (!(ctx->sig->sha1_body = malloc(sizeof(sha1_context))))
-  goto BAIL;
-sha1_starts(ctx->sig->sha1_body);
+sig->domain = strdup(domain);
+sig->selector = strdup(selector);
+sig->rsa_privkey = strdup(rsa_privkey);
+sig->algo = algo;
 
-if (!(ctx->sig->sha2_body = malloc(sizeof(sha2_context))))
+if (!sig->domain || !sig->selector || !sig->rsa_privkey)
   goto BAIL;
-sha2_starts(ctx->sig->sha2_body, 0);
-
-#endif
 
+exim_sha_init(&sig->body_hash, algo == PDKIM_ALGO_RSA_SHA1);
 return ctx;
 
 BAIL:
@@ -2254,4 +1962,12 @@ return PDKIM_OK;
 }
 
 
+void
+pdkim_init(void)
+{
+exim_rsa_init();
+}
+
+
+
 #endif /*DISABLE_DKIM*/
index 313afd5..4de0a7a 100644 (file)
  *  with this program; if not, write to the Free Software Foundation, Inc.,
  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
+#ifndef PDKIM_H
+#define PDKIM_H
+
+#include "blob.h"
+#include "hash.h"
 
 /* -------------------------------------------------------------------------- */
 /* Length of the preallocated buffer for the "answer" from the dns/txt
@@ -98,8 +103,7 @@ typedef struct pdkim_pubkey {
   char *srvtype;                  /* s=  */
   char *notes;                    /* n=  */
 
-  char *key;                      /* p=  */
-  int   key_len;
+  blob  key;                      /* p=  */
 
   int   testing;                  /* t=y */
   int   no_subdomaining;          /* t=s */
@@ -151,18 +155,16 @@ typedef struct pdkim_signature {
 
   /* (h=) Colon-separated list of header names that are included in the
      signature */
-  char *headernames;
+  uschar *headernames;
 
   /* (z=) */
   char *copiedheaders;
 
   /* (b=) Raw signature data, along with its length in bytes */
-  char *sigdata;
-  int   sigdata_len;
+  blob sigdata;
 
   /* (bh=) Raw body hash data, along with its length in bytes */
-  char *bodyhash;
-  int   bodyhash_len;
+  blob bodyhash;
 
   /* Folded DKIM-Signature: header. Singing only, NULL for verifying.
      Ready for insertion into the message. Note: Folded using CRLFTB,
@@ -228,15 +230,8 @@ typedef struct pdkim_signature {
   /* Properties below this point are used internally only ------------- */
 
   /* Per-signature helper variables ----------------------------------- */
-#ifdef SHA_OPENSSL
-  SHA_CTX      sha1_body;  /* SHA1 block                                */
-  SHA256_CTX   sha2_body;  /* SHA256 block                              */
-#elif defined(SHA_GNUTLS)
-  gnutls_hash_hd_t sha_body; /* Either SHA1 or SHA256 block             */
-#elif defined(SHA_POLARSSL)
-  sha1_context *sha1_body; /* SHA1 block                                */
-  sha2_context *sha2_body; /* SHA256 block                              */
-#endif
+  hctx         body_hash;
+
   unsigned long signed_body_bytes; /* How many body bytes we hashed     */
   pdkim_stringlist *headers; /* Raw headers included in the sig         */
   /* Signing specific ------------------------------------------------- */
@@ -283,6 +278,8 @@ typedef struct pdkim_ctx {
 extern "C" {
 #endif
 
+void      pdkim_init         (void);
+
 DLLEXPORT
 pdkim_ctx *pdkim_init_sign    (char *, char *, char *, int);
 
@@ -306,3 +303,5 @@ void       pdkim_free_ctx     (pdkim_ctx *);
 #ifdef __cplusplus
 }
 #endif
+
+#endif
diff --git a/src/src/pdkim/rsa.c b/src/src/pdkim/rsa.c
new file mode 100644 (file)
index 0000000..c5d4c2e
--- /dev/null
@@ -0,0 +1,679 @@
+/*
+ *  PDKIM - a RFC4871 (DKIM) implementation
+ *
+ *  Copyright (C) 2016  Exim maintainers
+ *
+ *  RSA signing/verification interface
+ */
+
+#include "../exim.h"
+
+#ifndef DISABLE_DKIM   /* entire file */
+
+#ifndef SUPPORT_TLS
+# error Need SUPPORT_TLS for DKIM
+#endif
+
+#include "crypt_ver.h"
+#include "rsa.h"
+
+
+/******************************************************************************/
+#ifdef RSA_GNUTLS
+
+void
+exim_rsa_init(void)
+{
+}
+
+
+/* accumulate data (gnutls-only).  String to be appended must be nul-terminated. */
+blob *
+exim_rsa_data_append(blob * b, int * alloc, uschar * s)
+{
+int len = b->len;
+b->data = string_append(b->data, alloc, &len, 1, s);
+b->len = len;
+return b;
+}
+
+
+
+/* import private key from PEM string in memory.
+Return: NULL for success, or an error string */
+
+const uschar *
+exim_rsa_signing_init(uschar * privkey_pem, es_ctx * sign_ctx)
+{
+gnutls_datum_t k;
+int rc;
+
+k.data = privkey_pem;
+k.size = strlen(privkey_pem);
+
+if (  (rc = gnutls_x509_privkey_init(&sign_ctx->rsa)) != GNUTLS_E_SUCCESS
+   /*|| (rc = gnutls_x509_privkey_import(sign_ctx->rsa, &k,
+         GNUTLS_X509_FMT_PEM)) != GNUTLS_E_SUCCESS */
+   )
+  return gnutls_strerror(rc);
+
+if (  /* (rc = gnutls_x509_privkey_init(&sign_ctx->rsa)) != GNUTLS_E_SUCCESS
+   ||*/ (rc = gnutls_x509_privkey_import(sign_ctx->rsa, &k,
+         GNUTLS_X509_FMT_PEM)) != GNUTLS_E_SUCCESS
+   )
+  return gnutls_strerror(rc);
+
+return NULL;
+}
+
+
+
+/* allocate mem for signature (when signing) */
+/* sign data (gnutls_only)
+OR
+sign hash.
+
+Return: NULL for success, or an error string */
+
+const uschar *
+exim_rsa_sign(es_ctx * sign_ctx, BOOL is_sha1, blob * data, blob * sig)
+{
+gnutls_datum_t k;
+size_t sigsize = 0;
+int rc;
+const uschar * ret = NULL;
+
+/* Allocate mem for signature */
+k.data = data->data;
+k.size = data->len;
+(void) gnutls_x509_privkey_sign_data(sign_ctx->rsa,
+  is_sha1 ? GNUTLS_DIG_SHA1 : GNUTLS_DIG_SHA256,
+  0, &k, NULL, &sigsize);
+
+sig->data = store_get(sigsize);
+sig->len = sigsize;
+
+/* Do signing */
+if ((rc = gnutls_x509_privkey_sign_data(sign_ctx->rsa,
+           is_sha1 ? GNUTLS_DIG_SHA1 : GNUTLS_DIG_SHA256,
+           0, &k, sig->data, &sigsize)) != GNUTLS_E_SUCCESS
+   )
+  ret = gnutls_strerror(rc);
+
+gnutls_x509_privkey_deinit(sign_ctx->rsa);
+return ret;
+}
+
+
+
+/* import public key (from DER in memory)
+Return: NULL for success, or an error string */
+
+const uschar *
+exim_rsa_verify_init(blob * pubkey_der, ev_ctx * verify_ctx)
+{
+gnutls_datum_t k;
+int rc;
+const uschar * ret = NULL;
+
+gnutls_pubkey_init(&verify_ctx->rsa);
+
+k.data = pubkey_der->data;
+k.size = pubkey_der->len;
+
+if ((rc = gnutls_pubkey_import(verify_ctx->rsa, &k, GNUTLS_X509_FMT_DER))
+       != GNUTLS_E_SUCCESS)
+  ret = gnutls_strerror(rc);
+return ret;
+}
+
+
+/* verify signature (of hash)  (given pubkey & alleged sig)
+Return: NULL for success, or an error string */
+
+const uschar *
+exim_rsa_verify(ev_ctx * verify_ctx, BOOL is_sha1, blob * data_hash, blob * sig)
+{
+gnutls_datum_t k, s;
+int rc;
+const uschar * ret = NULL;
+
+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->rsa,
+           is_sha1 ? GNUTLS_SIGN_RSA_SHA1 : GNUTLS_SIGN_RSA_SHA256,
+           0, &k, &s)) < 0)
+  ret = gnutls_strerror(rc);
+
+gnutls_pubkey_deinit(verify_ctx->rsa);
+return ret;
+}
+
+
+
+
+#elif defined(RSA_GCRYPT)
+/******************************************************************************/
+
+
+/* Internal service routine:
+Read and move past an asn.1 header, checking class & tag,
+optionally returning the data-length */
+
+static int
+as_tag(blob * der, uschar req_cls, long req_tag, long * alen)
+{
+int rc;
+uschar tag_class;
+int taglen;
+long tag, len;
+
+/* debug_printf("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)
+  return rc;
+
+if (tag_class != req_cls || tag != req_tag) return ASN1_ELEMENT_NOT_FOUND;
+
+if ((len = asn1_get_length_der(der->data, der->len, &taglen)) < 0)
+  return ASN1_DER_ERROR;
+if (alen) *alen = len;
+
+/* debug_printf("as_tag:  tlen %d dlen %d\n", taglen, (int)len); */
+
+der->data += taglen;
+der->len -= taglen;
+return rc;
+}
+
+/* Internal service routine:
+Read and move over an asn.1 integer, setting an MPI to the value
+*/
+
+static uschar *
+as_mpi(blob * der, gcry_mpi_t * mpi)
+{
+long alen;
+int rc;
+gcry_error_t gerr;
+
+/* integer; move past the header */
+if ((rc = as_tag(der, 0, ASN1_TAG_INTEGER, &alen)) != ASN1_SUCCESS)
+  return US asn1_strerror(rc);
+
+/* read to an MPI */
+if ((gerr = gcry_mpi_scan(mpi, GCRYMPI_FMT_STD, der->data, alen, NULL)))
+  return US gcry_strerror(gerr);
+
+/* move over the data */
+der->data += alen; der->len -= alen;
+return NULL;
+}
+
+
+
+void
+exim_rsa_init(void)
+{
+/* Version check should be the very first call because it
+makes sure that important subsystems are initialized. */
+if (!gcry_check_version (GCRYPT_VERSION))
+  {
+  fputs ("libgcrypt version mismatch\n", stderr);
+  exit (2);
+  }
+
+/* We don't want to see any warnings, e.g. because we have not yet
+parsed program options which might be used to suppress such
+warnings. */
+gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN);
+
+/* ... If required, other initialization goes here.  Note that the
+process might still be running with increased privileges and that
+the secure memory has not been initialized.  */
+
+/* Allocate a pool of 16k secure memory.  This make the secure memory
+available and also drops privileges where needed.  */
+gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0);
+
+/* It is now okay to let Libgcrypt complain when there was/is
+a problem with the secure memory. */
+gcry_control (GCRYCTL_RESUME_SECMEM_WARN);
+
+/* ... If required, other initialization goes here.  */
+
+/* Tell Libgcrypt that initialization has completed. */
+gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
+
+return;
+}
+
+
+
+
+/* Accumulate data (gnutls-only).
+String to be appended must be nul-terminated. */
+
+blob *
+exim_rsa_data_append(blob * b, int * alloc, uschar * s)
+{
+return b;      /*dummy*/
+}
+
+
+
+/* import private key from PEM string in memory.
+Return: NULL for success, or an error string */
+
+const uschar *
+exim_rsa_signing_init(uschar * privkey_pem, es_ctx * sign_ctx)
+{
+uschar * s1, * s2;
+blob der;
+long alen;
+int rc;
+
+/*
+ *  RSAPrivateKey ::= SEQUENCE
+ *      version           Version,
+ *      modulus           INTEGER,  -- n
+ *      publicExponent    INTEGER,  -- e
+ *      privateExponent   INTEGER,  -- d
+ *      prime1            INTEGER,  -- p
+ *      prime2            INTEGER,  -- q
+ *      exponent1         INTEGER,  -- d mod (p-1)
+ *      exponent2         INTEGER,  -- d mod (q-1)
+ *      coefficient       INTEGER,  -- (inverse of q) mod p
+ *      otherPrimeInfos   OtherPrimeInfos OPTIONAL
+ */
+if (  !(s1 = Ustrstr(CS privkey_pem, "-----BEGIN RSA PRIVATE KEY-----"))
+   || !(s2 = Ustrstr(CS (s1+=31),    "-----END RSA PRIVATE KEY-----" ))
+   )
+  return US"Bad PEM wrapper";
+
+*s2 = '\0';
+
+if ((der.len = b64decode(s1, &der.data)) < 0)
+  return US"Bad PEM-DER b64 decode";
+
+/* untangle asn.1 */
+
+/* sequence; just move past the header */
+if ((rc = as_tag(&der, ASN1_CLASS_STRUCTURED, ASN1_TAG_SEQUENCE, NULL))
+   != ASN1_SUCCESS) goto asn_err;
+
+/* integer version; move past the header, check is zero */
+if ((rc = as_tag(&der, 0, ASN1_TAG_INTEGER, &alen)) != ASN1_SUCCESS)
+  goto asn_err;
+if (alen != 1 || *der.data != 0)
+  return US"Bad version number";
+der.data++; der.len--;
+
+if (  (s1 = as_mpi(&der, &sign_ctx->n))
+   || (s1 = as_mpi(&der, &sign_ctx->e))
+   || (s1 = as_mpi(&der, &sign_ctx->d))
+   || (s1 = as_mpi(&der, &sign_ctx->p))
+   || (s1 = as_mpi(&der, &sign_ctx->q))
+   || (s1 = as_mpi(&der, &sign_ctx->dp))
+   || (s1 = as_mpi(&der, &sign_ctx->dq))
+   || (s1 = as_mpi(&der, &sign_ctx->qp))
+   )
+  return s1;
+
+DEBUG(D_acl) debug_printf("rsa_signing_init:\n");
+  {
+  uschar * s;
+  gcry_mpi_aprint (GCRYMPI_FMT_HEX, &s, NULL, sign_ctx->n);
+  debug_printf(" N : %s\n", s);
+  gcry_mpi_aprint (GCRYMPI_FMT_HEX, &s, NULL, sign_ctx->e);
+  debug_printf(" E : %s\n", s);
+  gcry_mpi_aprint (GCRYMPI_FMT_HEX, &s, NULL, sign_ctx->d);
+  debug_printf(" D : %s\n", s);
+  gcry_mpi_aprint (GCRYMPI_FMT_HEX, &s, NULL, sign_ctx->p);
+  debug_printf(" P : %s\n", s);
+  gcry_mpi_aprint (GCRYMPI_FMT_HEX, &s, NULL, sign_ctx->q);
+  debug_printf(" Q : %s\n", s);
+  gcry_mpi_aprint (GCRYMPI_FMT_HEX, &s, NULL, sign_ctx->dp);
+  debug_printf(" DP: %s\n", s);
+  gcry_mpi_aprint (GCRYMPI_FMT_HEX, &s, NULL, sign_ctx->dq);
+  debug_printf(" DQ: %s\n", s);
+  gcry_mpi_aprint (GCRYMPI_FMT_HEX, &s, NULL, sign_ctx->qp);
+  debug_printf(" QP: %s\n", s);
+  }
+return NULL;
+
+asn_err: return US asn1_strerror(rc);
+}
+
+
+
+/* allocate mem for signature (when signing) */
+/* sign data (gnutls_only)
+OR
+sign hash.
+
+Return: NULL for success, or an error string */
+
+const uschar *
+exim_rsa_sign(es_ctx * sign_ctx, BOOL is_sha1, blob * data, blob * sig)
+{
+gcry_sexp_t s_hash = NULL, s_key = NULL, s_sig = NULL;
+gcry_mpi_t m_sig;
+uschar * errstr;
+gcry_error_t gerr;
+
+#define SIGSPACE 128
+sig->data = store_get(SIGSPACE);
+
+if (gcry_mpi_cmp (sign_ctx->p, sign_ctx->q) > 0)
+  {
+  gcry_mpi_swap (sign_ctx->p, sign_ctx->q);
+  gcry_mpi_invm (sign_ctx->qp, sign_ctx->p, sign_ctx->q);
+  }
+
+if (  (gerr = gcry_sexp_build (&s_key, NULL,
+               "(private-key (rsa (n%m)(e%m)(d%m)(p%m)(q%m)(u%m)))",
+               sign_ctx->n, sign_ctx->e,
+               sign_ctx->d, sign_ctx->p,
+               sign_ctx->q, sign_ctx->qp))
+   || (gerr = gcry_sexp_build (&s_hash, NULL,
+               is_sha1
+               ? "(data(flags pkcs1)(hash sha1 %b))"
+               : "(data(flags pkcs1)(hash sha256 %b))",
+               (int) data->len, CS data->data))
+   ||  (gerr = gcry_pk_sign (&s_sig, s_hash, s_key))
+   )
+  return US gcry_strerror(gerr);
+
+/* gcry_sexp_dump(s_sig); */
+
+if (  !(s_sig = gcry_sexp_find_token(s_sig, "s", 0))
+   )
+  return US"no sig result";
+
+m_sig = gcry_sexp_nth_mpi(s_sig, 1, GCRYMPI_FMT_USG);
+
+DEBUG(D_acl)
+  {
+  uschar * s;
+  gcry_mpi_aprint (GCRYMPI_FMT_HEX, &s, NULL, m_sig);
+  debug_printf(" SG: %s\n", s);
+  }
+
+gerr = gcry_mpi_print(GCRYMPI_FMT_USG, sig->data, SIGSPACE, &sig->len, m_sig);
+if (gerr)
+  {
+  debug_printf("signature conversion from MPI to buffer failed\n");
+  return US gcry_strerror(gerr);
+  }
+#undef SIGSPACE
+
+return NULL;
+}
+
+
+/* import public key (from DER in memory)
+Return: NULL for success, or an error string */
+
+const uschar *
+exim_rsa_verify_init(blob * pubkey_der, ev_ctx * verify_ctx)
+{
+/*
+in code sequence per b81207d2bfa92 rsa_parse_public_key() and asn1_get_mpi()
+*/
+uschar tag_class;
+int taglen;
+long alen;
+int rc;
+uschar * errstr;
+gcry_error_t gerr;
+uschar * stage = US"S1";
+
+/*
+sequence
+ sequence
+  OBJECT:rsaEncryption
+  NULL
+ BIT STRING:RSAPublicKey
+  sequence
+   INTEGER:Public modulus
+   INTEGER:Public exponent
+
+openssl rsa -in aux-fixed/dkim/dkim.private -pubout -outform DER | od -t x1 | head;
+openssl rsa -in aux-fixed/dkim/dkim.private -pubout | openssl asn1parse -dump;
+openssl rsa -in aux-fixed/dkim/dkim.private -pubout | openssl asn1parse -dump -offset 22;
+*/
+
+/* sequence; just move past the header */
+if ((rc = as_tag(pubkey_der, 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))
+   != ASN1_SUCCESS) goto asn_err;
+pubkey_der->data += alen; pubkey_der->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)
+  goto asn_err;
+pubkey_der->len = alen;
+pubkey_der->data++; pubkey_der->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))
+   != 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))
+   )
+  return errstr;
+
+DEBUG(D_acl) debug_printf("rsa_verify_init:\n");
+       {
+       uschar * s;
+       gcry_mpi_aprint (GCRYMPI_FMT_HEX, &s, NULL, verify_ctx->n);
+       debug_printf(" N : %s\n", s);
+       gcry_mpi_aprint (GCRYMPI_FMT_HEX, &s, NULL, verify_ctx->e);
+       debug_printf(" E : %s\n", s);
+       }
+
+return NULL;
+
+asn_err:
+DEBUG(D_acl) return string_sprintf("%s: %s", stage, asn1_strerror(rc));
+            return US asn1_strerror(rc);
+}
+
+
+/* verify signature (of hash)  (given pubkey & alleged sig)
+Return: NULL for success, or an error string */
+
+const uschar *
+exim_rsa_verify(ev_ctx * verify_ctx, BOOL is_sha1, blob * data_hash, blob * sig)
+{
+/*
+cf. libgnutls 2.8.5 _wrap_gcry_pk_verify()
+*/
+gcry_mpi_t m_sig;
+gcry_sexp_t s_sig = NULL, s_hash = NULL, s_pkey = NULL;
+gcry_error_t gerr;
+uschar * stage;
+
+if (  (stage = US"pkey sexp build",
+       gerr = gcry_sexp_build (&s_pkey, NULL, "(public-key(rsa(n%m)(e%m)))",
+                       verify_ctx->n, verify_ctx->e))
+   || (stage = US"data sexp build",
+       gerr = gcry_sexp_build (&s_hash, NULL,
+               is_sha1
+               ? "(data(flags pkcs1)(hash sha1 %b))"
+               : "(data(flags pkcs1)(hash sha256 %b))",
+               (int) data_hash->len, CS data_hash->data))
+   || (stage = US"sig mpi scan",
+       gerr = gcry_mpi_scan(&m_sig, GCRYMPI_FMT_USG, sig->data, sig->len, NULL))
+   || (stage = US"sig sexp build",
+       gerr = gcry_sexp_build (&s_sig, NULL, "(sig-val(rsa(s%m)))", m_sig))
+   || (stage = US"verify",
+       gerr = gcry_pk_verify (s_sig, s_hash, s_pkey))
+   )
+  {
+  DEBUG(D_acl) debug_printf("verify: error in stage '%s'\n", stage);
+  return US gcry_strerror(gerr);
+  }
+
+if (s_sig) gcry_sexp_release (s_sig);
+if (s_hash) gcry_sexp_release (s_hash);
+if (s_pkey) gcry_sexp_release (s_pkey);
+gcry_mpi_release (m_sig);
+gcry_mpi_release (verify_ctx->n);
+gcry_mpi_release (verify_ctx->e);
+
+return NULL;
+}
+
+
+
+
+#elif defined(RSA_OPENSSL)
+/******************************************************************************/
+
+void
+exim_rsa_init(void)
+{
+}
+
+
+/* accumulate data (gnutls-only) */
+blob *
+exim_rsa_data_append(blob * b, int * alloc, uschar * s)
+{
+return b;      /*dummy*/
+}
+
+
+/* import private key from PEM string in memory.
+Return: NULL for success, or an error string */
+
+const uschar *
+exim_rsa_signing_init(uschar * privkey_pem, es_ctx * sign_ctx)
+{
+uschar * p, * q;
+int len;
+
+/* Convert PEM to DER */
+if (  !(p = Ustrstr(privkey_pem, "-----BEGIN RSA PRIVATE KEY-----"))
+   || !(q = Ustrstr(p+=31,       "-----END RSA PRIVATE KEY-----"))
+   )
+  return US"Bad PEM wrapping";
+
+*q = '\0';
+if ((len = b64decode(p, &p)) < 0)
+  return US"b64decode failed";
+
+if (!(sign_ctx->rsa = d2i_RSAPrivateKey(NULL, CUSS &p, len)))
+  {
+  char ssl_errstring[256];
+  ERR_load_crypto_strings();   /*XXX move to a startup routine */
+  ERR_error_string(ERR_get_error(), ssl_errstring);
+  return string_copy(ssl_errstring);
+  }
+
+return NULL;
+}
+
+
+
+/* allocate mem for signature (when signing) */
+/* sign data (gnutls_only)
+OR
+sign hash.
+
+Return: NULL for success, or an error string */
+
+const uschar *
+exim_rsa_sign(es_ctx * sign_ctx, BOOL is_sha1, blob * data, blob * sig)
+{
+uint len;
+const uschar * ret = NULL;
+
+/* Allocate mem for signature */
+len = RSA_size(sign_ctx->rsa);
+sig->data = store_get(len);
+sig->len = len;
+
+/* Do signing */
+if (RSA_sign(is_sha1 ? NID_sha1 : NID_sha256,
+      CUS data->data, data->len,
+      US sig->data, &len, sign_ctx->rsa) != 1)
+  {
+  char ssl_errstring[256];
+  ERR_load_crypto_strings();   /*XXX move to a startup routine */
+  ERR_error_string(ERR_get_error(), ssl_errstring);
+  ret = string_copy(ssl_errstring);
+  }
+
+RSA_free(sign_ctx->rsa);
+return ret;;
+}
+
+
+
+/* import public key (from DER in memory)
+Return: nULL for success, or an error string */
+
+const uschar *
+exim_rsa_verify_init(blob * pubkey_der, ev_ctx * verify_ctx)
+{
+const uschar * p = CUS pubkey_der->data;
+const uschar * ret = NULL;
+
+if (!(verify_ctx->rsa = d2i_RSA_PUBKEY(NULL, &p, (long) pubkey_der->len)))
+  {
+  char ssl_errstring[256];
+  ERR_load_crypto_strings();   /*XXX move to a startup routine */
+  ERR_error_string(ERR_get_error(), ssl_errstring);
+  ret = string_copy(ssl_errstring);
+  }
+return ret;
+}
+
+
+
+
+/* verify signature (of hash)  (given pubkey & alleged sig)
+Return: NULL for success, or an error string */
+
+const uschar *
+exim_rsa_verify(ev_ctx * verify_ctx, BOOL is_sha1, blob * data_hash, blob * sig)
+{
+const uschar * ret = NULL;
+
+if (RSA_verify(is_sha1 ? NID_sha1 : NID_sha256,
+      CUS data_hash->data, data_hash->len,
+      US sig->data, (uint) sig->len, verify_ctx->rsa) != 1)
+  {
+  char ssl_errstring[256];
+  ERR_load_crypto_strings();   /*XXX move to a startup routine */
+  ERR_error_string(ERR_get_error(), ssl_errstring);
+  ret = string_copy(ssl_errstring);
+  }
+return ret;
+}
+
+
+#endif
+/******************************************************************************/
+
+#endif /*DISABLE_DKIM*/
+/* End of File */
diff --git a/src/src/pdkim/rsa.h b/src/src/pdkim/rsa.h
new file mode 100644 (file)
index 0000000..32631fd
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ *  PDKIM - a RFC4871 (DKIM) implementation
+ *
+ *  Copyright (C) 2016  Exim maintainers
+ *
+ *  RSA signing/verification interface
+ */
+
+#include "../exim.h"
+
+#ifndef DISABLE_DKIM   /* entire file */
+
+#include "crypt_ver.h"
+
+#ifdef RSA_OPENSSL
+# include <openssl/rsa.h>
+# include <openssl/ssl.h>
+# include <openssl/err.h>
+#elif defined(RSA_GNUTLS)
+# include <gnutls/gnutls.h>
+# include <gnutls/x509.h>
+#  include <gnutls/abstract.h>
+#elif defined(RSA_GCRYPT)
+#  include <gcrypt.h>
+#  include <libtasn1.h>
+#endif
+
+#include "blob.h"
+
+
+#ifdef RSA_OPENSSL
+
+typedef struct {
+  RSA * rsa;
+} es_ctx;
+
+typedef struct {
+  RSA * rsa;
+} ev_ctx;
+
+#elif defined(RSA_GNUTLS)
+
+typedef struct {
+  gnutls_x509_privkey_t rsa;
+} es_ctx;
+
+typedef struct {
+  gnutls_pubkey_t rsa;
+} ev_ctx;
+
+#elif defined(RSA_GCRYPT)
+
+typedef struct {
+  gcry_mpi_t n;
+  gcry_mpi_t e;
+  gcry_mpi_t d;
+  gcry_mpi_t p;
+  gcry_mpi_t q;
+  gcry_mpi_t dp;
+  gcry_mpi_t dq;
+  gcry_mpi_t qp;
+} es_ctx;
+
+typedef struct {
+  gcry_mpi_t n;
+  gcry_mpi_t e;
+} ev_ctx;
+
+#endif
+
+
+extern void exim_rsa_init(void);
+extern blob * exim_rsa_data_append(blob *, int *, uschar *);
+
+extern const uschar * exim_rsa_signing_init(uschar *, es_ctx *);
+extern const uschar * exim_rsa_sign(es_ctx *, BOOL, blob *, blob *);
+extern const uschar * exim_rsa_verify_init(blob *, ev_ctx *);
+extern const uschar * exim_rsa_verify(ev_ctx *, BOOL, blob *, blob *);
+
+#endif /*DISABLE_DKIM*/
+/* End of File */
diff --git a/src/src/pdkim/sha1.c b/src/src/pdkim/sha1.c
deleted file mode 100644 (file)
index 96f4e7b..0000000
+++ /dev/null
@@ -1,323 +0,0 @@
-#include "crypt_ver.h"
-
-#ifdef SHA_POLARSSL    /* remainder of file */
-
-/*
- *  FIPS-180-1 compliant SHA-1 implementation
- *
- *  Copyright (C) 2006-2010, Brainspark B.V.
- *
- *  This file is part of PolarSSL (http://www.polarssl.org)
- *  Lead Maintainer: Paul Bakker <polarssl_maintainer at polarssl.org>
- *
- *  All rights reserved.
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License along
- *  with this program; if not, write to the Free Software Foundation, Inc.,
- *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-/*
- *  The SHA-1 standard was published by NIST in 1993.
- *
- *  http://www.itl.nist.gov/fipspubs/fip180-1.htm
- */
-
-#include "polarssl/config.h"
-
-#if defined(POLARSSL_SHA1_C)
-
-#include "polarssl/sha1.h"
-
-#include <string.h>
-#include <stdio.h>
-
-/*
- * 32-bit integer manipulation macros (big endian)
- */
-#ifndef GET_ULONG_BE
-#define GET_ULONG_BE(n,b,i)                             \
-{                                                       \
-    (n) = ( (unsigned long) (b)[(i)    ] << 24 )        \
-        | ( (unsigned long) (b)[(i) + 1] << 16 )        \
-        | ( (unsigned long) (b)[(i) + 2] <<  8 )        \
-        | ( (unsigned long) (b)[(i) + 3]       );       \
-}
-#endif
-
-#ifndef PUT_ULONG_BE
-#define PUT_ULONG_BE(n,b,i)                             \
-{                                                       \
-    (b)[(i)    ] = (unsigned char) ( (n) >> 24 );       \
-    (b)[(i) + 1] = (unsigned char) ( (n) >> 16 );       \
-    (b)[(i) + 2] = (unsigned char) ( (n) >>  8 );       \
-    (b)[(i) + 3] = (unsigned char) ( (n)       );       \
-}
-#endif
-
-/*
- * SHA-1 context setup
- * Called from pdkim_parse_sig_header() pdkim_feed_finish() pdkim_init_sign()
- */
-void sha1_starts( sha1_context *ctx )
-{
-    ctx->total[0] = 0;
-    ctx->total[1] = 0;
-
-    ctx->state[0] = 0x67452301;
-    ctx->state[1] = 0xEFCDAB89;
-    ctx->state[2] = 0x98BADCFE;
-    ctx->state[3] = 0x10325476;
-    ctx->state[4] = 0xC3D2E1F0;
-}
-
-static void sha1_process( sha1_context *ctx, const unsigned char data[64] )
-{
-    unsigned long temp, W[16], A, B, C, D, E;
-
-    GET_ULONG_BE( W[ 0], data,  0 );
-    GET_ULONG_BE( W[ 1], data,  4 );
-    GET_ULONG_BE( W[ 2], data,  8 );
-    GET_ULONG_BE( W[ 3], data, 12 );
-    GET_ULONG_BE( W[ 4], data, 16 );
-    GET_ULONG_BE( W[ 5], data, 20 );
-    GET_ULONG_BE( W[ 6], data, 24 );
-    GET_ULONG_BE( W[ 7], data, 28 );
-    GET_ULONG_BE( W[ 8], data, 32 );
-    GET_ULONG_BE( W[ 9], data, 36 );
-    GET_ULONG_BE( W[10], data, 40 );
-    GET_ULONG_BE( W[11], data, 44 );
-    GET_ULONG_BE( W[12], data, 48 );
-    GET_ULONG_BE( W[13], data, 52 );
-    GET_ULONG_BE( W[14], data, 56 );
-    GET_ULONG_BE( W[15], data, 60 );
-
-#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n)))
-
-#define R(t)                                            \
-(                                                       \
-    temp = W[(t -  3) & 0x0F] ^ W[(t - 8) & 0x0F] ^     \
-           W[(t - 14) & 0x0F] ^ W[ t      & 0x0F],      \
-    ( W[t & 0x0F] = S(temp,1) )                         \
-)
-
-#define P(a,b,c,d,e,x)                                  \
-{                                                       \
-    e += S(a,5) + F(b,c,d) + K + x; b = S(b,30);        \
-}
-
-    A = ctx->state[0];
-    B = ctx->state[1];
-    C = ctx->state[2];
-    D = ctx->state[3];
-    E = ctx->state[4];
-
-#define F(x,y,z) (z ^ (x & (y ^ z)))
-#define K 0x5A827999
-
-    P( A, B, C, D, E, W[0]  );
-    P( E, A, B, C, D, W[1]  );
-    P( D, E, A, B, C, W[2]  );
-    P( C, D, E, A, B, W[3]  );
-    P( B, C, D, E, A, W[4]  );
-    P( A, B, C, D, E, W[5]  );
-    P( E, A, B, C, D, W[6]  );
-    P( D, E, A, B, C, W[7]  );
-    P( C, D, E, A, B, W[8]  );
-    P( B, C, D, E, A, W[9]  );
-    P( A, B, C, D, E, W[10] );
-    P( E, A, B, C, D, W[11] );
-    P( D, E, A, B, C, W[12] );
-    P( C, D, E, A, B, W[13] );
-    P( B, C, D, E, A, W[14] );
-    P( A, B, C, D, E, W[15] );
-    P( E, A, B, C, D, R(16) );
-    P( D, E, A, B, C, R(17) );
-    P( C, D, E, A, B, R(18) );
-    P( B, C, D, E, A, R(19) );
-
-#undef K
-#undef F
-
-#define F(x,y,z) (x ^ y ^ z)
-#define K 0x6ED9EBA1
-
-    P( A, B, C, D, E, R(20) );
-    P( E, A, B, C, D, R(21) );
-    P( D, E, A, B, C, R(22) );
-    P( C, D, E, A, B, R(23) );
-    P( B, C, D, E, A, R(24) );
-    P( A, B, C, D, E, R(25) );
-    P( E, A, B, C, D, R(26) );
-    P( D, E, A, B, C, R(27) );
-    P( C, D, E, A, B, R(28) );
-    P( B, C, D, E, A, R(29) );
-    P( A, B, C, D, E, R(30) );
-    P( E, A, B, C, D, R(31) );
-    P( D, E, A, B, C, R(32) );
-    P( C, D, E, A, B, R(33) );
-    P( B, C, D, E, A, R(34) );
-    P( A, B, C, D, E, R(35) );
-    P( E, A, B, C, D, R(36) );
-    P( D, E, A, B, C, R(37) );
-    P( C, D, E, A, B, R(38) );
-    P( B, C, D, E, A, R(39) );
-
-#undef K
-#undef F
-
-#define F(x,y,z) ((x & y) | (z & (x | y)))
-#define K 0x8F1BBCDC
-
-    P( A, B, C, D, E, R(40) );
-    P( E, A, B, C, D, R(41) );
-    P( D, E, A, B, C, R(42) );
-    P( C, D, E, A, B, R(43) );
-    P( B, C, D, E, A, R(44) );
-    P( A, B, C, D, E, R(45) );
-    P( E, A, B, C, D, R(46) );
-    P( D, E, A, B, C, R(47) );
-    P( C, D, E, A, B, R(48) );
-    P( B, C, D, E, A, R(49) );
-    P( A, B, C, D, E, R(50) );
-    P( E, A, B, C, D, R(51) );
-    P( D, E, A, B, C, R(52) );
-    P( C, D, E, A, B, R(53) );
-    P( B, C, D, E, A, R(54) );
-    P( A, B, C, D, E, R(55) );
-    P( E, A, B, C, D, R(56) );
-    P( D, E, A, B, C, R(57) );
-    P( C, D, E, A, B, R(58) );
-    P( B, C, D, E, A, R(59) );
-
-#undef K
-#undef F
-
-#define F(x,y,z) (x ^ y ^ z)
-#define K 0xCA62C1D6
-
-    P( A, B, C, D, E, R(60) );
-    P( E, A, B, C, D, R(61) );
-    P( D, E, A, B, C, R(62) );
-    P( C, D, E, A, B, R(63) );
-    P( B, C, D, E, A, R(64) );
-    P( A, B, C, D, E, R(65) );
-    P( E, A, B, C, D, R(66) );
-    P( D, E, A, B, C, R(67) );
-    P( C, D, E, A, B, R(68) );
-    P( B, C, D, E, A, R(69) );
-    P( A, B, C, D, E, R(70) );
-    P( E, A, B, C, D, R(71) );
-    P( D, E, A, B, C, R(72) );
-    P( C, D, E, A, B, R(73) );
-    P( B, C, D, E, A, R(74) );
-    P( A, B, C, D, E, R(75) );
-    P( E, A, B, C, D, R(76) );
-    P( D, E, A, B, C, R(77) );
-    P( C, D, E, A, B, R(78) );
-    P( B, C, D, E, A, R(79) );
-
-#undef K
-#undef F
-
-    ctx->state[0] += A;
-    ctx->state[1] += B;
-    ctx->state[2] += C;
-    ctx->state[3] += D;
-    ctx->state[4] += E;
-}
-
-/*
- * SHA-1 process buffer
- * Called from pdkim_feed_finish() & pdkim_finish_bodyhash()
- */
-void sha1_update( sha1_context *ctx, const unsigned char *input, int ilen )
-{
-    int fill;
-    unsigned long left;
-
-    if( ilen <= 0 )
-        return;
-
-    left = ctx->total[0] & 0x3F;
-    fill = 64 - left;
-
-    ctx->total[0] += ilen;
-    ctx->total[0] &= 0xFFFFFFFF;
-
-    if( ctx->total[0] < (unsigned long) ilen )
-        ctx->total[1]++;
-
-    if( left && ilen >= fill )
-    {
-        memcpy( (void *) (ctx->buffer + left),
-                (void *) input, fill );
-        sha1_process( ctx, ctx->buffer );
-        input += fill;
-        ilen  -= fill;
-        left = 0;
-    }
-
-    while( ilen >= 64 )
-    {
-        sha1_process( ctx, input );
-        input += 64;
-        ilen  -= 64;
-    }
-
-    if( ilen > 0 )
-    {
-        memcpy( (void *) (ctx->buffer + left),
-                (void *) input, ilen );
-    }
-}
-
-static const unsigned char sha1_padding[64] =
-{
- 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
-};
-
-/*
- * SHA-1 final digest
- * Called from pdkim_feed_finish() & pdkim_finish_bodyhash()
- */
-void sha1_finish( sha1_context *ctx, unsigned char output[20] )
-{
-    unsigned long last, padn;
-    unsigned long high, low;
-    unsigned char msglen[8];
-
-    high = ( ctx->total[0] >> 29 )
-         | ( ctx->total[1] <<  3 );
-    low  = ( ctx->total[0] <<  3 );
-
-    PUT_ULONG_BE( high, msglen, 0 );
-    PUT_ULONG_BE( low,  msglen, 4 );
-
-    last = ctx->total[0] & 0x3F;
-    padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last );
-
-    sha1_update( ctx, (unsigned char *) sha1_padding, padn );
-    sha1_update( ctx, msglen, 8 );
-
-    PUT_ULONG_BE( ctx->state[0], output,  0 );
-    PUT_ULONG_BE( ctx->state[1], output,  4 );
-    PUT_ULONG_BE( ctx->state[2], output,  8 );
-    PUT_ULONG_BE( ctx->state[3], output, 12 );
-    PUT_ULONG_BE( ctx->state[4], output, 16 );
-}
-
-#endif
-#endif
diff --git a/src/src/pdkim/sha2.c b/src/src/pdkim/sha2.c
deleted file mode 100644 (file)
index 6ab6cb9..0000000
+++ /dev/null
@@ -1,453 +0,0 @@
-#include "crypt_ver.h"
-
-#ifdef SHA_POLARSSL    /* remainder of file */
-
-/*
- *  FIPS-180-2 compliant SHA-256 implementation
- *
- *  Copyright (C) 2006-2010, Brainspark B.V.
- *
- *  This file is part of PolarSSL (http://www.polarssl.org)
- *  Lead Maintainer: Paul Bakker <polarssl_maintainer at polarssl.org>
- *
- *  All rights reserved.
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License along
- *  with this program; if not, write to the Free Software Foundation, Inc.,
- *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-/*
- *  The SHA-256 Secure Hash Standard was published by NIST in 2002.
- *
- *  http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf
- */
-
-#include "polarssl/config.h"
-
-#if defined(POLARSSL_SHA2_C)
-
-#include "polarssl/sha2.h"
-
-#include <string.h>
-#include <stdio.h>
-
-/*
- * 32-bit integer manipulation macros (big endian)
- */
-#ifndef GET_ULONG_BE
-#define GET_ULONG_BE(n,b,i)                             \
-{                                                       \
-    (n) = ( (unsigned long) (b)[(i)    ] << 24 )        \
-        | ( (unsigned long) (b)[(i) + 1] << 16 )        \
-        | ( (unsigned long) (b)[(i) + 2] <<  8 )        \
-        | ( (unsigned long) (b)[(i) + 3]       );       \
-}
-#endif
-
-#ifndef PUT_ULONG_BE
-#define PUT_ULONG_BE(n,b,i)                             \
-{                                                       \
-    (b)[(i)    ] = (unsigned char) ( (n) >> 24 );       \
-    (b)[(i) + 1] = (unsigned char) ( (n) >> 16 );       \
-    (b)[(i) + 2] = (unsigned char) ( (n) >>  8 );       \
-    (b)[(i) + 3] = (unsigned char) ( (n)       );       \
-}
-#endif
-
-/*
- * SHA-256 context setup
- */
-void sha2_starts( sha2_context *ctx, int is224 )
-{
-    ctx->total[0] = 0;
-    ctx->total[1] = 0;
-
-    if( is224 == 0 )
-    {
-        /* SHA-256 */
-        ctx->state[0] = 0x6A09E667;
-        ctx->state[1] = 0xBB67AE85;
-        ctx->state[2] = 0x3C6EF372;
-        ctx->state[3] = 0xA54FF53A;
-        ctx->state[4] = 0x510E527F;
-        ctx->state[5] = 0x9B05688C;
-        ctx->state[6] = 0x1F83D9AB;
-        ctx->state[7] = 0x5BE0CD19;
-    }
-    else
-    {
-        /* SHA-224 */
-        ctx->state[0] = 0xC1059ED8;
-        ctx->state[1] = 0x367CD507;
-        ctx->state[2] = 0x3070DD17;
-        ctx->state[3] = 0xF70E5939;
-        ctx->state[4] = 0xFFC00B31;
-        ctx->state[5] = 0x68581511;
-        ctx->state[6] = 0x64F98FA7;
-        ctx->state[7] = 0xBEFA4FA4;
-    }
-
-    ctx->is224 = is224;
-}
-
-static void sha2_process( sha2_context *ctx, const unsigned char data[64] )
-{
-    unsigned long temp1, temp2, W[64];
-    unsigned long A, B, C, D, E, F, G, H;
-
-    GET_ULONG_BE( W[ 0], data,  0 );
-    GET_ULONG_BE( W[ 1], data,  4 );
-    GET_ULONG_BE( W[ 2], data,  8 );
-    GET_ULONG_BE( W[ 3], data, 12 );
-    GET_ULONG_BE( W[ 4], data, 16 );
-    GET_ULONG_BE( W[ 5], data, 20 );
-    GET_ULONG_BE( W[ 6], data, 24 );
-    GET_ULONG_BE( W[ 7], data, 28 );
-    GET_ULONG_BE( W[ 8], data, 32 );
-    GET_ULONG_BE( W[ 9], data, 36 );
-    GET_ULONG_BE( W[10], data, 40 );
-    GET_ULONG_BE( W[11], data, 44 );
-    GET_ULONG_BE( W[12], data, 48 );
-    GET_ULONG_BE( W[13], data, 52 );
-    GET_ULONG_BE( W[14], data, 56 );
-    GET_ULONG_BE( W[15], data, 60 );
-
-#define  SHR(x,n) ((x & 0xFFFFFFFF) >> n)
-#define ROTR(x,n) (SHR(x,n) | (x << (32 - n)))
-
-#define S0(x) (ROTR(x, 7) ^ ROTR(x,18) ^  SHR(x, 3))
-#define S1(x) (ROTR(x,17) ^ ROTR(x,19) ^  SHR(x,10))
-
-#define S2(x) (ROTR(x, 2) ^ ROTR(x,13) ^ ROTR(x,22))
-#define S3(x) (ROTR(x, 6) ^ ROTR(x,11) ^ ROTR(x,25))
-
-#define F0(x,y,z) ((x & y) | (z & (x | y)))
-#define F1(x,y,z) (z ^ (x & (y ^ z)))
-
-#define R(t)                                    \
-(                                               \
-    W[t] = S1(W[t -  2]) + W[t -  7] +          \
-           S0(W[t - 15]) + W[t - 16]            \
-)
-
-#define P(a,b,c,d,e,f,g,h,x,K)                  \
-{                                               \
-    temp1 = h + S3(e) + F1(e,f,g) + K + x;      \
-    temp2 = S2(a) + F0(a,b,c);                  \
-    d += temp1; h = temp1 + temp2;              \
-}
-
-    A = ctx->state[0];
-    B = ctx->state[1];
-    C = ctx->state[2];
-    D = ctx->state[3];
-    E = ctx->state[4];
-    F = ctx->state[5];
-    G = ctx->state[6];
-    H = ctx->state[7];
-
-    P( A, B, C, D, E, F, G, H, W[ 0], 0x428A2F98 );
-    P( H, A, B, C, D, E, F, G, W[ 1], 0x71374491 );
-    P( G, H, A, B, C, D, E, F, W[ 2], 0xB5C0FBCF );
-    P( F, G, H, A, B, C, D, E, W[ 3], 0xE9B5DBA5 );
-    P( E, F, G, H, A, B, C, D, W[ 4], 0x3956C25B );
-    P( D, E, F, G, H, A, B, C, W[ 5], 0x59F111F1 );
-    P( C, D, E, F, G, H, A, B, W[ 6], 0x923F82A4 );
-    P( B, C, D, E, F, G, H, A, W[ 7], 0xAB1C5ED5 );
-    P( A, B, C, D, E, F, G, H, W[ 8], 0xD807AA98 );
-    P( H, A, B, C, D, E, F, G, W[ 9], 0x12835B01 );
-    P( G, H, A, B, C, D, E, F, W[10], 0x243185BE );
-    P( F, G, H, A, B, C, D, E, W[11], 0x550C7DC3 );
-    P( E, F, G, H, A, B, C, D, W[12], 0x72BE5D74 );
-    P( D, E, F, G, H, A, B, C, W[13], 0x80DEB1FE );
-    P( C, D, E, F, G, H, A, B, W[14], 0x9BDC06A7 );
-    P( B, C, D, E, F, G, H, A, W[15], 0xC19BF174 );
-    P( A, B, C, D, E, F, G, H, R(16), 0xE49B69C1 );
-    P( H, A, B, C, D, E, F, G, R(17), 0xEFBE4786 );
-    P( G, H, A, B, C, D, E, F, R(18), 0x0FC19DC6 );
-    P( F, G, H, A, B, C, D, E, R(19), 0x240CA1CC );
-    P( E, F, G, H, A, B, C, D, R(20), 0x2DE92C6F );
-    P( D, E, F, G, H, A, B, C, R(21), 0x4A7484AA );
-    P( C, D, E, F, G, H, A, B, R(22), 0x5CB0A9DC );
-    P( B, C, D, E, F, G, H, A, R(23), 0x76F988DA );
-    P( A, B, C, D, E, F, G, H, R(24), 0x983E5152 );
-    P( H, A, B, C, D, E, F, G, R(25), 0xA831C66D );
-    P( G, H, A, B, C, D, E, F, R(26), 0xB00327C8 );
-    P( F, G, H, A, B, C, D, E, R(27), 0xBF597FC7 );
-    P( E, F, G, H, A, B, C, D, R(28), 0xC6E00BF3 );
-    P( D, E, F, G, H, A, B, C, R(29), 0xD5A79147 );
-    P( C, D, E, F, G, H, A, B, R(30), 0x06CA6351 );
-    P( B, C, D, E, F, G, H, A, R(31), 0x14292967 );
-    P( A, B, C, D, E, F, G, H, R(32), 0x27B70A85 );
-    P( H, A, B, C, D, E, F, G, R(33), 0x2E1B2138 );
-    P( G, H, A, B, C, D, E, F, R(34), 0x4D2C6DFC );
-    P( F, G, H, A, B, C, D, E, R(35), 0x53380D13 );
-    P( E, F, G, H, A, B, C, D, R(36), 0x650A7354 );
-    P( D, E, F, G, H, A, B, C, R(37), 0x766A0ABB );
-    P( C, D, E, F, G, H, A, B, R(38), 0x81C2C92E );
-    P( B, C, D, E, F, G, H, A, R(39), 0x92722C85 );
-    P( A, B, C, D, E, F, G, H, R(40), 0xA2BFE8A1 );
-    P( H, A, B, C, D, E, F, G, R(41), 0xA81A664B );
-    P( G, H, A, B, C, D, E, F, R(42), 0xC24B8B70 );
-    P( F, G, H, A, B, C, D, E, R(43), 0xC76C51A3 );
-    P( E, F, G, H, A, B, C, D, R(44), 0xD192E819 );
-    P( D, E, F, G, H, A, B, C, R(45), 0xD6990624 );
-    P( C, D, E, F, G, H, A, B, R(46), 0xF40E3585 );
-    P( B, C, D, E, F, G, H, A, R(47), 0x106AA070 );
-    P( A, B, C, D, E, F, G, H, R(48), 0x19A4C116 );
-    P( H, A, B, C, D, E, F, G, R(49), 0x1E376C08 );
-    P( G, H, A, B, C, D, E, F, R(50), 0x2748774C );
-    P( F, G, H, A, B, C, D, E, R(51), 0x34B0BCB5 );
-    P( E, F, G, H, A, B, C, D, R(52), 0x391C0CB3 );
-    P( D, E, F, G, H, A, B, C, R(53), 0x4ED8AA4A );
-    P( C, D, E, F, G, H, A, B, R(54), 0x5B9CCA4F );
-    P( B, C, D, E, F, G, H, A, R(55), 0x682E6FF3 );
-    P( A, B, C, D, E, F, G, H, R(56), 0x748F82EE );
-    P( H, A, B, C, D, E, F, G, R(57), 0x78A5636F );
-    P( G, H, A, B, C, D, E, F, R(58), 0x84C87814 );
-    P( F, G, H, A, B, C, D, E, R(59), 0x8CC70208 );
-    P( E, F, G, H, A, B, C, D, R(60), 0x90BEFFFA );
-    P( D, E, F, G, H, A, B, C, R(61), 0xA4506CEB );
-    P( C, D, E, F, G, H, A, B, R(62), 0xBEF9A3F7 );
-    P( B, C, D, E, F, G, H, A, R(63), 0xC67178F2 );
-
-    ctx->state[0] += A;
-    ctx->state[1] += B;
-    ctx->state[2] += C;
-    ctx->state[3] += D;
-    ctx->state[4] += E;
-    ctx->state[5] += F;
-    ctx->state[6] += G;
-    ctx->state[7] += H;
-}
-
-/*
- * SHA-256 process buffer
- */
-void sha2_update( sha2_context *ctx, const unsigned char *input, int ilen )
-{
-    int fill;
-    unsigned long left;
-
-    if( ilen <= 0 )
-        return;
-
-    left = ctx->total[0] & 0x3F;
-    fill = 64 - left;
-
-    ctx->total[0] += ilen;
-    ctx->total[0] &= 0xFFFFFFFF;
-
-    if( ctx->total[0] < (unsigned long) ilen )
-        ctx->total[1]++;
-
-    if( left && ilen >= fill )
-    {
-        memcpy( (void *) (ctx->buffer + left),
-                (void *) input, fill );
-        sha2_process( ctx, ctx->buffer );
-        input += fill;
-        ilen  -= fill;
-        left = 0;
-    }
-
-    while( ilen >= 64 )
-    {
-        sha2_process( ctx, input );
-        input += 64;
-        ilen  -= 64;
-    }
-
-    if( ilen > 0 )
-    {
-        memcpy( (void *) (ctx->buffer + left),
-                (void *) input, ilen );
-    }
-}
-
-static const unsigned char sha2_padding[64] =
-{
- 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
-};
-
-/*
- * SHA-256 final digest
- */
-void sha2_finish( sha2_context *ctx, unsigned char output[32] )
-{
-    unsigned long last, padn;
-    unsigned long high, low;
-    unsigned char msglen[8];
-
-    high = ( ctx->total[0] >> 29 )
-         | ( ctx->total[1] <<  3 );
-    low  = ( ctx->total[0] <<  3 );
-
-    PUT_ULONG_BE( high, msglen, 0 );
-    PUT_ULONG_BE( low,  msglen, 4 );
-
-    last = ctx->total[0] & 0x3F;
-    padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last );
-
-    sha2_update( ctx, (unsigned char *) sha2_padding, padn );
-    sha2_update( ctx, msglen, 8 );
-
-    PUT_ULONG_BE( ctx->state[0], output,  0 );
-    PUT_ULONG_BE( ctx->state[1], output,  4 );
-    PUT_ULONG_BE( ctx->state[2], output,  8 );
-    PUT_ULONG_BE( ctx->state[3], output, 12 );
-    PUT_ULONG_BE( ctx->state[4], output, 16 );
-    PUT_ULONG_BE( ctx->state[5], output, 20 );
-    PUT_ULONG_BE( ctx->state[6], output, 24 );
-
-    if( ctx->is224 == 0 )
-        PUT_ULONG_BE( ctx->state[7], output, 28 );
-}
-
-/*
- * output = SHA-256( input buffer )
- */
-void sha2( const unsigned char *input, int ilen,
-           unsigned char output[32], int is224 )
-{
-    sha2_context ctx;
-
-    sha2_starts( &ctx, is224 );
-    sha2_update( &ctx, input, ilen );
-    sha2_finish( &ctx, output );
-
-    memset( &ctx, 0, sizeof( sha2_context ) );
-}
-
-/*
- * output = SHA-256( file contents )
- */
-int sha2_file( const char *path, unsigned char output[32], int is224 )
-{
-    FILE *f;
-    size_t n;
-    sha2_context ctx;
-    unsigned char buf[1024];
-
-    if( ( f = fopen( path, "rb" ) ) == NULL )
-        return( 1 );
-
-    sha2_starts( &ctx, is224 );
-
-    while( ( n = fread( buf, 1, sizeof( buf ), f ) ) > 0 )
-        sha2_update( &ctx, buf, (int) n );
-
-    sha2_finish( &ctx, output );
-
-    memset( &ctx, 0, sizeof( sha2_context ) );
-
-    if( ferror( f ) != 0 )
-    {
-        fclose( f );
-        return( 2 );
-    }
-
-    fclose( f );
-    return( 0 );
-}
-
-/*
- * SHA-256 HMAC context setup
- */
-void sha2_hmac_starts( sha2_context *ctx, const unsigned char *key, int keylen,
-                       int is224 )
-{
-    int i;
-    unsigned char sum[32];
-
-    if( keylen > 64 )
-    {
-        sha2( key, keylen, sum, is224 );
-        keylen = ( is224 ) ? 28 : 32;
-        key = sum;
-    }
-
-    memset( ctx->ipad, 0x36, 64 );
-    memset( ctx->opad, 0x5C, 64 );
-
-    for( i = 0; i < keylen; i++ )
-    {
-        ctx->ipad[i] = (unsigned char)( ctx->ipad[i] ^ key[i] );
-        ctx->opad[i] = (unsigned char)( ctx->opad[i] ^ key[i] );
-    }
-
-    sha2_starts( ctx, is224 );
-    sha2_update( ctx, ctx->ipad, 64 );
-
-    memset( sum, 0, sizeof( sum ) );
-}
-
-/*
- * SHA-256 HMAC process buffer
- */
-void sha2_hmac_update( sha2_context *ctx, const unsigned char *input, int ilen )
-{
-    sha2_update( ctx, input, ilen );
-}
-
-/*
- * SHA-256 HMAC final digest
- */
-void sha2_hmac_finish( sha2_context *ctx, unsigned char output[32] )
-{
-    int is224, hlen;
-    unsigned char tmpbuf[32];
-
-    is224 = ctx->is224;
-    hlen = ( is224 == 0 ) ? 32 : 28;
-
-    sha2_finish( ctx, tmpbuf );
-    sha2_starts( ctx, is224 );
-    sha2_update( ctx, ctx->opad, 64 );
-    sha2_update( ctx, tmpbuf, hlen );
-    sha2_finish( ctx, output );
-
-    memset( tmpbuf, 0, sizeof( tmpbuf ) );
-}
-
-/*
- * SHA-256 HMAC context reset
- */
-void sha2_hmac_reset( sha2_context *ctx )
-{
-    sha2_starts( ctx, ctx->is224 );
-    sha2_update( ctx, ctx->ipad, 64 );
-}
-
-/*
- * output = HMAC-SHA-256( hmac key, input buffer )
- */
-void sha2_hmac( const unsigned char *key, int keylen,
-                const unsigned char *input, int ilen,
-                unsigned char output[32], int is224 )
-{
-    sha2_context ctx;
-
-    sha2_hmac_starts( &ctx, key, keylen, is224 );
-    sha2_hmac_update( &ctx, input, ilen );
-    sha2_hmac_finish( &ctx, output );
-
-    memset( &ctx, 0, sizeof( sha2_context ) );
-}
-
-
-#endif
-#endif
index 6220015..a08f38f 100644 (file)
@@ -6,16 +6,18 @@ use Getopt::Long;
 my $method = "simple/simple";
 my $selector = "sel";
 my $keyfile = "aux-fixed/dkim/dkim.private";
+my $algorithm = "rsa-sha1";
 
 GetOptions(
        "method=s" => \$method,
        "selector=s" => \$selector,
        "keyfile=s" => \$keyfile,
+       "algorithm=s" => \$algorithm,
 );
 
 # create a signer object
 my $dkim = Mail::DKIM::Signer->new(
-                  Algorithm => "rsa-sha1",
+                  Algorithm => $algorithm,
                   Method => $method,
                   Domain => "test.ex",
                   Selector => $selector,
index e9f2d5d..ddd87d0 100644 (file)
@@ -43,5 +43,6 @@ send_to_server:
   dkim_domain =                test.ex
   dkim_selector =      sel
   dkim_private_key =   DIR/aux-fixed/dkim/dkim.private
+  dkim_sign_headers =  From
 
 # End
index 4787e64..0e0f840 100644 (file)
@@ -7,3 +7,6 @@
 1999-03-02 09:44:33 10HmaY-0005vi-00 DKIM: d=test.ex s=ses c=simple/simple a=rsa-sha1 b=512 [verification succeeded]
 1999-03-02 09:44:33 10HmaY-0005vi-00 signer: test.ex bits: 512
 1999-03-02 09:44:33 10HmaY-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 10HmaZ-0005vi-00 DKIM: d=test.ex s=sel c=simple/simple a=rsa-sha256 b=1024 [verification succeeded]
+1999-03-02 09:44:33 10HmaZ-0005vi-00 signer: test.ex bits: 1024
+1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@bloggs.com H=(xxx) [127.0.0.1] P=smtp S=sss id=qwerty1234@disco-zombie.net
index 056b529..39b918a 100644 (file)
@@ -1,4 +1,6 @@
 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]:1225 C="250 OK id=10HmaY-0005vi-00"
+1999-03-02 09:44:33 10HmaX-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
index b352893..5628399 100644 (file)
@@ -4,6 +4,7 @@ exim -DSERVER=server -bd -oX PORT_D
 ****
 #
 # This should pass.
+#  - sha1, 1024b
 # Mail original in aux-fixed/4500.msg1.txt
 # Sig generated by: perl aux-fixed/dkim/sign.pl --method=simple/simple < aux-fixed/4500.msg1.txt
 client 127.0.0.1 PORT_D
@@ -35,6 +36,7 @@ QUIT
 ****
 #
 # This should pass.
+#  - sha1, 512b
 # Mail original in aux-fixed/4500.msg1.txt
 # Sig generated by: perl aux-fixed/dkim/sign.pl --method=simple/simple --selector=ses \
 #                      --keyfile=aux-fixed/dkim/dkim512.private < aux-fixed/4500.msg1.txt
@@ -65,6 +67,39 @@ QUIT
 ??? 221
 ****
 #
+# This should pass.
+#  - sha256, 1024b
+# Mail original in aux-fixed/4500.msg1.txt
+# Sig generated by: perl aux-fixed/dkim/sign.pl --algorithm=rsa-sha256 \
+#                      --method=simple/simple < aux-fixed/4500.msg1.txt
+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=rsa-sha256; c=simple/simple; d=test.ex; h=from:to
+       :date:message-id:subject; s=sel; bh=3UbbJTudPxmejzh7U1Zg33U3QT+1
+       6kfV2eOTvMeiEis=; b=xQSD/JMqz0C+xKf0A1NTkPTbkDuDdJbpBuyjjT9iYvyP
+       Zez+xl0TkoPobFGVa6EN8+ZeYV18zjifhtWYLSsNmPinUtcpKQLG1zxAKmmS0JEh
+       +qihlWbeGJ5+tK588ugUzXHPj+4JBW0H6kxHvdH0l2SlQE5xs/cdggnx5QX5USY=
+From: mrgus@text.ex
+To: bakawolf@yahoo.com
+Date: Thu, 19 Nov 2015 17:00:07 -0700
+Message-ID: <qwerty1234@disco-zombie.net>
+Subject: simple test
+
+This is a simple test.
+.
+??? 250
+QUIT
+??? 221
+****
+#
 #
 killdaemon
 no_stdout_check
index 7ca3382..cebc62d 100644 (file)
@@ -3,8 +3,10 @@
 exim -bd -DSERVER=server -oX PORT_D
 ****
 exim a@test.ex
+From: nobody@example.com
+
 content
 ****
-millisleep 200
+millisleep 500
 killdaemon
 no_msglog_check