Merge native DKIM support (from DEVEL_PDKIM)
authorTom Kistner <tom@duncanthrax.net>
Wed, 10 Jun 2009 07:34:04 +0000 (07:34 +0000)
committerTom Kistner <tom@duncanthrax.net>
Wed, 10 Jun 2009 07:34:04 +0000 (07:34 +0000)
49 files changed:
src/Makefile
src/OS/Makefile-Base
src/OS/os.h-Linux
src/scripts/MakeLinks
src/src/acl.c
src/src/config.h.defaults
src/src/dk.c [deleted file]
src/src/dk.h [deleted file]
src/src/dkim-exim.c [deleted file]
src/src/dkim-exim.h [deleted file]
src/src/dkim.c [new file with mode: 0644]
src/src/dkim.h [new file with mode: 0644]
src/src/dns.c
src/src/drtables.c
src/src/exim.c
src/src/exim.h
src/src/expand.c
src/src/functions.h
src/src/globals.c
src/src/globals.h
src/src/lookups/Makefile
src/src/lookups/dkim.c [deleted file]
src/src/lookups/dkim.h [deleted file]
src/src/lookups/dnsdb.c
src/src/macros.h
src/src/pdkim/Makefile [new file with mode: 0644]
src/src/pdkim/README [new file with mode: 0644]
src/src/pdkim/base64.c [new file with mode: 0644]
src/src/pdkim/base64.h [new file with mode: 0644]
src/src/pdkim/bignum.c [new file with mode: 0644]
src/src/pdkim/bignum.h [new file with mode: 0644]
src/src/pdkim/bn_mul.h [new file with mode: 0644]
src/src/pdkim/pdkim.c [new file with mode: 0644]
src/src/pdkim/pdkim.h [new file with mode: 0644]
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 [new file with mode: 0644]
src/src/pdkim/sha1.h [new file with mode: 0644]
src/src/pdkim/sha2.c [new file with mode: 0644]
src/src/pdkim/sha2.h [new file with mode: 0644]
src/src/readconf.c
src/src/receive.c
src/src/smtp_in.c
src/src/spool_in.c
src/src/tls-gnu.c
src/src/tls-openssl.c
src/src/transport.c
src/src/transports/smtp.c
src/src/transports/smtp.h

index cf7968d..ec2856e 100644 (file)
@@ -1,4 +1,4 @@
-# $Cambridge: exim/src/Makefile,v 1.5 2008/02/14 13:49:35 fanf2 Exp $
+# $Cambridge: exim/src/Makefile,v 1.6 2009/06/10 07:34:04 tom Exp $
 
 # Top-level makefile for Exim; handles creating a build directory with
 # appropriate links, and then creating and running the main makefile in that
@@ -77,7 +77,7 @@ clean:; @echo ""; echo '*** "make clean" just removes all .o and .a files'
        cd build-$(buildname); \
        $(RM_COMMAND) -f *.o lookups/*.o lookups/*.a auths/*.o auths/*.a \
        routers/*.o routers/*.a transports/*.o transports/*.a \
-       pcre/*.o pcre/*.a
+       pdkim/*.o pdkim/*.a
 
 clean_exim:; cd build-$(buildname); \
         $(RM_COMMAND) -f *.o lookups/*.o lookups/*.a auths/*.o auths/*.a \
index e62f64a..451fcd2 100644 (file)
@@ -1,4 +1,4 @@
-# $Cambridge: exim/src/OS/Makefile-Base,v 1.16 2009/01/20 16:06:14 fanf2 Exp $
+# $Cambridge: exim/src/OS/Makefile-Base,v 1.17 2009/06/10 07:34:04 tom Exp $
 
 # This file is the basis of the main makefile for Exim and friends. The
 # makefile at the top level arranges to build the main makefile by calling
@@ -96,7 +96,7 @@ config.h: Makefile buildconfig ../src/config.h.defaults $(EDITME)
 # therefore always be run, even if the files exist. This shouldn't in fact be a
 # problem, but it does no harm. Other make programs will just ignore this.
 
-.PHONY: all allexim buildauths buildlookups buildrouters \
+.PHONY: all allexim buildauths buildlookups buildpdkim buildrouters \
         buildtransports checklocalmake clean
 
 
@@ -109,7 +109,7 @@ allexim: config.h $(EXIM_MONITOR) exicyclog exinext exiwhat \
         exim_checkaccess \
         exim_dbmbuild exim_dumpdb exim_fixdb exim_tidydb exim_lock \
         buildlookups buildrouters buildtransports \
-        buildauths exim
+        buildauths buildpdkim exim
 
 
 # Targets for special-purpose configuration header builders
@@ -300,14 +300,14 @@ convert4r4: Makefile ../src/convert4r4.src
 
 OBJ_WITH_CONTENT_SCAN = malware.o mime.o regex.o spam.o spool_mbox.o
 OBJ_WITH_OLD_DEMIME = demime.o
-OBJ_EXPERIMENTAL = bmi_spam.o spf.o srs.o dk.o dkim-exim.o dcc.o
+OBJ_EXPERIMENTAL = bmi_spam.o spf.o srs.o dcc.o
 
 # Targets for final binaries; the main one has a build number which is
 # updated each time. We don't bother with that for the auxiliaries.
 
 OBJ_EXIM = acl.o child.o crypt16.o daemon.o dbfn.o debug.o deliver.o \
         directory.o dns.o drtables.o enq.o exim.o expand.o filter.o \
-        filtertest.o globals.o \
+        filtertest.o globals.o dkim.o \
         header.o host.o ip.o log.o lss.o match.o moan.o \
         os.o parse.o queue.o \
         rda.o readconf.o receive.o retry.o rewrite.o rfc2047.o \
@@ -316,7 +316,7 @@ OBJ_EXIM = acl.o child.o crypt16.o daemon.o dbfn.o debug.o deliver.o \
         local_scan.o $(EXIM_PERL) $(OBJ_WITH_CONTENT_SCAN) \
         $(OBJ_WITH_OLD_DEMIME) $(OBJ_EXPERIMENTAL)
 
-exim:   lookups/lookups.a auths/auths.a \
+exim:   lookups/lookups.a auths/auths.a pdkim/pdkim.a \
         routers/routers.a transports/transports.a \
         $(OBJ_EXIM) version.c
        @echo " "
@@ -328,7 +328,7 @@ exim:   lookups/lookups.a auths/auths.a \
        @echo "$(LNCC) -o exim"
        $(FE)$(PURIFY) $(LNCC) -o exim $(LFLAGS) $(OBJ_EXIM) version.o \
          routers/routers.a transports/transports.a lookups/lookups.a \
-         auths/auths.a \
+         auths/auths.a pdkim/pdkim.a \
          $(LIBRESOLV) $(LIBS) $(LIBS_EXIM) $(IPV6_LIBS) $(EXTRALIBS) \
          $(EXTRALIBS_EXIM) $(DBMLIB) $(LOOKUP_LIBS) $(AUTH_LIBS) \
          $(PERL_LIBS) $(TLS_LIBS) $(PCRE_LIBS) $(LDFLAGS)
@@ -578,7 +578,7 @@ tod.o:           $(HDRS) tod.c
 transport.o:     $(HDRS) transport.c
 tree.o:          $(HDRS) tree.c
 verify.o:        $(HDRS) verify.c
-
+dkim.o:          $(HDRS) dkim.c
 
 # Dependencies for WITH_CONTENT_SCAN modules
 
@@ -599,8 +599,6 @@ demime.o:        $(HDRS) demime.c
 bmi_spam.o:      $(HDRS) bmi_spam.c
 spf.o:           $(HDRS) spf.h spf.c
 srs.o:           $(HDRS) srs.h srs.c
-dk.o:            $(HDRS) dk.h dk.c
-dkim-exim.o:     $(HDRS) dkim-exim.h dkim-exim.c
 dcc.o:           $(HDRS) dcc.h dcc.c
 
 # The module containing tables of available lookups, routers, auths, and
@@ -670,6 +668,14 @@ buildauths auths/auths.a: config.h
           INCLUDE="$(INCLUDE) $(IPV6_INCLUDE) $(TLS_INCLUDE)"; \
         echo " "
 
+# The PDKIM library
+
+buildpdkim pdkim/pdkim.a: config.h
+        @cd pdkim; $(MAKE) SHELL=$(SHELL) AR="$(AR)" $(MFLAGS) CC="$(CC)" CFLAGS="$(CFLAGS)" \
+          FE="$(FE)" RANLIB="$(RANLIB)" RM_COMMAND="$(RM_COMMAND)" HDRS="$(PHDRS)" \
+          INCLUDE="$(INCLUDE) $(IPV6_INCLUDE) $(TLS_INCLUDE)"; \
+        echo " "
+
 # The "clean", "install", and "makefile" targets just pass themselves back to
 # the main Exim makefile. These targets will be obeyed only if "make" is obeyed
 # for them in the build directory.
index bde5dd7..c9f417b 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/OS/os.h-Linux,v 1.7 2007/10/04 13:28:06 tom Exp $ */
+/* $Cambridge: exim/src/OS/os.h-Linux,v 1.8 2009/06/10 07:34:04 tom Exp $ */
 
 /* Exim: OS-specific C header file for Linux */
 
 #define NO_IP_VAR_H
 #define SIG_IGN_WORKS
 
-/* When using the experimental Domainkeys/DKIM extensions, setting
-HAVE_LINUX_SENDFILE can increase performance on outgoing mail a bit.
-Note: With older glibc versions this setting will conflict with the
-_FILE_OFFSET_BITS=64 setting defined as part of the Linux CFLAGS. */
+/* When using the DKIM, setting HAVE_LINUX_SENDFILE can increase
+performance on outgoing mail a bit. Note: With older glibc versions
+this setting will conflict with the _FILE_OFFSET_BITS=64 setting
+defined as part of the Linux CFLAGS. */
 
 /* #define HAVE_LINUX_SENDFILE */
 
index 74d7adc..15c648c 100755 (executable)
@@ -1,5 +1,5 @@
 #!/bin/sh
-# $Cambridge: exim/src/scripts/MakeLinks,v 1.14 2008/01/17 13:03:35 tom Exp $
+# $Cambridge: exim/src/scripts/MakeLinks,v 1.15 2009/06/10 07:34:04 tom Exp $
 
 # Script to build links for all the exim source files from the system-
 # specific build directory. It should be run from within that directory.
@@ -189,6 +189,27 @@ ln -s ../../src/auths/spa.c              spa.c
 ln -s ../../src/auths/spa.h              spa.h
 cd ..
 
+# Likewise for the code for the PDKIM library
+mkdir pdkim
+cd pdkim
+ln -s ../../src/pdkim/README             README
+ln -s ../../src/pdkim/Makefile           Makefile
+ln -s ../../src/pdkim/base64.c           base64.c
+ln -s ../../src/pdkim/base64.h           base64.h
+ln -s ../../src/pdkim/bignum.c           bignum.c
+ln -s ../../src/pdkim/bignum.h           bignum.h
+ln -s ../../src/pdkim/bn_mul.h           bn_mul.h
+ln -s ../../src/pdkim/pdkim.c            pdkim.c
+ln -s ../../src/pdkim/pdkim.h            pdkim.h
+ln -s ../../src/pdkim/pdkim-api.h        pdkim-api.h
+ln -s ../../src/pdkim/rsa.c              rsa.c
+ln -s ../../src/pdkim/rsa.h              rsa.h
+ln -s ../../src/pdkim/sha1.c             sha1.c
+ln -s ../../src/pdkim/sha1.h             sha1.h
+ln -s ../../src/pdkim/sha2.c             sha2.c
+ln -s ../../src/pdkim/sha2.h             sha2.h
+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
 # configuration. Likewise for the os.c file, which gets build dynamically.
@@ -259,6 +280,8 @@ ln -s ../src/transport.c       transport.c
 ln -s ../src/tree.c            tree.c
 ln -s ../src/verify.c          verify.c
 ln -s ../src/version.c         version.c
+ln -s ../src/dkim.c            dkim.c
+ln -s ../src/dkim.h            dkim.h
 
 # WITH_CONTENT_SCAN
 ln -s ../src/spam.c            spam.c
@@ -280,10 +303,6 @@ ln -s ../src/spf.c             spf.c
 ln -s ../src/spf.h             spf.h
 ln -s ../src/srs.c             srs.c
 ln -s ../src/srs.h             srs.h
-ln -s ../src/dk.c              dk.c
-ln -s ../src/dk.h              dk.h
-ln -s ../src/dkim-exim.c       dkim-exim.c
-ln -s ../src/dkim-exim.h       dkim-exim.h
 ln -s ../src/dcc.c             dcc.c
 ln -s ../src/dcc.h             dcc.h
 
index fcafc6b..a3e79b1 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/acl.c,v 1.82 2008/02/12 12:52:51 nm4 Exp $ */
+/* $Cambridge: exim/src/src/acl.c,v 1.83 2009/06/10 07:34:04 tom Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -65,13 +65,9 @@ enum { ACLC_ACL,
 #ifdef WITH_OLD_DEMIME
        ACLC_DEMIME,
 #endif
-#ifdef EXPERIMENTAL_DOMAINKEYS
-       ACLC_DK_DOMAIN_SOURCE,
-       ACLC_DK_POLICY,
-       ACLC_DK_SENDER_DOMAINS,
-       ACLC_DK_SENDER_LOCAL_PARTS,
-       ACLC_DK_SENDERS,
-       ACLC_DK_STATUS,
+#ifndef DISABLE_DKIM
+       ACLC_DKIM_SIGNER,
+       ACLC_DKIM_STATUS,
 #endif
        ACLC_DNSLISTS,
        ACLC_DOMAINS,
@@ -131,13 +127,9 @@ static uschar *conditions[] = {
 #ifdef WITH_OLD_DEMIME
   US"demime",
 #endif
-#ifdef EXPERIMENTAL_DOMAINKEYS
-  US"dk_domain_source",
-  US"dk_policy",
-  US"dk_sender_domains",
-  US"dk_sender_local_parts",
-  US"dk_senders",
-  US"dk_status",
+#ifndef DISABLE_DKIM
+  US"dkim_signers",
+  US"dkim_status",
 #endif
   US"dnslists",
   US"domains",
@@ -179,10 +171,7 @@ enum {
   #ifdef EXPERIMENTAL_BRIGHTMAIL
   CONTROL_BMI_RUN,
   #endif
-  #ifdef EXPERIMENTAL_DOMAINKEYS
-  CONTROL_DK_VERIFY,
-  #endif
-  #ifdef EXPERIMENTAL_DKIM
+  #ifndef DISABLE_DKIM
   CONTROL_DKIM_VERIFY,
   #endif
   CONTROL_ERROR,
@@ -215,11 +204,8 @@ static uschar *controls[] = {
   #ifdef EXPERIMENTAL_BRIGHTMAIL
   US"bmi_run",
   #endif
-  #ifdef EXPERIMENTAL_DOMAINKEYS
-  US"dk_verify",
-  #endif
-  #ifdef EXPERIMENTAL_DKIM
-  US"dkim_verify",
+  #ifndef DISABLE_DKIM
+  US"dkim_disable_verify",
   #endif
   US"error",
   US"caseful_local_part",
@@ -265,13 +251,9 @@ static uschar cond_expand_at_top[] = {
 #ifdef WITH_OLD_DEMIME
   TRUE,    /* demime */
 #endif
-#ifdef EXPERIMENTAL_DOMAINKEYS
-  TRUE,    /* dk_domain_source */
-  TRUE,    /* dk_policy */
-  TRUE,    /* dk_sender_domains */
-  TRUE,    /* dk_sender_local_parts */
-  TRUE,    /* dk_senders */
-  TRUE,    /* dk_status */
+#ifndef DISABLE_DKIM
+  TRUE,    /* dkim_signers */
+  TRUE,    /* dkim_status */
 #endif
   TRUE,    /* dnslists */
   FALSE,   /* domains */
@@ -329,13 +311,9 @@ static uschar cond_modifiers[] = {
 #ifdef WITH_OLD_DEMIME
   FALSE,   /* demime */
 #endif
-#ifdef EXPERIMENTAL_DOMAINKEYS
-  FALSE,   /* dk_domain_source */
-  FALSE,   /* dk_policy */
-  FALSE,   /* dk_sender_domains */
-  FALSE,   /* dk_sender_local_parts */
-  FALSE,   /* dk_senders */
-  FALSE,   /* dk_status */
+#ifndef DISABLE_DKIM
+  FALSE,   /* dkim_signers */
+  FALSE,   /* dkim_status */
 #endif
   FALSE,   /* dnslists */
   FALSE,   /* domains */
@@ -426,54 +404,12 @@ static unsigned int cond_forbids[] = {
   ~((1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_NOTSMTP)),   /* demime */
   #endif
 
-  #ifdef EXPERIMENTAL_DOMAINKEYS
-  (1<<ACL_WHERE_AUTH)|                             /* dk_domain_source */
-    (1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)|
-    (1<<ACL_WHERE_RCPT)|(1<<ACL_WHERE_PREDATA)|
-    (1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|
-    (1<<ACL_WHERE_MAILAUTH)|(1<<ACL_WHERE_QUIT)|
-    (1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_STARTTLS)|
-    (1<<ACL_WHERE_VRFY)|(1<<ACL_WHERE_NOTSMTP_START),
-
-  (1<<ACL_WHERE_AUTH)|                             /* dk_policy */
-    (1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)|
-    (1<<ACL_WHERE_RCPT)|(1<<ACL_WHERE_PREDATA)|
-    (1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|
-    (1<<ACL_WHERE_MAILAUTH)|(1<<ACL_WHERE_QUIT)|
-    (1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_STARTTLS)|
-    (1<<ACL_WHERE_VRFY)|(1<<ACL_WHERE_NOTSMTP_START),
-
-  (1<<ACL_WHERE_AUTH)|                             /* dk_sender_domains */
-    (1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)|
-    (1<<ACL_WHERE_RCPT)|(1<<ACL_WHERE_PREDATA)|
-    (1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|
-    (1<<ACL_WHERE_MAILAUTH)|(1<<ACL_WHERE_QUIT)|
-    (1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_STARTTLS)|
-    (1<<ACL_WHERE_VRFY)|(1<<ACL_WHERE_NOTSMTP_START),
-
-  (1<<ACL_WHERE_AUTH)|                             /* dk_sender_local_parts */
-    (1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)|
-    (1<<ACL_WHERE_RCPT)|(1<<ACL_WHERE_PREDATA)|
-    (1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|
-    (1<<ACL_WHERE_MAILAUTH)|(1<<ACL_WHERE_QUIT)|
-    (1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_STARTTLS)|
-    (1<<ACL_WHERE_VRFY)|(1<<ACL_WHERE_NOTSMTP_START),
-
-  (1<<ACL_WHERE_AUTH)|                             /* dk_senders */
-    (1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)|
-    (1<<ACL_WHERE_RCPT)|(1<<ACL_WHERE_PREDATA)|
-    (1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|
-    (1<<ACL_WHERE_MAILAUTH)|(1<<ACL_WHERE_QUIT)|
-    (1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_STARTTLS)|
-    (1<<ACL_WHERE_VRFY)|(1<<ACL_WHERE_NOTSMTP_START),
+  #ifndef DISABLE_DKIM
+  (unsigned int)
+  ~(1<<ACL_WHERE_DKIM),                            /* dkim_signers */
 
-  (1<<ACL_WHERE_AUTH)|                             /* dk_status */
-    (1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)|
-    (1<<ACL_WHERE_RCPT)|(1<<ACL_WHERE_PREDATA)|
-    (1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|
-    (1<<ACL_WHERE_MAILAUTH)|(1<<ACL_WHERE_QUIT)|
-    (1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_STARTTLS)|
-    (1<<ACL_WHERE_VRFY)|(1<<ACL_WHERE_NOTSMTP_START),
+  (unsigned int)
+  ~(1<<ACL_WHERE_DKIM),                            /* dkim_status */
   #endif
 
   (1<<ACL_WHERE_NOTSMTP)|                          /* dnslists */
@@ -580,13 +516,8 @@ static unsigned int control_forbids[] = {
   0,                                               /* bmi_run */
   #endif
 
-  #ifdef EXPERIMENTAL_DOMAINKEYS
-  (1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_NOTSMTP)|      /* dk_verify */
-    (1<<ACL_WHERE_NOTSMTP_START),
-  #endif
-
-  #ifdef EXPERIMENTAL_DKIM
-  (1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_NOTSMTP)|      /* dkim_verify */
+  #ifndef DISABLE_DKIM
+  (1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_NOTSMTP)|      /* dkim_disable_verify */
     (1<<ACL_WHERE_NOTSMTP_START),
   #endif
 
@@ -666,11 +597,8 @@ static control_def controls_list[] = {
 #ifdef EXPERIMENTAL_BRIGHTMAIL
   { US"bmi_run",                 CONTROL_BMI_RUN, FALSE },
 #endif
-#ifdef EXPERIMENTAL_DOMAINKEYS
-  { US"dk_verify",               CONTROL_DK_VERIFY, FALSE },
-#endif
-#ifdef EXPERIMENTAL_DKIM
-  { US"dkim_verify",             CONTROL_DKIM_VERIFY, FALSE },
+#ifndef DISABLE_DKIM
+  { US"dkim_disable_verify",     CONTROL_DKIM_VERIFY, FALSE },
 #endif
   { US"caseful_local_part",      CONTROL_CASEFUL_LOCAL_PART, FALSE },
   { US"caselower_local_part",    CONTROL_CASELOWER_LOCAL_PART, FALSE },
@@ -2650,15 +2578,9 @@ for (; cb != NULL; cb = cb->next)
       break;
       #endif
 
-      #ifdef EXPERIMENTAL_DOMAINKEYS
-      case CONTROL_DK_VERIFY:
-      dk_do_verify = 1;
-      break;
-      #endif
-
-      #ifdef EXPERIMENTAL_DKIM
+      #ifndef DISABLE_DKIM
       case CONTROL_DKIM_VERIFY:
-      dkim_do_verify = 1;
+      dkim_disable_verify = TRUE;
       break;
       #endif
 
@@ -2862,95 +2784,27 @@ for (; cb != NULL; cb = cb->next)
     break;
     #endif
 
-    #ifdef EXPERIMENTAL_DOMAINKEYS
-    case ACLC_DK_DOMAIN_SOURCE:
-    if (dk_verify_block == NULL) { rc = FAIL; break; };
-    /* check header source of domain against given string */
-    switch (dk_verify_block->address_source) {
-      case DK_EXIM_ADDRESS_FROM_FROM:
-        rc = match_isinlist(US"from", &arg, 0, NULL,
-                            NULL, MCL_STRING, TRUE, NULL);
-      break;
-      case DK_EXIM_ADDRESS_FROM_SENDER:
-        rc = match_isinlist(US"sender", &arg, 0, NULL,
-                            NULL, MCL_STRING, TRUE, NULL);
-      break;
-      case DK_EXIM_ADDRESS_NONE:
-        rc = match_isinlist(US"none", &arg, 0, NULL,
-                            NULL, MCL_STRING, TRUE, NULL);
-      break;
+    #ifndef DISABLE_DKIM
+    case ACLC_DKIM_SIGNER:
+    if (dkim_signing_domain != NULL)
+      {
+      rc = match_isinlist(dkim_signing_domain,
+                          &arg,0,NULL,NULL,MCL_STRING,TRUE,NULL);
+      if (rc == FAIL)
+        {
+        rc = match_isinlist(dkim_exim_expand_query(DKIM_IDENTITY),
+                            &arg,0,NULL,NULL,MCL_STRING,TRUE,NULL);
+        }
+      }
+    else
+      {
+       rc = FAIL;
       }
     break;
 
-    case ACLC_DK_POLICY:
-    if (dk_verify_block == NULL) { rc = FAIL; break; };
-    /* check policy against given string, default FAIL */
-    rc = FAIL;
-    if (dk_verify_block->signsall)
-      rc = match_isinlist(US"signsall", &arg, 0, NULL,
-                          NULL, MCL_STRING, TRUE, NULL);
-    if (dk_verify_block->testing)
-      rc = match_isinlist(US"testing", &arg, 0, NULL,
-                          NULL, MCL_STRING, TRUE, NULL);
-    break;
-
-    case ACLC_DK_SENDER_DOMAINS:
-    if (dk_verify_block == NULL) { rc = FAIL; break; };
-    if (dk_verify_block->domain != NULL)
-      rc = match_isinlist(dk_verify_block->domain, &arg, 0, &domainlist_anchor,
-                          NULL, MCL_DOMAIN, TRUE, NULL);
-    else rc = FAIL;
-    break;
-
-    case ACLC_DK_SENDER_LOCAL_PARTS:
-    if (dk_verify_block == NULL) { rc = FAIL; break; };
-    if (dk_verify_block->local_part != NULL)
-      rc = match_isinlist(dk_verify_block->local_part, &arg, 0, &localpartlist_anchor,
-                          NULL, MCL_LOCALPART, TRUE, NULL);
-    else rc = FAIL;
-    break;
-
-    case ACLC_DK_SENDERS:
-    if (dk_verify_block == NULL) { rc = FAIL; break; };
-    if (dk_verify_block->address != NULL)
-      rc = match_address_list(dk_verify_block->address, TRUE, TRUE, &arg, NULL, -1, 0, NULL);
-    else rc = FAIL;
-    break;
-
-    case ACLC_DK_STATUS:
-    if (dk_verify_block == NULL) { rc = FAIL; break; };
-    if (dk_verify_block->result > 0) {
-      switch(dk_verify_block->result) {
-        case DK_EXIM_RESULT_BAD_FORMAT:
-          rc = match_isinlist(US"bad format", &arg, 0, NULL,
-                              NULL, MCL_STRING, TRUE, NULL);
-        break;
-        case DK_EXIM_RESULT_NO_KEY:
-          rc = match_isinlist(US"no key", &arg, 0, NULL,
-                              NULL, MCL_STRING, TRUE, NULL);
-        break;
-        case DK_EXIM_RESULT_NO_SIGNATURE:
-          rc = match_isinlist(US"no signature", &arg, 0, NULL,
-                              NULL, MCL_STRING, TRUE, NULL);
-        break;
-        case DK_EXIM_RESULT_REVOKED:
-          rc = match_isinlist(US"revoked", &arg, 0, NULL,
-                              NULL, MCL_STRING, TRUE, NULL);
-        break;
-        case DK_EXIM_RESULT_NON_PARTICIPANT:
-          rc = match_isinlist(US"non-participant", &arg, 0, NULL,
-                              NULL, MCL_STRING, TRUE, NULL);
-        break;
-        case DK_EXIM_RESULT_GOOD:
-          rc = match_isinlist(US"good", &arg, 0, NULL,
-                              NULL, MCL_STRING, TRUE, NULL);
-        break;
-        case DK_EXIM_RESULT_BAD:
-          rc = match_isinlist(US"bad", &arg, 0, NULL,
-                              NULL, MCL_STRING, TRUE, NULL);
-        break;
-        }
-      }
+    case ACLC_DKIM_STATUS:
+    rc = match_isinlist(dkim_exim_expand_query(DKIM_VERIFY_STATUS),
+                        &arg,0,NULL,NULL,MCL_STRING,TRUE,NULL);
     break;
     #endif
 
index 9df56e0..298f2f3 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/config.h.defaults,v 1.16 2008/01/17 13:03:35 tom Exp $ */
+/* $Cambridge: exim/src/src/config.h.defaults,v 1.17 2009/06/10 07:34:04 tom Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -150,15 +150,13 @@ it's a default value. */
 /* EXPERIMENTAL features */
 #define EXPERIMENTAL_SPF
 #define EXPERIMENTAL_SRS
-#define EXPERIMENTAL_DOMAINKEYS
-#define EXPERIMENTAL_DKIM
 #define EXPERIMENTAL_BRIGHTMAIL
 #define EXPERIMENTAL_DCC
 
 /* Things that are not routinely changed but are nevertheless configurable
 just in case. */
 
-#define DNS_MAXNAME                 256
+#define DNS_MAXNAME                1024
 #define EXPAND_MAXN                  20
 #define ROOT_UID                      0
 
diff --git a/src/src/dk.c b/src/src/dk.c
deleted file mode 100644 (file)
index 713684b..0000000
+++ /dev/null
@@ -1,440 +0,0 @@
-/* $Cambridge: exim/src/src/dk.c,v 1.12 2007/01/08 10:50:18 ph10 Exp $ */
-
-/*************************************************
-*     Exim - an Internet mail transport agent    *
-*************************************************/
-
-/* Copyright (c) University of Cambridge 1995 - 2007 */
-/* See the file NOTICE for conditions of use and distribution. */
-
-/* Code for DomainKeys support. Other DK relevant code is in
-   receive.c, transport.c and transports/smtp.c */
-
-#include "exim.h"
-
-#ifdef EXPERIMENTAL_DOMAINKEYS
-
-/* Globals related to the DK reference library. */
-DK                   *dk_context             = NULL;
-DK_LIB               *dk_lib                 = NULL;
-DK_FLAGS              dk_flags;
-DK_STAT               dk_internal_status;
-
-/* Globals related to Exim DK implementation. */
-dk_exim_verify_block *dk_verify_block        = NULL;
-
-/* Global char buffer for getc/ungetc functions. We need
-   to accumulate some chars to be able to match EOD and
-   doubled SMTP dots. Those must not be fed to the validation
-   engine. */
-int dkbuff[6] = {256,256,256,256,256,256};
-
-/* receive_getc() wrapper that feeds DK while Exim reads
-   the message. */
-int dk_receive_getc(void) {
-  int i;
-  int c = receive_getc();
-
-  if (dk_context != NULL) {
-    /* Send oldest byte */
-    if ((dkbuff[0] < 256) && (dk_internal_status == DK_STAT_OK)) {
-      dk_internal_status = dk_message(dk_context, CUS &dkbuff[0], 1);
-      if (dk_internal_status != DK_STAT_OK)
-        DEBUG(D_receive) debug_printf("DK: %s\n", DK_STAT_to_string(dk_internal_status));
-    }
-    /* rotate buffer */
-    for (i=0;i<5;i++) dkbuff[i]=dkbuff[i+1];
-    dkbuff[5]=c;
-    /* look for our candidate patterns */
-    if ( (dkbuff[1] == '\r') &&
-         (dkbuff[2] == '\n') &&
-         (dkbuff[3] == '.') &&
-         (dkbuff[4] == '\r') &&
-         (dkbuff[5] == '\n') ) {
-      /* End of DATA */
-      dkbuff[3] = 256;
-      dkbuff[4] = 256;
-      dkbuff[5] = 256;
-    }
-    if ( (dkbuff[2] == '\r') &&
-         (dkbuff[3] == '\n') &&
-         (dkbuff[4] == '.') &&
-         (dkbuff[5] == '.') ) {
-      /* doubled dot, skip this char */
-      dkbuff[5] = 256;
-    }
-  }
-return c;
-}
-
-/* When exim puts a char back in the fd, we
-   must rotate our buffer back. */
-int dk_receive_ungetc(int c) {
-  int i;
-  if (dk_context != NULL) {
-    /* rotate buffer back */
-    for (i=5;i>0;i--) dkbuff[i]=dkbuff[i-1];
-    dkbuff[0]=256;
-  }
-  return receive_ungetc(c);
-}
-
-
-void dk_exim_verify_init(void) {
-  int old_pool = store_pool;
-  store_pool = POOL_PERM;
-
-  /* Reset DK state in any case. */
-  dk_context = NULL;
-  dk_lib = NULL;
-  dk_verify_block = NULL;
-
-  /* Set up DK context if DK was requested and input is SMTP. */
-  if (smtp_input && !smtp_batched_input && dk_do_verify) {
-    /* initialize library */
-    dk_lib = dk_init(&dk_internal_status);
-    if (dk_internal_status != DK_STAT_OK)
-      debug_printf("DK: %s\n", DK_STAT_to_string(dk_internal_status));
-    else {
-      /* initialize verification context */
-      dk_context = dk_verify(dk_lib, &dk_internal_status);
-      if (dk_internal_status != DK_STAT_OK) {
-        debug_printf("DK: %s\n", DK_STAT_to_string(dk_internal_status));
-        dk_context = NULL;
-      }
-      else {
-        /* Reserve some space for the verify block. */
-        dk_verify_block = store_get(sizeof(dk_exim_verify_block));
-        if (dk_verify_block == NULL) {
-          debug_printf("DK: Can't allocate %d bytes.\n",sizeof(dk_exim_verify_block));
-          dk_context = NULL;
-        }
-        else {
-          memset(dk_verify_block, 0, sizeof(dk_exim_verify_block));
-        }
-      }
-    }
-  }
-  store_pool = old_pool;
-}
-
-
-void dk_exim_verify_finish(void) {
-  char *p,*q;
-  int i;
-  int old_pool = store_pool;
-
-  /* Bail out if context could not be set up earlier. */
-  if (dk_context == NULL)
-    return;
-
-  store_pool = POOL_PERM;
-
-  /* Send remaining bytes from input which are still in the buffer. */
-  for (i=0;i<6;i++)
-    if (dkbuff[i] < 256)
-      dk_internal_status = dk_message(dk_context, CUS &dkbuff[i], 1);
-
-  /* Flag end-of-message. */
-  dk_internal_status = dk_end(dk_context, &dk_flags);
-
-  /* dk_flags now has the selector flags (if there was one).
-     It seems that currently only the "t=" flag is supported
-     in selectors. */
-  if (dk_flags & DK_FLAG_SET)
-    if (dk_flags & DK_FLAG_TESTING)
-      dk_verify_block->testing = TRUE;
-
-  /* Grab address/domain information. */
-  p = dk_address(dk_context);
-  if (p != NULL) {
-    switch(p[0]) {
-      case 'N':
-        dk_verify_block->address_source = DK_EXIM_ADDRESS_NONE;
-      break;
-      case 'S':
-        dk_verify_block->address_source = DK_EXIM_ADDRESS_FROM_SENDER;
-      break;
-      case 'F':
-        dk_verify_block->address_source = DK_EXIM_ADDRESS_FROM_FROM;
-      break;
-    }
-    p++;
-    if (*p != '\0') {
-      dk_verify_block->address = string_copy((uschar *)p);
-      q = strrchr(p,'@');
-      if ((q != NULL) && (*(q+1) != '\0')) {
-        dk_verify_block->domain = string_copy((uschar *)(q+1));
-        *q = '\0';
-        dk_verify_block->local_part = string_copy((uschar *)p);
-        *q = '@';
-      }
-    }
-  }
-
-  /* Now grab the domain-wide DK policy */
-  dk_flags = dk_policy(dk_context);
-
-  if (dk_flags & DK_FLAG_SET) {
-    /* Selector "t=" flag has precedence, don't overwrite it if
-       the selector has set it above. */
-    if ((dk_flags & DK_FLAG_TESTING) && !dk_verify_block->testing)
-      dk_verify_block->testing = TRUE;
-    if (dk_flags & DK_FLAG_SIGNSALL)
-      dk_verify_block->signsall = TRUE;
-  }
-
-  /* Set up main result. */
-  switch(dk_internal_status)
-    {
-    case DK_STAT_NOSIG:
-      dk_verify_block->is_signed = FALSE;
-      dk_verify_block->result = DK_EXIM_RESULT_NO_SIGNATURE;
-    break;
-    case DK_STAT_OK:
-      dk_verify_block->is_signed = TRUE;
-      dk_verify_block->result = DK_EXIM_RESULT_GOOD;
-    break;
-    case DK_STAT_BADSIG:
-      dk_verify_block->is_signed = TRUE;
-      dk_verify_block->result = DK_EXIM_RESULT_BAD;
-    break;
-    case DK_STAT_REVOKED:
-      dk_verify_block->is_signed = TRUE;
-      dk_verify_block->result = DK_EXIM_RESULT_REVOKED;
-    break;
-    case DK_STAT_BADKEY:
-    case DK_STAT_SYNTAX:
-      dk_verify_block->is_signed = TRUE;
-      /* Syntax -> Bad format? */
-      dk_verify_block->result = DK_EXIM_RESULT_BAD_FORMAT;
-    break;
-    case DK_STAT_NOKEY:
-      dk_verify_block->is_signed = TRUE;
-      dk_verify_block->result = DK_EXIM_RESULT_NO_KEY;
-    break;
-    case DK_STAT_NORESOURCE:
-    case DK_STAT_INTERNAL:
-    case DK_STAT_ARGS:
-    case DK_STAT_CANTVRFY:
-      dk_verify_block->result = DK_EXIM_RESULT_ERR;
-    break;
-    /* This is missing DK_EXIM_RESULT_NON_PARTICIPANT. The lib does not
-       report such a status. */
-    }
-
-  /* Set up human readable result string. */
-  dk_verify_block->result_string = string_copy((uschar *)DK_STAT_to_string(dk_internal_status));
-
-  /* All done, reset dk_context. */
-  dk_free(dk_context,1);
-  dk_context = NULL;
-
-  store_pool = old_pool;
-}
-
-uschar *dk_exim_sign(int dk_fd,
-                     uschar *dk_private_key,
-                     uschar *dk_domain,
-                     uschar *dk_selector,
-                     uschar *dk_canon) {
-  uschar *rc = NULL;
-  uschar *headers = NULL;
-  int headers_len;
-  int dk_canon_int = DK_CANON_SIMPLE;
-  char buf[4096];
-  int seen_lf = 0;
-  int seen_lfdot = 0;
-  uschar sig[1024];
-  int save_errno = 0;
-  int sread;
-  int old_pool = store_pool;
-  store_pool = POOL_PERM;
-
-  dk_lib = dk_init(&dk_internal_status);
-  if (dk_internal_status != DK_STAT_OK) {
-    debug_printf("DK: %s\n", DK_STAT_to_string(dk_internal_status));
-    rc = NULL;
-    goto CLEANUP;
-  }
-
-  /* Figure out what canonicalization to use. Unfortunately
-     we must do this BEFORE knowing which domain we sign for. */
-  if ((dk_canon != NULL) && (Ustrcmp(dk_canon, "nofws") == 0)) dk_canon_int = DK_CANON_NOFWS;
-  else dk_canon = US "simple";
-
-  /* Initialize signing context. */
-  dk_context = dk_sign(dk_lib, &dk_internal_status, dk_canon_int);
-  if (dk_internal_status != DK_STAT_OK) {
-    debug_printf("DK: %s\n", DK_STAT_to_string(dk_internal_status));
-    dk_context = NULL;
-    goto CLEANUP;
-  }
-
-  while((sread = read(dk_fd,&buf,4096)) > 0) {
-    int pos = 0;
-    char c;
-
-    while (pos < sread) {
-      c = buf[pos++];
-
-      if ((c == '.') && seen_lfdot) {
-        /* escaped dot, write "\n.", continue */
-        dk_message(dk_context, CUS "\n.", 2);
-        seen_lf = 0;
-        seen_lfdot = 0;
-        continue;
-      }
-
-      if (seen_lfdot) {
-        /* EOM, write "\n" and break */
-        dk_message(dk_context, CUS "\n", 1);
-        break;
-      }
-
-      if ((c == '.') && seen_lf) {
-        seen_lfdot = 1;
-        continue;
-      }
-
-      if (seen_lf) {
-        /* normal lf, just send it */
-        dk_message(dk_context, CUS "\n", 1);
-        seen_lf = 0;
-      }
-
-      if (c == '\n') {
-        seen_lf = 1;
-        continue;
-      }
-
-      /* write the char */
-      dk_message(dk_context, CUS &c, 1);
-    }
-  }
-
-  /* Handle failed read above. */
-  if (sread == -1) {
-    debug_printf("DK: Error reading -K file.\n");
-    save_errno = errno;
-    rc = NULL;
-    goto CLEANUP;
-  }
-
-  /* Flag end-of-message. */
-  dk_internal_status = dk_end(dk_context, NULL);
-  /* TODO: check status */
-
-
-  /* Get domain to use, unless overridden. */
-  if (dk_domain == NULL) {
-    dk_domain = US dk_address(dk_context);
-    switch(dk_domain[0]) {
-      case 'N': dk_domain = NULL; break;
-      case 'F':
-      case 'S':
-        dk_domain++;
-        dk_domain = Ustrrchr(dk_domain,'@');
-        if (dk_domain != NULL) {
-          uschar *p;
-          dk_domain++;
-          p = dk_domain;
-          while (*p != 0) { *p = tolower(*p); p++; }
-        }
-      break;
-    }
-    if (dk_domain == NULL) {
-      debug_printf("DK: Could not determine domain to use for signing from message headers.\n");
-      /* In this case, we return "OK" by sending up an empty string as the
-         DomainKey-Signature header. If there is no domain to sign for, we
-         can send the message anyway since the recipient has no policy to
-         apply ... */
-      rc = US"";
-      goto CLEANUP;
-    }
-  }
-  else {
-    dk_domain = expand_string(dk_domain);
-    if (dk_domain == NULL) {
-      /* expansion error, do not send message. */
-      debug_printf("DK: Error while expanding dk_domain option.\n");
-      rc = NULL;
-      goto CLEANUP;
-    }
-  }
-
-  /* Set up $dk_domain expansion variable. */
-  dk_signing_domain = dk_domain;
-
-  /* Get selector to use. */
-  dk_selector = expand_string(dk_selector);
-  if (dk_selector == NULL) {
-    log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
-      "dk_selector: %s", expand_string_message);
-    rc = NULL;
-    goto CLEANUP;
-  }
-
-  /* Set up $dk_selector expansion variable. */
-  dk_signing_selector = dk_selector;
-
-  /* Get private key to use. */
-  dk_private_key = expand_string(dk_private_key);
-  if (dk_private_key == NULL) {
-    log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
-      "dk_private_key: %s", expand_string_message);
-    rc = NULL;
-    goto CLEANUP;
-  }
-
-  if ( (Ustrlen(dk_private_key) == 0) ||
-       (Ustrcmp(dk_private_key,"0") == 0) ||
-       (Ustrcmp(dk_private_key,"false") == 0) ) {
-    /* don't sign, but no error */
-    rc = US"";
-    goto CLEANUP;
-  }
-
-  if (dk_private_key[0] == '/') {
-    int privkey_fd = 0;
-    /* Looks like a filename, load the private key. */
-    memset(big_buffer,0,big_buffer_size);
-    privkey_fd = open(CS dk_private_key,O_RDONLY);
-    (void)read(privkey_fd,big_buffer,16383);
-    (void)close(privkey_fd);
-    dk_private_key = big_buffer;
-  }
-
-  /* Get the signature. */
-  dk_internal_status = dk_getsig(dk_context, dk_private_key, sig, 1024);
-
-  /* Check for unuseable key */
-  if (dk_internal_status != DK_STAT_OK) {
-    debug_printf("DK: %s\n", DK_STAT_to_string(dk_internal_status));
-    rc = NULL;
-    goto CLEANUP;
-  }
-
-  headers_len = dk_headers(dk_context, NULL);
-  rc = store_get(1024+256+headers_len);
-  headers = store_malloc(headers_len);
-  dk_headers(dk_context, CS headers);
-  /* Build DomainKey-Signature header to return. */
-  (void)string_format(rc, 1024+256+headers_len, "DomainKey-Signature: a=rsa-sha1; q=dns; c=%s; s=%s; d=%s;\r\n"
-                     "\th=%s;\r\n"
-                     "\tb=%s;\r\n", dk_canon, dk_selector, dk_domain, headers, sig);
-
-  log_write(0, LOG_MAIN, "DK: message signed using a=rsa-sha1; q=dns; c=%s; s=%s; d=%s; h=%s;", dk_canon, dk_selector, dk_domain, headers);
-  store_free(headers);
-
-  CLEANUP:
-  if (dk_context != NULL) {
-    dk_free(dk_context,1);
-    dk_context = NULL;
-  }
-  store_pool = old_pool;
-  errno = save_errno;
-  return rc;
-}
-
-#endif
diff --git a/src/src/dk.h b/src/src/dk.h
deleted file mode 100644 (file)
index 85e1dd7..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-/* $Cambridge: exim/src/src/dk.h,v 1.3 2007/01/08 10:50:18 ph10 Exp $ */
-
-/*************************************************
-*     Exim - an Internet mail transport agent    *
-*************************************************/
-
-/* Copyright (c) University of Cambridge 1995 - 2007 */
-/* See the file NOTICE for conditions of use and distribution. */
-
-/* Code for DomainKeys support. Other DK relevant code is in
-   receive.c, transport.c and transports/smtp.c */
-
-#ifdef EXPERIMENTAL_DOMAINKEYS
-
-#include <domainkeys.h>
-
-#define DK_EXIM_ADDRESS_NONE        0
-#define DK_EXIM_ADDRESS_FROM_FROM   1
-#define DK_EXIM_ADDRESS_FROM_SENDER 2
-
-#define DK_EXIM_RESULT_ERR              0
-#define DK_EXIM_RESULT_BAD_FORMAT       1
-#define DK_EXIM_RESULT_NO_KEY           2
-#define DK_EXIM_RESULT_NO_SIGNATURE     3
-#define DK_EXIM_RESULT_REVOKED          4
-#define DK_EXIM_RESULT_NON_PARTICIPANT  5
-#define DK_EXIM_RESULT_GOOD             6
-#define DK_EXIM_RESULT_BAD              7
-
-typedef struct dk_exim_verify_block {
-  int     result;
-  int     address_source;
-  uschar *result_string;
-  uschar *address;
-  uschar *domain;
-  uschar *local_part;
-  BOOL    is_signed;
-  BOOL    signsall;
-  BOOL    testing;
-} dk_exim_verify_block;
-
-int     dk_receive_getc(void);
-int     dk_receive_ungetc(int);
-void    dk_exim_verify_init(void);
-void    dk_exim_verify_finish(void);
-int     dk_exim_verify_result(uschar **);
-uschar *dk_exim_sign(int, uschar *, uschar *, uschar *, uschar *);
-
-extern  dk_exim_verify_block *dk_verify_block;
-
-#endif
diff --git a/src/src/dkim-exim.c b/src/src/dkim-exim.c
deleted file mode 100755 (executable)
index 35c6fcf..0000000
+++ /dev/null
@@ -1,510 +0,0 @@
-/* $Cambridge: exim/src/src/dkim-exim.c,v 1.4 2008/09/30 10:03:55 tom Exp $ */
-
-/*************************************************
-*     Exim - an Internet mail transport agent    *
-*************************************************/
-
-/* Copyright (c) University of Cambridge 1995 - 2007 */
-/* See the file NOTICE for conditions of use and distribution. */
-
-/* Code for DKIM support. Other DKIM relevant code is in
-   receive.c, transport.c and transports/smtp.c */
-
-#include "exim.h"
-
-#ifdef EXPERIMENTAL_DKIM
-
-/* Globals related to the DKIM reference library. */
-DKIMContext          *dkim_context           = NULL;
-DKIMSignOptions      *dkim_sign_options      = NULL;
-DKIMVerifyOptions    *dkim_verify_options    = NULL;
-int                   dkim_verify_result     = DKIM_NEUTRAL;
-int                   dkim_internal_status   = DKIM_SUCCESS;
-
-/* Global char buffer for getc/ungetc functions. We need
-   to accumulate some chars to be able to match EOD and
-   doubled SMTP dots. Those must not be fed to the validation
-   engine. */
-int dkimbuff[6] = {256,256,256,256,256,256};
-
-/* receive_getc() wrapper that feeds DKIM while Exim reads
-   the message. */
-int dkim_receive_getc(void) {
-  int i;
-
-#ifdef EXPERIMENTAL_DOMAINKEYS
-  int c = dk_receive_getc();
-#else
-  int c = receive_getc();
-#endif
-
-  if ((dkim_context != NULL) &&
-      (dkim_internal_status == DKIM_SUCCESS)) {
-    /* Send oldest byte */
-    if (dkimbuff[0] < 256) {
-      DKIMVerifyProcess(dkim_context,(char *)&dkimbuff[0],1);
-      /* debug_printf("%c",(int)dkimbuff[0]); */
-    }
-    /* rotate buffer */
-    for (i=0;i<5;i++) dkimbuff[i]=dkimbuff[i+1];
-    dkimbuff[5]=c;
-    /* look for our candidate patterns */
-    if ( (dkimbuff[1] == '\r') &&
-         (dkimbuff[2] == '\n') &&
-         (dkimbuff[3] == '.') &&
-         (dkimbuff[4] == '\r') &&
-         (dkimbuff[5] == '\n') ) {
-      /* End of DATA */
-      dkimbuff[1] = 256;
-      dkimbuff[2] = 256;
-      dkimbuff[3] = 256;
-      dkimbuff[4] = 256;
-      dkimbuff[5] = 256;
-    }
-    if ( (dkimbuff[2] == '\r') &&
-         (dkimbuff[3] == '\n') &&
-         (dkimbuff[4] == '.') &&
-         (dkimbuff[5] == '.') ) {
-      /* doubled dot, skip this char */
-      dkimbuff[5] = 256;
-    }
-  }
-
-  return c;
-}
-
-/* When exim puts a char back in the fd, we
-   must rotate our buffer back. */
-int dkim_receive_ungetc(int c) {
-
-  if ((dkim_context != NULL) &&
-      (dkim_internal_status == DKIM_SUCCESS)) {
-    int i;
-    /* rotate buffer back */
-    for (i=5;i>0;i--) dkimbuff[i]=dkimbuff[i-1];
-    dkimbuff[0]=256;
-  }
-
-#ifdef EXPERIMENTAL_DOMAINKEYS
-  return dk_receive_ungetc(c);
-#else
-  return receive_ungetc(c);
-#endif
-}
-
-
-void dkim_exim_verify_init(void) {
-  int old_pool = store_pool;
-
-  /* Bail out unless we got perfect conditions */
-  if (!(smtp_input &&
-        !smtp_batched_input &&
-        dkim_do_verify)) {
-    return;
-  }
-
-  store_pool = POOL_PERM;
-
-  dkim_context = NULL;
-  dkim_verify_options = NULL;
-
-  dkim_context = store_get(sizeof(DKIMContext));
-  dkim_verify_options = store_get(sizeof(DKIMVerifyOptions));
-
-  if (!dkim_context ||
-      !dkim_verify_options) {
-    debug_printf("DKIM: Can't allocate memory for verifying.\n");
-    dkim_context = NULL;
-  }
-
-  memset(dkim_context,0,sizeof(DKIMContext));
-  memset(dkim_verify_options,0,sizeof(DKIMVerifyOptions));
-
-  dkim_verify_options->nHonorBodyLengthTag = 1; /* Honor the l= tag */
-  dkim_verify_options->nCheckPolicy = 1;        /* Fetch sender's policy */
-  dkim_verify_options->nSubjectRequired = 1;    /* Do not require Subject header inclusion */
-
-  dkim_verify_options->pfnSelectorCallback = NULL;
-  dkim_verify_options->pfnPolicyCallback = NULL;
-
-  dkim_status_wrap( DKIMVerifyInit(dkim_context, dkim_verify_options),
-                    "error calling DKIMVerifyInit()" );
-
-  if (dkim_internal_status != DKIM_SUCCESS) {
-    /* Invalidate context */
-    dkim_context = NULL;
-  }
-
-  store_pool = old_pool;
-}
-
-
-void dkim_exim_verify_finish(void) {
-  int i;
-  int old_pool = store_pool;
-
-  if (!dkim_do_verify ||
-      (!(smtp_input && !smtp_batched_input)) ||
-      (dkim_context == NULL) ||
-      (dkim_internal_status != DKIM_SUCCESS)) return;
-
-  store_pool = POOL_PERM;
-
-  /* Flush eventual remaining input chars */
-  for (i=0;i<6;i++)
-    if (dkimbuff[i] < 256)
-      DKIMVerifyProcess(dkim_context,(char *)&dkimbuff[i],1);
-
-  /* Fetch global result. Can be one of:
-      DKIM_SUCCESS
-      DKIM_PARTIAL_SUCCESS
-      DKIM_NEUTRAL
-      DKIM_FAIL
-  */
-  dkim_verify_result = DKIMVerifyResults(dkim_context);
-
-  store_pool = old_pool;
-}
-
-
-/* Lookup result for a given domain (or identity) */
-int dkim_exim_verify_result(uschar *domain, uschar **result, uschar **error) {
-  int sig_count = 0;
-  int i,rc;
-  char policy[512];
-  DKIMVerifyDetails *dkim_verify_details = NULL;
-
-  if (!dkim_do_verify ||
-      (!(smtp_input && !smtp_batched_input)) ||
-      (dkim_context == NULL) ||
-      (dkim_internal_status != DKIM_SUCCESS)) {
-    rc = DKIM_EXIM_UNVERIFIED;
-    goto YIELD;
-  }
-
-  DKIMVerifyGetDetails(dkim_context,
-                       &sig_count,
-                       &dkim_verify_details,
-                       policy);
-
-
-  rc = DKIM_EXIM_UNSIGNED;
-
-  debug_printf("DKIM: We have %d signature(s)\n",sig_count);
-  for (i=0;i<sig_count;i++) {
-    debug_printf( "DKIM: [%d] ", i + 1 );
-    if (!dkim_verify_details[i].Domain) {
-      debug_printf("parse error (no domain)\n");
-      continue;
-    }
-
-    if (dkim_verify_details[i].nResult >= 0) {
-      debug_printf( "GOOD d=%s i=%s\n",
-                    dkim_verify_details[i].Domain,
-                    dkim_verify_details[i].IdentityDomain );
-    }
-    else {
-      debug_printf( "FAIL d=%s i=%s c=%d\n",
-                    dkim_verify_details[i].Domain,
-                    dkim_verify_details[i].IdentityDomain,
-                    dkim_verify_details[i].nResult
-                    );
-
-    }
-
-    if ( (strcmpic(domain,dkim_verify_details[i].Domain) == 0) ||
-         (strcmpic(domain,dkim_verify_details[i].IdentityDomain) == 0) ) {
-      if (dkim_verify_details[i].nResult >= 0) {
-        rc = DKIM_EXIM_GOOD;
-        /* TODO: Add From: domain check */
-      }
-      else {
-        /* Return DEFER for temp. error types */
-        if (dkim_verify_details[i].nResult == DKIM_SELECTOR_DNS_TEMP_FAILURE) {
-          rc = DKIM_EXIM_DEFER;
-        }
-        else {
-          rc = DKIM_EXIM_FAIL;
-        }
-      }
-    }
-  }
-
-  YIELD:
-  switch (rc) {
-    case DKIM_EXIM_FAIL:
-      *result = "bad";
-    break;
-    case DKIM_EXIM_DEFER:
-      *result = "defer";
-    break;
-    case DKIM_EXIM_UNVERIFIED:
-      *result = "unverified";
-    break;
-    case DKIM_EXIM_UNSIGNED:
-      *result = "unsigned";
-    break;
-    case DKIM_EXIM_GOOD:
-      *result = "good";
-    break;
-  }
-
-  return rc;
-}
-
-
-
-uschar *dkim_exim_sign_headers = NULL;
-int dkim_exim_header_callback(const char* header) {
-  int sep = 0;
-  uschar *hdr_ptr = dkim_exim_sign_headers;
-  uschar *hdr_itr = NULL;
-  uschar  hdr_buf[512];
-  uschar *hdr_name = string_copy(US header);
-  char *colon_pos = strchr(hdr_name,':');
-
-  if (colon_pos == NULL) return 0;
-  *colon_pos = '\0';
-
-  debug_printf("DKIM: header '%s' ",hdr_name);
-  while ((hdr_itr = string_nextinlist(&hdr_ptr, &sep,
-                                      hdr_buf,
-                                      sizeof(hdr_buf))) != NULL) {
-    if (strcmpic((uschar *)hdr_name,hdr_itr) == 0) {
-      debug_printf("included in signature.\n");
-      return 1;
-    }
-  }
-  debug_printf("NOT included in signature.\n");
-  return 0;
-}
-
-uschar *dkim_exim_sign(int dkim_fd,
-                       uschar *dkim_private_key,
-                       uschar *dkim_domain,
-                       uschar *dkim_selector,
-                       uschar *dkim_canon,
-                       uschar *dkim_sign_headers) {
-
-  uschar *rc = NULL;
-  char buf[4096];
-  int seen_lf = 0;
-  int seen_lfdot = 0;
-  int save_errno = 0;
-  int sread;
-  char *signature;
-  int old_pool = store_pool;
-  store_pool = POOL_PERM;
-
-  dkim_context = NULL;
-  dkim_sign_options = NULL;
-
-  dkim_context = store_get(sizeof(DKIMContext));
-  dkim_sign_options = store_get(sizeof(DKIMSignOptions));
-
-  memset(dkim_sign_options,0,sizeof(DKIMSignOptions));
-  memset(dkim_context,0,sizeof(DKIMContext));
-
-  dkim_sign_options->nIncludeBodyLengthTag = 0;
-  dkim_sign_options->nIncludeCopiedHeaders = 0;
-  dkim_sign_options->nHash = DKIM_HASH_SHA256;
-  dkim_sign_options->nIncludeTimeStamp = 0;
-  dkim_sign_options->nIncludeQueryMethod = 0;
-  dkim_sign_options->pfnHeaderCallback = dkim_exim_header_callback;
-  dkim_sign_options->nIncludeBodyHash = DKIM_BODYHASH_IETF_1;
-
-
-  dkim_domain = expand_string(dkim_domain);
-  if (dkim_domain == NULL) {
-    /* expansion error, do not send message. */
-    log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
-          "dkim_domain: %s", expand_string_message);
-    rc = NULL;
-    goto CLEANUP;
-  }
-  /* Set up $dkim_domain expansion variable. */
-  dkim_signing_domain = dkim_domain;
-  Ustrncpy((uschar *)dkim_sign_options->szDomain,dkim_domain,255);
-
-
-  /* Get selector to use. */
-  dkim_selector = expand_string(dkim_selector);
-  if (dkim_selector == NULL) {
-    log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
-      "dkim_selector: %s", expand_string_message);
-    rc = NULL;
-    goto CLEANUP;
-  }
-  /* Set up $dkim_selector expansion variable. */
-  dkim_signing_selector = dkim_selector;
-  Ustrncpy((uschar *)dkim_sign_options->szSelector,dkim_selector,79);
-
-  /* Expand provided options */
-  dkim_canon = expand_string(dkim_canon?dkim_canon:US"relaxed");
-  if (dkim_canon == NULL) {
-    /* expansion error, do not send message. */
-    log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
-          "dkim_canon: %s", expand_string_message);
-    rc = NULL;
-    goto CLEANUP;
-  }
-  if (Ustrcmp(dkim_canon, "relaxed") == 0)
-    dkim_sign_options->nCanon = DKIM_SIGN_RELAXED;
-  else if (Ustrcmp(dkim_canon, "simple") == 0)
-    dkim_sign_options->nCanon = DKIM_SIGN_SIMPLE;
-  else {
-    log_write(0, LOG_MAIN, "DKIM: unknown canonicalization method '%s', defaulting to 'relaxed'.\n",dkim_canon);
-    dkim_sign_options->nCanon = DKIM_SIGN_RELAXED;
-  }
-
-  /* Expand signing headers once */
-  if (dkim_sign_headers != NULL) {
-    dkim_sign_headers = expand_string(dkim_sign_headers);
-    if (dkim_sign_headers == NULL) {
-      log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
-        "dkim_sign_headers: %s", expand_string_message);
-      rc = NULL;
-      goto CLEANUP;
-    }
-  }
-
-  if (dkim_sign_headers == NULL) {
-    /* Use RFC defaults */
-    dkim_sign_headers = US"from:sender:reply-to:subject:date:"
-                          "message-id:to:cc:mime-version:content-type:"
-                          "content-transfer-encoding:content-id:"
-                          "content-description:resent-date:resent-from:"
-                          "resent-sender:resent-to:resent-cc:resent-message-id:"
-                          "in-reply-to:references:"
-                          "list-id:list-help:list-unsubscribe:"
-                          "list-subscribe:list-post:list-owner:list-archive";
-  }
-  dkim_exim_sign_headers = dkim_sign_headers;
-
-  /* Get private key to use. */
-  dkim_private_key = expand_string(dkim_private_key);
-  if (dkim_private_key == NULL) {
-    log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
-      "dkim_private_key: %s", expand_string_message);
-    rc = NULL;
-    goto CLEANUP;
-  }
-
-  if ( (Ustrlen(dkim_private_key) == 0) ||
-       (Ustrcmp(dkim_private_key,"0") == 0) ||
-       (Ustrcmp(dkim_private_key,"false") == 0) ) {
-    /* don't sign, but no error */
-    rc = US"";
-    goto CLEANUP;
-  }
-
-  if (dkim_private_key[0] == '/') {
-    int privkey_fd = 0;
-    /* Looks like a filename, load the private key. */
-    memset(big_buffer,0,big_buffer_size);
-    privkey_fd = open(CS dkim_private_key,O_RDONLY);
-    (void)read(privkey_fd,big_buffer,16383);
-    (void)close(privkey_fd);
-    dkim_private_key = big_buffer;
-  }
-
-  /* Initialize signing context. */
-  dkim_status_wrap( DKIMSignInit(dkim_context, dkim_sign_options),
-                    "error calling DKIMSignInit()" );
-
-  if (dkim_internal_status != DKIM_SUCCESS) {
-    /* Invalidate context */
-    dkim_context = NULL;
-    goto CLEANUP;
-  }
-
-  while((sread = read(dkim_fd,&buf,4096)) > 0) {
-    int pos = 0;
-    char c;
-
-    while (pos < sread) {
-      c = buf[pos++];
-
-      if ((c == '.') && seen_lfdot) {
-        /* escaped dot, write "\n.", continue */
-        dkim_internal_status = DKIMSignProcess(dkim_context,"\n.",2);
-        seen_lf = 0;
-        seen_lfdot = 0;
-        continue;
-      }
-
-      if (seen_lfdot) {
-        /* EOM, write "\n" and break */
-        dkim_internal_status = DKIMSignProcess(dkim_context,"\n",1);
-        break;
-      }
-
-      if ((c == '.') && seen_lf) {
-        seen_lfdot = 1;
-        continue;
-      }
-
-      if (seen_lf) {
-        /* normal lf, just send it */
-        dkim_internal_status = DKIMSignProcess(dkim_context,"\n",1);
-        seen_lf = 0;
-      }
-
-      if (c == '\n') {
-        seen_lf = 1;
-        continue;
-      }
-
-      /* write the char */
-      dkim_internal_status = DKIMSignProcess(dkim_context,&c,1);
-    }
-  }
-
-  /* Handle failed read above. */
-  if (sread == -1) {
-    debug_printf("DKIM: Error reading -K file.\n");
-    save_errno = errno;
-    rc = NULL;
-    goto CLEANUP;
-  }
-
-  if (!dkim_status_wrap(dkim_internal_status,
-                        "error while processing message data")) {
-    rc = NULL;
-    goto CLEANUP;
-  }
-
-  if (!dkim_status_wrap( DKIMSignGetSig2( dkim_context, dkim_private_key, &signature ),
-                         "error while signing message" ) ) {
-    rc = NULL;
-    goto CLEANUP;
-  }
-
-  log_write(0, LOG_MAIN, "Message signed with DKIM: %s\n",signature);
-
-  rc = store_get(strlen(signature)+3);
-  Ustrcpy(rc,US signature);
-  Ustrcat(rc,US"\r\n");
-
-  CLEANUP:
-  if (dkim_context != NULL) {
-    dkim_context = NULL;
-  }
-  store_pool = old_pool;
-  errno = save_errno;
-  return rc;
-}
-
-unsigned int dkim_status_wrap(int stat, uschar *text) {
-  char *p = DKIMGetErrorString(stat);
-
-  if (stat != DKIM_SUCCESS) {
-    debug_printf("DKIM: %s",text?text:US"");
-    if (p) debug_printf(" (%s)",p);
-    debug_printf("\n");
-  }
-  dkim_internal_status = stat;
-  return (dkim_internal_status==DKIM_SUCCESS)?1:0;
-}
-
-#endif
diff --git a/src/src/dkim-exim.h b/src/src/dkim-exim.h
deleted file mode 100755 (executable)
index b974d95..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-/* $Cambridge: exim/src/src/dkim-exim.h,v 1.1 2007/09/28 12:21:57 tom Exp $ */
-
-/*************************************************
-*     Exim - an Internet mail transport agent    *
-*************************************************/
-
-/* Copyright (c) University of Cambridge 1995 - 2007 */
-/* See the file NOTICE for conditions of use and distribution. */
-
-/* Code for DKIM support. Other DKIM relevant code is in
-   receive.c, transport.c and transports/smtp.c */
-
-/* Exim interface to DKIM results */
-
-#define DKIM_EXIM_FAIL         -2     /* Message has a bad signature from that domain or identity. */
-#define DKIM_EXIM_DEFER        -1     /* Message has an unverified signature from that domain */
-#define DKIM_EXIM_UNVERIFIED    0     /* Message was not validated with the DK engine */
-#define DKIM_EXIM_UNSIGNED      1     /* Message has no signature from that domain or identity */
-#define DKIM_EXIM_GOOD          2     /* Message has good signature from that domain or identity */
-
-
-#ifdef EXPERIMENTAL_DKIM
-#include <dkim.h>
-
-int     dkim_exim_verify_result(uschar *,uschar **,uschar **);
-
-/* Internal prototypes */
-int     dkim_receive_getc(void);
-int     dkim_receive_ungetc(int);
-void    dkim_exim_verify_init(void);
-void    dkim_exim_verify_finish(void);
-uschar *dkim_exim_sign(int, uschar *, uschar *, uschar *, uschar *, uschar *);
-unsigned int dkim_status_wrap(int, uschar *);
-
-#endif
diff --git a/src/src/dkim.c b/src/src/dkim.c
new file mode 100644 (file)
index 0000000..2a53f15
--- /dev/null
@@ -0,0 +1,500 @@
+/* $Cambridge: exim/src/src/dkim.c,v 1.2 2009/06/10 07:34:04 tom Exp $ */
+
+/*************************************************
+*     Exim - an Internet mail transport agent    *
+*************************************************/
+
+/* Copyright (c) University of Cambridge 2009 */
+/* See the file NOTICE for conditions of use and distribution. */
+
+/* Code for DKIM support. Other DKIM relevant code is in
+   receive.c, transport.c and transports/smtp.c */
+
+#include "exim.h"
+
+#ifndef DISABLE_DKIM
+
+#include "pdkim/pdkim.h"
+
+pdkim_ctx       *dkim_verify_ctx = NULL;
+pdkim_signature *dkim_signatures = NULL;
+pdkim_signature *dkim_cur_sig    = NULL;
+
+int dkim_exim_query_dns_txt(char *name, char *answer) {
+  dns_answer dnsa;
+  dns_scan   dnss;
+  dns_record *rr;
+
+  if (dns_lookup(&dnsa, (uschar *)name, T_TXT, NULL) != DNS_SUCCEED) return PDKIM_FAIL;
+
+  /* Search for TXT record */
+  for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
+       rr != NULL;
+       rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT))
+    if (rr->type == T_TXT) break;
+
+  /* Copy record content to the answer buffer */
+  if (rr != NULL) {
+    int rr_offset = 0;
+    int answer_offset = 0;
+    while (rr_offset < rr->size) {
+      uschar len = (rr->data)[rr_offset++];
+      snprintf(answer+(answer_offset),
+               PDKIM_DNS_TXT_MAX_RECLEN-(answer_offset),
+               "%.*s", (int)len, (char *)((rr->data)+rr_offset));
+      rr_offset+=len;
+      answer_offset+=len;
+    }
+  }
+  else return PDKIM_FAIL;
+
+  return PDKIM_OK;
+}
+
+
+void dkim_exim_verify_init(void) {
+
+  /* Free previous context if there is one */
+  if (dkim_verify_ctx) pdkim_free_ctx(dkim_verify_ctx);
+
+  /* Create new context */
+  dkim_verify_ctx = pdkim_init_verify(PDKIM_INPUT_SMTP,
+                                      &dkim_exim_query_dns_txt
+                                     );
+
+  if (dkim_verify_ctx != NULL) {
+    dkim_collect_input = TRUE;
+    pdkim_set_debug_stream(dkim_verify_ctx,debug_file);
+  }
+  else dkim_collect_input = FALSE;
+
+}
+
+
+void dkim_exim_verify_feed(uschar *data, int len) {
+  if (dkim_collect_input &&
+      pdkim_feed(dkim_verify_ctx,
+                 (char *)data,
+                 len) != PDKIM_OK) dkim_collect_input = FALSE;
+}
+
+
+void dkim_exim_verify_finish(void) {
+  pdkim_signature *sig = NULL;
+  int dkim_signing_domains_size = 0;
+  int dkim_signing_domains_ptr = 0;
+  dkim_signing_domains = NULL;
+
+  /* Delete eventual previous signature chain */
+  dkim_signatures = NULL;
+
+  /* If we have arrived here with dkim_collect_input == FALSE, it
+     means there was a processing error somewhere along the way.
+     Log the incident and disable futher verification. */
+  if (!dkim_collect_input) {
+    log_write(0, LOG_MAIN|LOG_PANIC, "DKIM: Error while running this message through validation, disabling signature verification.");
+    dkim_disable_verify = TRUE;
+    return;
+  }
+  dkim_collect_input = FALSE;
+
+  /* Finish DKIM operation and fetch link to signatures chain */
+  if (pdkim_feed_finish(dkim_verify_ctx,&dkim_signatures) != PDKIM_OK) return;
+
+  sig = dkim_signatures;
+  while (sig != NULL) {
+    int size = 0;
+    int ptr = 0;
+    /* Log a line for each signature */
+    uschar *logmsg = string_append(NULL, &size, &ptr, 5,
+
+      string_sprintf( "DKIM: d=%s s=%s c=%s/%s a=%s ",
+                      sig->domain,
+                      sig->selector,
+                      (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->identity != NULL)?
+        string_sprintf("i=%s ", sig->identity)
+        :
+        US""
+      ),
+      ((sig->created > 0)?
+        string_sprintf("t=%lu ", sig->created)
+        :
+        US""
+      ),
+      ((sig->expires > 0)?
+        string_sprintf("x=%lu ", sig->expires)
+        :
+        US""
+      ),
+      ((sig->bodylength > -1)?
+        string_sprintf("l=%lu ", sig->bodylength)
+        :
+        US""
+      )
+    );
+
+    switch(sig->verify_status) {
+      case PDKIM_VERIFY_NONE:
+        logmsg = string_append(logmsg, &size, &ptr, 1, "[not verified]");
+      break;
+      case PDKIM_VERIFY_INVALID:
+        logmsg = string_append(logmsg, &size, &ptr, 1, "[invalid - ");
+        switch (sig->verify_ext_status) {
+          case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
+            logmsg = string_append(logmsg, &size, &ptr, 1, "public key record (currently?) unavailable]");
+          break;
+          case PDKIM_VERIFY_INVALID_BUFFER_SIZE:
+            logmsg = string_append(logmsg, &size, &ptr, 1, "overlong public key record]");
+          break;
+          case PDKIM_VERIFY_INVALID_PUBKEY_PARSING:
+            logmsg = string_append(logmsg, &size, &ptr, 1, "syntax error in public key record]");
+          break;
+          default:
+            logmsg = string_append(logmsg, &size, &ptr, 1, "unspecified problem]");
+        }
+      break;
+      case PDKIM_VERIFY_FAIL:
+        logmsg = string_append(logmsg, &size, &ptr, 1, "[verification failed - ");
+        switch (sig->verify_ext_status) {
+          case PDKIM_VERIFY_FAIL_BODY:
+            logmsg = string_append(logmsg, &size, &ptr, 1, "body hash mismatch (body probably modified in transit)]");
+          break;
+          case PDKIM_VERIFY_FAIL_MESSAGE:
+            logmsg = string_append(logmsg, &size, &ptr, 1, "signature did not verify (headers probably modified in transit)]");
+          break;
+          default:
+            logmsg = string_append(logmsg, &size, &ptr, 1, "unspecified reason]");
+        }
+      break;
+      case PDKIM_VERIFY_PASS:
+        logmsg = string_append(logmsg, &size, &ptr, 1, "[verification succeeded]");
+      break;
+    }
+
+    logmsg[ptr] = '\0';
+    log_write(0, LOG_MAIN, (char *)logmsg);
+
+    /* Build a colon-separated list of signing domains in dkim_signing_domains */
+    dkim_signing_domains = string_append(dkim_signing_domains,
+                                         &dkim_signing_domains_size,
+                                         &dkim_signing_domains_ptr,
+                                         2,
+                                         sig->domain,
+                                         ":"
+                                        );
+
+    /* Process next signature */
+    sig = sig->next;
+  }
+
+  /* Chop the last colon from the domain list */
+  if ((dkim_signing_domains != NULL) &&
+      (Ustrlen(dkim_signing_domains) > 0))
+    dkim_signing_domains[Ustrlen(dkim_signing_domains)-1] = '\0';
+}
+
+
+void dkim_exim_acl_setup(uschar *id) {
+  pdkim_signature *sig = dkim_signatures;
+  dkim_cur_sig = NULL;
+  if (dkim_disable_verify ||
+      !id || !sig ||
+      !dkim_verify_ctx) return;
+  /* Find signature to run ACL on */
+  while (sig != NULL) {
+    uschar *cmp_val = NULL;
+    if (Ustrchr(id,'@') != NULL) cmp_val = (uschar *)sig->identity;
+                            else cmp_val = (uschar *)sig->domain;
+    if (cmp_val && (strcmpic(cmp_val,id) == 0)) {
+      dkim_cur_sig = sig;
+      /* The "dkim_domain" and "dkim_selector" expansion variables have
+         related globals, since they are used in the signing code too.
+         Instead of inventing separate names for verification, we set
+         them here. This is easy since a domain and selector is guaranteed
+         to be in a signature. The other dkim_* expansion items are
+         dynamically fetched from dkim_cur_sig at expansion time (see
+         function below). */
+      dkim_signing_domain   = (uschar *)sig->domain;
+      dkim_signing_selector = (uschar *)sig->selector;
+      return;
+    }
+    sig = sig->next;
+  }
+}
+
+
+uschar *dkim_exim_expand_query(int what) {
+
+  if (!dkim_verify_ctx ||
+      dkim_disable_verify ||
+      !dkim_cur_sig) return dkim_exim_expand_defaults(what);
+
+  switch(what) {
+    case DKIM_ALGO:
+      return dkim_cur_sig->algo?
+              (uschar *)(dkim_cur_sig->algo)
+              :dkim_exim_expand_defaults(what);
+    case DKIM_BODYLENGTH:
+      return (dkim_cur_sig->bodylength >= 0)?
+              (uschar *)string_sprintf(OFF_T_FMT,(LONGLONG_T)dkim_cur_sig->bodylength)
+              :dkim_exim_expand_defaults(what);
+    case DKIM_CANON_BODY:
+      return dkim_cur_sig->canon_body?
+              (uschar *)(dkim_cur_sig->canon_body)
+              :dkim_exim_expand_defaults(what);
+    case DKIM_CANON_HEADERS:
+      return dkim_cur_sig->canon_headers?
+              (uschar *)(dkim_cur_sig->canon_headers)
+              :dkim_exim_expand_defaults(what);
+    case DKIM_COPIEDHEADERS:
+      return dkim_cur_sig->copiedheaders?
+              (uschar *)(dkim_cur_sig->copiedheaders)
+              :dkim_exim_expand_defaults(what);
+    case DKIM_CREATED:
+      return (dkim_cur_sig->created > 0)?
+              (uschar *)string_sprintf("%llu",dkim_cur_sig->created)
+              :dkim_exim_expand_defaults(what);
+    case DKIM_EXPIRES:
+      return (dkim_cur_sig->expires > 0)?
+              (uschar *)string_sprintf("%llu",dkim_cur_sig->expires)
+              :dkim_exim_expand_defaults(what);
+    case DKIM_HEADERNAMES:
+      return dkim_cur_sig->headernames?
+              (uschar *)(dkim_cur_sig->headernames)
+              :dkim_exim_expand_defaults(what);
+    case DKIM_IDENTITY:
+      return dkim_cur_sig->identity?
+              (uschar *)(dkim_cur_sig->identity)
+              :dkim_exim_expand_defaults(what);
+    case DKIM_KEY_GRANULARITY:
+      return dkim_cur_sig->pubkey?
+              (dkim_cur_sig->pubkey->granularity?
+                (uschar *)(dkim_cur_sig->pubkey->granularity)
+                :dkim_exim_expand_defaults(what)
+              )
+              :dkim_exim_expand_defaults(what);
+    case DKIM_KEY_SRVTYPE:
+      return dkim_cur_sig->pubkey?
+              (dkim_cur_sig->pubkey->srvtype?
+                (uschar *)(dkim_cur_sig->pubkey->srvtype)
+                :dkim_exim_expand_defaults(what)
+              )
+              :dkim_exim_expand_defaults(what);
+    case DKIM_KEY_NOTES:
+      return dkim_cur_sig->pubkey?
+              (dkim_cur_sig->pubkey->notes?
+                (uschar *)(dkim_cur_sig->pubkey->notes)
+                :dkim_exim_expand_defaults(what)
+              )
+              :dkim_exim_expand_defaults(what);
+    case DKIM_KEY_TESTING:
+      return dkim_cur_sig->pubkey?
+              (dkim_cur_sig->pubkey->testing?
+                US"1"
+                :dkim_exim_expand_defaults(what)
+              )
+              :dkim_exim_expand_defaults(what);
+    case DKIM_NOSUBDOMAINS:
+      return dkim_cur_sig->pubkey?
+              (dkim_cur_sig->pubkey->no_subdomaining?
+                US"1"
+                :dkim_exim_expand_defaults(what)
+              )
+              :dkim_exim_expand_defaults(what);
+    case DKIM_VERIFY_STATUS:
+      switch(dkim_cur_sig->verify_status) {
+        case PDKIM_VERIFY_INVALID:
+          return US"invalid";
+        case PDKIM_VERIFY_FAIL:
+          return US"fail";
+        case PDKIM_VERIFY_PASS:
+          return US"pass";
+        case PDKIM_VERIFY_NONE:
+        default:
+          return US"none";
+      }
+    case DKIM_VERIFY_REASON:
+      switch (dkim_cur_sig->verify_ext_status) {
+        case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
+          return US"pubkey_unavailable";
+        case PDKIM_VERIFY_INVALID_PUBKEY_PARSING:
+          return US"pubkey_syntax";
+        case PDKIM_VERIFY_FAIL_BODY:
+          return US"bodyhash_mismatch";
+        case PDKIM_VERIFY_FAIL_MESSAGE:
+          return US"signature_incorrect";
+      }
+    default:
+      return US"";
+  }
+}
+
+
+uschar *dkim_exim_expand_defaults(int what) {
+  switch(what) {
+    case DKIM_ALGO:               return US"";
+    case DKIM_BODYLENGTH:         return US"9999999999999";
+    case DKIM_CANON_BODY:         return US"";
+    case DKIM_CANON_HEADERS:      return US"";
+    case DKIM_COPIEDHEADERS:      return US"";
+    case DKIM_CREATED:            return US"0";
+    case DKIM_EXPIRES:            return US"9999999999999";
+    case DKIM_HEADERNAMES:        return US"";
+    case DKIM_IDENTITY:           return US"";
+    case DKIM_KEY_GRANULARITY:    return US"*";
+    case DKIM_KEY_SRVTYPE:        return US"*";
+    case DKIM_KEY_NOTES:          return US"";
+    case DKIM_KEY_TESTING:        return US"0";
+    case DKIM_NOSUBDOMAINS:       return US"0";
+    case DKIM_VERIFY_STATUS:      return US"none";
+    case DKIM_VERIFY_REASON:      return US"";
+    default:                      return US"";
+  }
+}
+
+
+uschar *dkim_exim_sign(int dkim_fd,
+                       uschar *dkim_private_key,
+                       uschar *dkim_domain,
+                       uschar *dkim_selector,
+                       uschar *dkim_canon,
+                       uschar *dkim_sign_headers) {
+  pdkim_ctx *ctx = NULL;
+  uschar *rc = NULL;
+  pdkim_signature *signature;
+  int pdkim_canon;
+  int sread;
+  char buf[4096];
+  int save_errno = 0;
+  int old_pool = store_pool;
+
+  dkim_domain = expand_string(dkim_domain);
+  if (dkim_domain == NULL) {
+    /* expansion error, do not send message. */
+    log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
+          "dkim_domain: %s", expand_string_message);
+    rc = NULL;
+    goto CLEANUP;
+  }
+  /* Set up $dkim_domain expansion variable. */
+  dkim_signing_domain = dkim_domain;
+
+  /* Get selector to use. */
+  dkim_selector = expand_string(dkim_selector);
+  if (dkim_selector == NULL) {
+    log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
+      "dkim_selector: %s", expand_string_message);
+    rc = NULL;
+    goto CLEANUP;
+  }
+  /* Set up $dkim_selector expansion variable. */
+  dkim_signing_selector = dkim_selector;
+
+  /* Get canonicalization to use */
+  dkim_canon = expand_string(dkim_canon?dkim_canon:US"relaxed");
+  if (dkim_canon == NULL) {
+    /* expansion error, do not send message. */
+    log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
+          "dkim_canon: %s", expand_string_message);
+    rc = NULL;
+    goto CLEANUP;
+  }
+  if (Ustrcmp(dkim_canon, "relaxed") == 0)
+    pdkim_canon = PDKIM_CANON_RELAXED;
+  else if (Ustrcmp(dkim_canon, "simple") == 0)
+    pdkim_canon = PDKIM_CANON_RELAXED;
+  else {
+    log_write(0, LOG_MAIN, "DKIM: unknown canonicalization method '%s', defaulting to 'relaxed'.\n",dkim_canon);
+    pdkim_canon = PDKIM_CANON_RELAXED;
+  }
+
+  /* Expand signing headers once */
+  if (dkim_sign_headers != NULL) {
+    dkim_sign_headers = expand_string(dkim_sign_headers);
+    if (dkim_sign_headers == NULL) {
+      log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
+        "dkim_sign_headers: %s", expand_string_message);
+      rc = NULL;
+      goto CLEANUP;
+    }
+  }
+
+  /* Get private key to use. */
+  dkim_private_key = expand_string(dkim_private_key);
+  if (dkim_private_key == NULL) {
+    log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
+      "dkim_private_key: %s", expand_string_message);
+    rc = NULL;
+    goto CLEANUP;
+  }
+  if ( (Ustrlen(dkim_private_key) == 0) ||
+       (Ustrcmp(dkim_private_key,"0") == 0) ||
+       (Ustrcmp(dkim_private_key,"false") == 0) ) {
+    /* don't sign, but no error */
+    rc = US"";
+    goto CLEANUP;
+  }
+
+  if (dkim_private_key[0] == '/') {
+    int privkey_fd = 0;
+    /* Looks like a filename, load the private key. */
+    memset(big_buffer,0,big_buffer_size);
+    privkey_fd = open(CS dkim_private_key,O_RDONLY);
+    (void)read(privkey_fd,big_buffer,16383);
+    (void)close(privkey_fd);
+    dkim_private_key = big_buffer;
+  }
+
+  ctx = pdkim_init_sign(PDKIM_INPUT_SMTP,
+                        (char *)dkim_signing_domain,
+                        (char *)dkim_signing_selector,
+                        (char *)dkim_private_key
+                       );
+
+  pdkim_set_debug_stream(ctx,debug_file);
+
+  pdkim_set_optional(ctx,
+                     (char *)dkim_sign_headers,
+                     NULL,
+                     pdkim_canon,
+                     pdkim_canon,
+                     -1,
+                     PDKIM_ALGO_RSA_SHA256,
+                     0,
+                     0);
+
+  while((sread = read(dkim_fd,&buf,4096)) > 0) {
+    if (pdkim_feed(ctx,buf,sread) != PDKIM_OK) {
+      rc = NULL;
+      goto CLEANUP;
+    }
+  }
+  /* Handle failed read above. */
+  if (sread == -1) {
+    debug_printf("DKIM: Error reading -K file.\n");
+    save_errno = errno;
+    rc = NULL;
+    goto CLEANUP;
+  }
+
+  if (pdkim_feed_finish(ctx,&signature) != PDKIM_OK)
+    goto CLEANUP;
+
+  rc = store_get(strlen(signature->signature_header)+3);
+  Ustrcpy(rc,US signature->signature_header);
+  Ustrcat(rc,US"\r\n");
+
+  CLEANUP:
+  if (ctx != NULL) {
+    pdkim_free_ctx(ctx);
+  }
+  store_pool = old_pool;
+  errno = save_errno;
+  return rc;
+};
+
+#endif
diff --git a/src/src/dkim.h b/src/src/dkim.h
new file mode 100644 (file)
index 0000000..28459c5
--- /dev/null
@@ -0,0 +1,33 @@
+/* $Cambridge: exim/src/src/dkim.h,v 1.2 2009/06/10 07:34:04 tom Exp $ */
+
+/*************************************************
+*     Exim - an Internet mail transport agent    *
+*************************************************/
+
+/* Copyright (c) University of Cambridge 2009 */
+/* See the file NOTICE for conditions of use and distribution. */
+
+uschar *dkim_exim_sign(int,uschar *,uschar *,uschar *,uschar *,uschar *);
+void    dkim_exim_verify_init(void);
+void    dkim_exim_verify_feed(uschar *, int);
+void    dkim_exim_verify_finish(void);
+void    dkim_exim_acl_setup(uschar *);
+uschar *dkim_exim_expand_query(int);
+uschar *dkim_exim_expand_defaults(int);
+
+#define DKIM_ALGO               1
+#define DKIM_BODYLENGTH         2
+#define DKIM_CANON_BODY         3
+#define DKIM_CANON_HEADERS      4
+#define DKIM_COPIEDHEADERS      5
+#define DKIM_CREATED            6
+#define DKIM_EXPIRES            7
+#define DKIM_HEADERNAMES        8
+#define DKIM_IDENTITY           9
+#define DKIM_KEY_GRANULARITY   10
+#define DKIM_KEY_SRVTYPE       11
+#define DKIM_KEY_NOTES         12
+#define DKIM_KEY_TESTING       13
+#define DKIM_NOSUBDOMAINS      14
+#define DKIM_VERIFY_STATUS     15
+#define DKIM_VERIFY_REASON     16
index 0b1d59d..deafedc 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/dns.c,v 1.17 2007/01/08 10:50:18 ph10 Exp $ */
+/* $Cambridge: exim/src/src/dns.c,v 1.18 2009/06/10 07:34:04 tom Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -562,7 +562,12 @@ if (running_in_test_harness)
 else
   dnsa->answerlen = res_search(CS name, C_IN, type, dnsa->answer, MAXPACKET);
 
-if (dnsa->answerlen > MAXPACKET) dnsa->answerlen = MAXPACKET;
+if (dnsa->answerlen > MAXPACKET)
+  {
+  DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) resulted in overlong packet (size %d), truncating to %d.\n",
+    name, dns_text_type(type), dnsa->answerlen, MAXPACKET);
+  dnsa->answerlen = MAXPACKET;
+  }
 
 if (dnsa->answerlen < 0) switch (h_errno)
   {
index b95d4fc..b441907 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/drtables.c,v 1.9 2007/09/28 12:21:57 tom Exp $ */
+/* $Cambridge: exim/src/src/drtables.c,v 1.10 2009/06/10 07:34:04 tom Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -105,11 +105,6 @@ be NULL for methods that don't need them. */
 #include "lookups/whoson.h"
 #endif
 
-#ifdef EXPERIMENTAL_DKIM
-#include "lookups/dkim.h"
-#endif
-
-
 /* The second field in each item below is a set of bit flags:
 
   lookup_querystyle     => this is a query-style lookup,
@@ -176,23 +171,6 @@ of the key strings. */
 #endif
   },
 
-/* DKIM lookups */
-
-  {
-  US"dkim",                      /* lookup name */
-  lookup_querystyle,             /* query style */
-#ifdef EXPERIMENTAL_DKIM
-  dkim_open,                     /* open function */
-  NULL,                          /* check function */
-  dkim_find,                     /* find function */
-  NULL,                          /* no close function */
-  NULL,                          /* no tidy function */
-  NULL                           /* no quoting function */
-#else
-  NULL, NULL, NULL, NULL, NULL, NULL /* lookup not present */
-#endif
-  },
-
 /* Using DNS TXT records as a database */
 
   {
index b078c60..77d27ab 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/exim.c,v 1.61 2008/10/12 09:58:13 nm4 Exp $ */
+/* $Cambridge: exim/src/src/exim.c,v 1.62 2009/06/10 07:34:04 tom Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -905,6 +905,9 @@ fprintf(f, "Support for:");
 #ifdef WITH_CONTENT_SCAN
   fprintf(f, " Content_Scanning");
 #endif
+#ifndef DISABLE_DKIM
+  fprintf(f, " DKIM");
+#endif
 #ifdef WITH_OLD_DEMIME
   fprintf(f, " Old_Demime");
 #endif
@@ -917,12 +920,6 @@ fprintf(f, "Support for:");
 #ifdef EXPERIMENTAL_BRIGHTMAIL
   fprintf(f, " Experimental_Brightmail");
 #endif
-#ifdef EXPERIMENTAL_DOMAINKEYS
-  fprintf(f, " Experimental_DomainKeys");
-#endif
-#ifdef EXPERIMENTAL_DKIM
-  fprintf(f, " Experimental_DKIM");
-#endif
 #ifdef EXPERIMENTAL_DCC
   fprintf(f, " Experimental_DCC");
 #endif
index a2f422c..34fb118 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/exim.h,v 1.24 2008/01/16 13:44:45 nm4 Exp $ */
+/* $Cambridge: exim/src/src/exim.h,v 1.25 2009/06/10 07:34:04 tom Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -446,11 +446,8 @@ config.h, mytypes.h, and store.h, so we don't need to mention them explicitly.
 #ifdef EXPERIMENTAL_SRS
 #include "srs.h"
 #endif
-#ifdef EXPERIMENTAL_DOMAINKEYS
-#include "dk.h"
-#endif
-#ifdef EXPERIMENTAL_DKIM
-#include "dkim-exim.h"
+#ifndef DISABLE_DKIM
+#include "dkim.h"
 #endif
 
 /* The following stuff must follow the inclusion of config.h because it
index 599dd9c..d01d0d1 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/expand.c,v 1.97 2008/12/12 14:51:47 nm4 Exp $ */
+/* $Cambridge: exim/src/src/expand.c,v 1.98 2009/06/10 07:34:04 tom Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -364,9 +364,9 @@ enum {
   vtype_load_avg,       /* value not used; result is int from os_getloadavg */
   vtype_pspace,         /* partition space; value is T/F for spool/log */
   vtype_pinodes         /* partition inodes; value is T/F for spool/log */
-#ifdef EXPERIMENTAL_DOMAINKEYS
,vtype_dk_verify       /* Serve request out of DomainKeys verification structure */
-#endif
+  #ifndef DISABLE_DKIM
 ,vtype_dkim           /* Lookup of value in DKIM signature */
+  #endif
   };
 
 /* This table must be kept in alphabetical order. */
@@ -404,22 +404,26 @@ static var_entry var_table[] = {
   { "demime_errorlevel",   vtype_int,         &demime_errorlevel },
   { "demime_reason",       vtype_stringptr,   &demime_reason },
 #endif
-#ifdef EXPERIMENTAL_DOMAINKEYS
-  { "dk_domain",           vtype_stringptr,   &dk_signing_domain },
-  { "dk_is_signed",        vtype_dk_verify,   NULL },
-  { "dk_result",           vtype_dk_verify,   NULL },
-  { "dk_selector",         vtype_stringptr,   &dk_signing_selector },
-  { "dk_sender",           vtype_dk_verify,   NULL },
-  { "dk_sender_domain",    vtype_dk_verify,   NULL },
-  { "dk_sender_local_part",vtype_dk_verify,   NULL },
-  { "dk_sender_source",    vtype_dk_verify,   NULL },
-  { "dk_signsall",         vtype_dk_verify,   NULL },
-  { "dk_status",           vtype_dk_verify,   NULL },
-  { "dk_testing",          vtype_dk_verify,   NULL },
-#endif
-#ifdef EXPERIMENTAL_DKIM
+#ifndef DISABLE_DKIM
+  { "dkim_algo",           vtype_dkim,        (void *)DKIM_ALGO },
+  { "dkim_bodylength",     vtype_dkim,        (void *)DKIM_BODYLENGTH },
+  { "dkim_canon_body",     vtype_dkim,        (void *)DKIM_CANON_BODY },
+  { "dkim_canon_headers",  vtype_dkim,        (void *)DKIM_CANON_HEADERS },
+  { "dkim_copiedheaders",  vtype_dkim,        (void *)DKIM_COPIEDHEADERS },
+  { "dkim_created",        vtype_dkim,        (void *)DKIM_CREATED },
   { "dkim_domain",         vtype_stringptr,   &dkim_signing_domain },
+  { "dkim_expires",        vtype_dkim,        (void *)DKIM_EXPIRES },
+  { "dkim_headernames",    vtype_dkim,        (void *)DKIM_HEADERNAMES },
+  { "dkim_identity",       vtype_dkim,        (void *)DKIM_IDENTITY },
+  { "dkim_key_granularity",vtype_dkim,        (void *)DKIM_KEY_GRANULARITY },
+  { "dkim_key_nosubdomains",vtype_dkim,       (void *)DKIM_NOSUBDOMAINS },
+  { "dkim_key_notes",      vtype_dkim,        (void *)DKIM_KEY_NOTES },
+  { "dkim_key_srvtype",    vtype_dkim,        (void *)DKIM_KEY_SRVTYPE },
+  { "dkim_key_testing",    vtype_dkim,        (void *)DKIM_KEY_TESTING },
   { "dkim_selector",       vtype_stringptr,   &dkim_signing_selector },
+  { "dkim_signing_domains",vtype_stringptr,   &dkim_signing_domains },
+  { "dkim_verify_reason",  vtype_dkim,        (void *)DKIM_VERIFY_REASON },
+  { "dkim_verify_status",  vtype_dkim,        (void *)DKIM_VERIFY_STATUS},
 #endif
   { "dnslist_domain",      vtype_stringptr,   &dnslist_domain },
   { "dnslist_matched",     vtype_stringptr,   &dnslist_matched },
@@ -1382,51 +1386,6 @@ while (last > first)
 
   switch (var_table[middle].type)
     {
-#ifdef EXPERIMENTAL_DOMAINKEYS
-
-    case vtype_dk_verify:
-    if (dk_verify_block == NULL) return US"";
-    s = NULL;
-    if (Ustrcmp(var_table[middle].name, "dk_result") == 0)
-      s = dk_verify_block->result_string;
-    if (Ustrcmp(var_table[middle].name, "dk_sender") == 0)
-      s = dk_verify_block->address;
-    if (Ustrcmp(var_table[middle].name, "dk_sender_domain") == 0)
-      s = dk_verify_block->domain;
-    if (Ustrcmp(var_table[middle].name, "dk_sender_local_part") == 0)
-      s = dk_verify_block->local_part;
-
-    if (Ustrcmp(var_table[middle].name, "dk_sender_source") == 0)
-      switch(dk_verify_block->address_source) {
-        case DK_EXIM_ADDRESS_NONE: s = US"0"; break;
-        case DK_EXIM_ADDRESS_FROM_FROM: s = US"from"; break;
-        case DK_EXIM_ADDRESS_FROM_SENDER: s = US"sender"; break;
-      }
-
-    if (Ustrcmp(var_table[middle].name, "dk_status") == 0)
-      switch(dk_verify_block->result) {
-        case DK_EXIM_RESULT_ERR: s = US"error"; break;
-        case DK_EXIM_RESULT_BAD_FORMAT: s = US"bad format"; break;
-        case DK_EXIM_RESULT_NO_KEY: s = US"no key"; break;
-        case DK_EXIM_RESULT_NO_SIGNATURE: s = US"no signature"; break;
-        case DK_EXIM_RESULT_REVOKED: s = US"revoked"; break;
-        case DK_EXIM_RESULT_NON_PARTICIPANT: s = US"non-participant"; break;
-        case DK_EXIM_RESULT_GOOD: s = US"good"; break;
-        case DK_EXIM_RESULT_BAD: s = US"bad"; break;
-      }
-
-    if (Ustrcmp(var_table[middle].name, "dk_signsall") == 0)
-      s = (dk_verify_block->signsall)? US"1" : US"0";
-
-    if (Ustrcmp(var_table[middle].name, "dk_testing") == 0)
-      s = (dk_verify_block->testing)? US"1" : US"0";
-
-    if (Ustrcmp(var_table[middle].name, "dk_is_signed") == 0)
-      s = (dk_verify_block->is_signed)? US"1" : US"0";
-
-    return (s == NULL)? US"" : s;
-#endif
-
     case vtype_filter_int:
     if (!filter_running) return NULL;
     /* Fall through */
@@ -1605,6 +1564,12 @@ while (last > first)
       sprintf(CS var_buffer, "%d", inodes);
       }
     return var_buffer;
+
+    #ifndef DKIM_DISABLE
+    case vtype_dkim:
+    return dkim_exim_expand_query((int)var_table[middle].value);
+    #endif
+
     }
   }
 
index 2c77c17..691ff7a 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/functions.h,v 1.43 2008/12/18 13:13:54 michael Exp $ */
+/* $Cambridge: exim/src/src/functions.h,v 1.44 2009/06/10 07:34:04 tom Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -83,12 +83,10 @@ extern void    deliver_succeeded(address_item *);
 extern int     demime(uschar **);
 #endif
 extern BOOL    directory_make(uschar *, uschar *, int, BOOL);
-#if (defined EXPERIMENTAL_DOMAINKEYS) || (defined EXPERIMENTAL_DKIM)
+#ifndef DISABLE_DKIM
 extern BOOL    dkim_transport_write_message(address_item *, int, int,
                    int, uschar *, uschar *, uschar *, uschar *, rewrite_rule *,
-                   int, uschar *, uschar *, uschar *, uschar *, uschar *, uschar *,
-                   uschar *, uschar *, uschar *, uschar *, uschar *, uschar *
-                   );
+                   int, uschar *, uschar *, uschar *, uschar *, uschar *, uschar *);
 #endif
 extern dns_address *dns_address_from_rr(dns_answer *, dns_record *);
 extern void    dns_build_reverse(uschar *, uschar *);
index 93f7491..dcb6bec 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/globals.c,v 1.81 2008/02/12 12:52:51 nm4 Exp $ */
+/* $Cambridge: exim/src/src/globals.c,v 1.82 2009/06/10 07:34:04 tom Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -182,6 +182,9 @@ uschar *acl_not_smtp_start     = NULL;
 uschar *acl_smtp_auth          = NULL;
 uschar *acl_smtp_connect       = NULL;
 uschar *acl_smtp_data          = NULL;
+#ifndef DISABLE_DKIM
+uschar *acl_smtp_dkim          = NULL;
+#endif
 uschar *acl_smtp_etrn          = NULL;
 uschar *acl_smtp_expn          = NULL;
 uschar *acl_smtp_helo          = NULL;
@@ -210,6 +213,7 @@ uschar *acl_wherenames[]       = { US"RCPT",
                                    US"MAIL",
                                    US"PREDATA",
                                    US"MIME",
+                                   US"DKIM",
                                    US"DATA",
                                    US"non-SMTP",
                                    US"AUTH",
@@ -229,6 +233,7 @@ uschar *acl_wherecodes[]       = { US"550",     /* RCPT */
                                    US"550",     /* MAIL */
                                    US"550",     /* PREDATA */
                                    US"550",     /* MIME */
+                                   US"550",     /* DKIM */
                                    US"550",     /* DATA */
                                    US"0",       /* not SMTP; not relevant */
                                    US"503",     /* AUTH */
@@ -391,7 +396,7 @@ int     callout_cache_domain_negative_expire = 3*60*60;
 int     callout_cache_positive_expire = 24*60*60;
 int     callout_cache_negative_expire = 2*60*60;
 uschar *callout_random_local_part = US"$primary_hostname-$tod_epoch-testing";
-uschar *check_dns_names_pattern= US"(?i)^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9/-]*[^\\W_])?)+$";
+uschar *check_dns_names_pattern= US"(?i)^(?>(?(1)\\.|())[^\\W](?>[a-z0-9/_-]*[^\\W])?)+(\\.?)$";
 int     check_log_inodes       = 0;
 int     check_log_space        = 0;
 BOOL    check_rfc2047_length   = TRUE;
@@ -526,16 +531,13 @@ BOOL    disable_fsync          = FALSE;
 BOOL    disable_ipv6           = FALSE;
 BOOL    disable_logging        = FALSE;
 
-#ifdef EXPERIMENTAL_DOMAINKEYS
-uschar *dk_signing_domain      = NULL;
-uschar *dk_signing_selector    = NULL;
-int     dk_do_verify           = 0;
-#endif
-
-#ifdef EXPERIMENTAL_DKIM
+#ifndef DISABLE_DKIM
+uschar *dkim_signing_domains     = NULL;
 uschar *dkim_signing_domain      = NULL;
 uschar *dkim_signing_selector    = NULL;
-int     dkim_do_verify           = 0;
+uschar *dkim_verify_signers      = US"$dkim_signing_domains";
+BOOL    dkim_collect_input       = FALSE;
+BOOL    dkim_disable_verify      = FALSE;
 #endif
 
 uschar *dns_again_means_nonexist = NULL;
index ac425ed..ff087df 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/globals.h,v 1.62 2008/02/12 12:52:51 nm4 Exp $ */
+/* $Cambridge: exim/src/src/globals.h,v 1.63 2009/06/10 07:34:04 tom Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -118,6 +118,9 @@ extern uschar *acl_not_smtp_start;     /* ACL run at the beginning of a non-SMTP
 extern uschar *acl_smtp_auth;          /* ACL run for AUTH */
 extern uschar *acl_smtp_connect;       /* ACL run on SMTP connection */
 extern uschar *acl_smtp_data;          /* ACL run after DATA received */
+#ifndef DISABLE_DKIM
+extern uschar *acl_smtp_dkim;          /* ACL run for DKIM signatures / domains */
+#endif
 extern uschar *acl_smtp_etrn;          /* ACL run for ETRN */
 extern uschar *acl_smtp_expn;          /* ACL run for EXPN */
 extern uschar *acl_smtp_helo;          /* ACL run for HELO/EHLO */
@@ -295,16 +298,13 @@ extern BOOL    disable_fsync;          /* Not for normal use */
 extern BOOL    disable_ipv6;           /* Don't do any IPv6 things */
 extern BOOL    disable_logging;        /* Disables log writing when TRUE */
 
-#ifdef EXPERIMENTAL_DOMAINKEYS
-extern uschar *dk_signing_domain;      /* Domain used for signing a message. */
-extern uschar *dk_signing_selector;    /* Selector used for signing a message. */
-extern int     dk_do_verify;           /* DK verification switch. Set with ACL control statement. */
-#endif
-
-#ifdef EXPERIMENTAL_DKIM
-extern uschar *dkim_signing_domain;      /* Domain used for signing a message. */
-extern uschar *dkim_signing_selector;    /* Selector used for signing a message. */
-extern int     dkim_do_verify;           /* DKIM verification switch. Set with ACL control statement. */
+#ifndef DISABLE_DKIM
+extern uschar *dkim_signing_domains;   /* Expansion variable, holds colon-separated list of domains that have signed a message */
+extern uschar *dkim_signing_domain;    /* Expansion variable, domain used for signing a message. */
+extern uschar *dkim_signing_selector;  /* Expansion variable, selector used for signing a message. */
+extern uschar *dkim_verify_signers;    /* Colon-separated list of domains for each of which we call the DKIM ACL */
+extern BOOL    dkim_collect_input;     /* Runtime flag that tracks wether SMTP input is fed to DKIM validation */
+extern BOOL    dkim_disable_verify;    /* Set via ACL control statement. When set, DKIM verification is disabled for the current message */
 #endif
 
 extern uschar *dns_again_means_nonexist; /* Domains that are badly set up */
index 48d208d..2c7cb87 100644 (file)
@@ -1,11 +1,11 @@
-# $Cambridge: exim/src/src/lookups/Makefile,v 1.8 2008/02/14 13:49:35 fanf2 Exp $
+# $Cambridge: exim/src/src/lookups/Makefile,v 1.9 2009/06/10 07:34:05 tom Exp $
 
 # Make file for building a library containing all the available lookups and
 # calling it lookups.a. This is called from the main make file, after cd'ing
 # to the lookups subdirectory. When the relevant LOOKUP_ macros are not
 # defined, dummy modules get compiled.
 
-OBJ = cdb.o dbmdb.o dkim.o dnsdb.o dsearch.o ibase.o ldap.o lsearch.o mysql.o nis.o \
+OBJ = cdb.o dbmdb.o dnsdb.o dsearch.o ibase.o ldap.o lsearch.o mysql.o nis.o \
       nisplus.o oracle.o passwd.o pgsql.o spf.o sqlite.o testdb.o whoson.o \
       lf_check_file.o lf_quote.o lf_sqlperform.o
 
@@ -25,7 +25,6 @@ lf_sqlperform.o: $(HDRS) lf_sqlperform.c  lf_functions.h
 
 cdb.o:           $(HDRS) cdb.c       cdb.h
 dbmdb.o:         $(HDRS) dbmdb.c     dbmdb.h
-dkim.o:                 $(HDRS) dkim.c      dkim.h
 dnsdb.o:         $(HDRS) dnsdb.c     dnsdb.h
 dsearch.o:       $(HDRS) dsearch.c   dsearch.h
 ibase.o:         $(HDRS) ibase.c     ibase.h
diff --git a/src/src/lookups/dkim.c b/src/src/lookups/dkim.c
deleted file mode 100755 (executable)
index f90283e..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-/* $Cambridge: exim/src/src/lookups/dkim.c,v 1.1 2007/09/28 12:21:57 tom Exp $ */
-
-/*************************************************
-*     Exim - an Internet mail transport agent    *
-*************************************************/
-
-/* Copyright (c) University of Cambridge 1995 - 2007 */
-/* See the file NOTICE for conditions of use and distribution. */
-
-#include "../exim.h"
-#include "dkim.h"
-
-
-
-/*************************************************
-*              Open entry point                  *
-*************************************************/
-
-/* See local README for interface description */
-
-void *
-dkim_open(uschar *filename, uschar **errmsg)
-{
-filename = filename;     /* Keep picky compilers happy */
-errmsg = errmsg;
-return (void *)(-1);     /* Just return something non-null */
-}
-
-
-
-
-/*************************************************
-*         Find entry point for passwd           *
-*************************************************/
-
-/* See local README for interface description */
-
-int
-dkim_find(void *handle, uschar *filename, uschar *keystring, int length,
-  uschar **result, uschar **errmsg, BOOL *do_cache)
-{
-#ifdef EXPERIMENTAL_DKIM
-  dkim_exim_verify_result(keystring,result,errmsg);
-  return OK;
-#else
-  *errmsg = US"DKIM support not compiled.";
-  *result = US"unverified";
-  return FAIL;
-#endif
-}
-
-/* End of lookups/dkim.c */
diff --git a/src/src/lookups/dkim.h b/src/src/lookups/dkim.h
deleted file mode 100755 (executable)
index 6e07142..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-/* $Cambridge: exim/src/src/lookups/dkim.h,v 1.1 2007/09/28 12:21:57 tom Exp $ */
-
-/*************************************************
-*     Exim - an Internet mail transport agent    *
-*************************************************/
-
-/* Copyright (c) University of Cambridge 1995 - 2007 */
-/* See the file NOTICE for conditions of use and distribution. */
-
-/* Header for the DKIM lookup */
-
-extern void *dkim_open(uschar *, uschar **);
-extern int   dkim_find(void *, uschar *, uschar *, int, uschar **, uschar **,
-               BOOL *);
-
-/* End of lookups/dkim.h */
index ab3bc43..c81671a 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/lookups/dnsdb.c,v 1.17 2007/01/08 10:50:19 ph10 Exp $ */
+/* $Cambridge: exim/src/src/lookups/dnsdb.c,v 1.18 2009/06/10 07:34:05 tom Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -303,8 +303,14 @@ while ((domain = string_nextinlist(&keystring, &sep, buffer, sizeof(buffer)))
 
     if (type == T_TXT)
       {
-      yield = string_cat(yield, &size, &ptr, (uschar *)(rr->data+1),
-        (rr->data)[0]);
+      int data_offset = 0;
+      while (data_offset < rr->size)
+        {
+        uschar chunk_len = (rr->data)[data_offset++];
+        yield = string_cat(yield, &size, &ptr,
+                           (uschar *)((rr->data)+data_offset), chunk_len);
+        data_offset += chunk_len;
+        }
       }
     else   /* T_CNAME, T_CSA, T_MX, T_MXH, T_NS, T_PTR, T_SRV */
       {
index aa4acd1..73944a3 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/macros.h,v 1.37 2008/09/29 11:41:07 nm4 Exp $ */
+/* $Cambridge: exim/src/src/macros.h,v 1.38 2009/06/10 07:34:04 tom Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -799,6 +799,7 @@ enum { ACL_WHERE_RCPT,       /* Some controls are for RCPT only */
        ACL_WHERE_MAIL,       /* )                                           */
        ACL_WHERE_PREDATA,    /* ) There are several tests for "in message", */
        ACL_WHERE_MIME,       /* ) implemented by <= WHERE_NOTSMTP           */
+       ACL_WHERE_DKIM,       /* )                                           */
        ACL_WHERE_DATA,       /* )                                           */
        ACL_WHERE_NOTSMTP,    /* )                                           */
 
diff --git a/src/src/pdkim/Makefile b/src/src/pdkim/Makefile
new file mode 100644 (file)
index 0000000..a2bf6b0
--- /dev/null
@@ -0,0 +1,22 @@
+# $Cambridge: exim/src/src/pdkim/Makefile,v 1.2 2009/06/10 07:34:05 tom Exp $
+
+OBJ = base64.o bignum.o pdkim.o rsa.o sha1.o sha2.o
+
+pdkim.a:         $(OBJ)
+                @$(RM_COMMAND) -f pdkim.a
+                @echo "$(AR) pdkim.a"
+                $(FE)$(AR) pdkim.a $(OBJ)
+                $(RANLIB) $@
+
+.SUFFIXES:       .o .c
+.c.o:;           @echo "$(CC) $*.c"
+                $(FE)$(CC) -c $(CFLAGS) $(INCLUDE) $*.c
+
+base64.o:           $(HDRS) base64.c
+bignum.o:           $(HDRS) bignum.c
+pdkim.o:            $(HDRS) pdkim.c
+rsa.o:              $(HDRS) rsa.c
+sha1.o:             $(HDRS) sha1.c
+sha2.o:             $(HDRS) sha2.c
+
+# End
diff --git a/src/src/pdkim/README b/src/src/pdkim/README
new file mode 100644 (file)
index 0000000..cd01065
--- /dev/null
@@ -0,0 +1,13 @@
+$Cambridge: exim/src/src/pdkim/README,v 1.2 2009/06/10 07:34:05 tom Exp $
+
+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
+
+This copy of PDKIM is included with Exim. For a standalone distribution,
+visit http://duncanthrax.net/pdkim/.
diff --git a/src/src/pdkim/base64.c b/src/src/pdkim/base64.c
new file mode 100644 (file)
index 0000000..385d877
--- /dev/null
@@ -0,0 +1,180 @@
+/*
+ *  RFC 1521 base64 encoding/decoding
+ *
+ *  Based on XySSL: Copyright (C) 2006-2008  Christophe Devine
+ *
+ *  Copyright (C) 2009  Paul Bakker <polarssl_maintainer at polarssl dot org>
+ *
+ *  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.
+ */
+
+/* $Cambridge: exim/src/src/pdkim/base64.c,v 1.2 2009/06/10 07:34:05 tom Exp $ */
+
+#include "base64.h"
+
+static const unsigned char base64_enc_map[64] =
+{
+    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
+    'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
+    'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
+    'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
+    'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
+    'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
+    '8', '9', '+', '/'
+};
+
+static const unsigned char base64_dec_map[128] =
+{
+    127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
+    127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
+    127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
+    127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
+    127, 127, 127,  62, 127, 127, 127,  63,  52,  53,
+     54,  55,  56,  57,  58,  59,  60,  61, 127, 127,
+    127,  64, 127, 127, 127,   0,   1,   2,   3,   4,
+      5,   6,   7,   8,   9,  10,  11,  12,  13,  14,
+     15,  16,  17,  18,  19,  20,  21,  22,  23,  24,
+     25, 127, 127, 127, 127, 127, 127,  26,  27,  28,
+     29,  30,  31,  32,  33,  34,  35,  36,  37,  38,
+     39,  40,  41,  42,  43,  44,  45,  46,  47,  48,
+     49,  50,  51, 127, 127, 127, 127, 127
+};
+
+/*
+ * Encode a buffer into base64 format
+ */
+int base64_encode( unsigned char *dst, int *dlen,
+                   unsigned char *src, int  slen )
+{
+    int i, n;
+    int C1, C2, C3;
+    unsigned char *p;
+
+    if( slen == 0 )
+        return( 0 );
+
+    n = (slen << 3) / 6;
+
+    switch( (slen << 3) - (n * 6) )
+    {
+        case  2: n += 3; break;
+        case  4: n += 2; break;
+        default: break;
+    }
+
+    if( *dlen < n + 1 )
+    {
+        *dlen = n + 1;
+        return( POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL );
+    }
+
+    n = (slen / 3) * 3;
+
+    for( i = 0, p = dst; i < n; i += 3 )
+    {
+        C1 = *src++;
+        C2 = *src++;
+        C3 = *src++;
+
+        *p++ = base64_enc_map[(C1 >> 2) & 0x3F];
+        *p++ = base64_enc_map[(((C1 &  3) << 4) + (C2 >> 4)) & 0x3F];
+        *p++ = base64_enc_map[(((C2 & 15) << 2) + (C3 >> 6)) & 0x3F];
+        *p++ = base64_enc_map[C3 & 0x3F];
+    }
+
+    if( i < slen )
+    {
+        C1 = *src++;
+        C2 = ((i + 1) < slen) ? *src++ : 0;
+
+        *p++ = base64_enc_map[(C1 >> 2) & 0x3F];
+        *p++ = base64_enc_map[(((C1 & 3) << 4) + (C2 >> 4)) & 0x3F];
+
+        if( (i + 1) < slen )
+             *p++ = base64_enc_map[((C2 & 15) << 2) & 0x3F];
+        else *p++ = '=';
+
+        *p++ = '=';
+    }
+
+    *dlen = p - dst;
+    *p = 0;
+
+    return( 0 );
+}
+
+/*
+ * Decode a base64-formatted buffer
+ */
+int base64_decode( unsigned char *dst, int *dlen,
+                   unsigned char *src, int  slen )
+{
+    int i, j, n;
+    unsigned long x;
+    unsigned char *p;
+
+    for( i = j = n = 0; i < slen; i++ )
+    {
+        if( ( slen - i ) >= 2 &&
+            src[i] == '\r' && src[i + 1] == '\n' )
+            continue;
+
+        if( src[i] == '\n' )
+            continue;
+
+        if( src[i] == '=' && ++j > 2 )
+            return( POLARSSL_ERR_BASE64_INVALID_CHARACTER );
+
+        if( src[i] > 127 || base64_dec_map[src[i]] == 127 )
+            return( POLARSSL_ERR_BASE64_INVALID_CHARACTER );
+
+        if( base64_dec_map[src[i]] < 64 && j != 0 )
+            return( POLARSSL_ERR_BASE64_INVALID_CHARACTER );
+
+        n++;
+    }
+
+    if( n == 0 )
+        return( 0 );
+
+    n = ((n * 6) + 7) >> 3;
+
+    if( *dlen < n )
+    {
+        *dlen = n;
+        return( POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL );
+    }
+
+   for( j = 3, n = x = 0, p = dst; i > 0; i--, src++ )
+   {
+        if( *src == '\r' || *src == '\n' )
+            continue;
+
+        j -= ( base64_dec_map[*src] == 64 );
+        x  = (x << 6) | ( base64_dec_map[*src] & 0x3F );
+
+        if( ++n == 4 )
+        {
+            n = 0;
+            if( j > 0 ) *p++ = (unsigned char)( x >> 16 );
+            if( j > 1 ) *p++ = (unsigned char)( x >>  8 );
+            if( j > 2 ) *p++ = (unsigned char)( x       );
+        }
+    }
+
+    *dlen = p - dst;
+
+    return( 0 );
+}
diff --git a/src/src/pdkim/base64.h b/src/src/pdkim/base64.h
new file mode 100644 (file)
index 0000000..b5ac98c
--- /dev/null
@@ -0,0 +1,76 @@
+/**
+ * \file base64.h
+ *
+ *  Based on XySSL: Copyright (C) 2006-2008  Christophe Devine
+ *
+ *  Copyright (C) 2009  Paul Bakker <polarssl_maintainer at polarssl dot org>
+ *
+ *  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.
+ */
+
+/* $Cambridge: exim/src/src/pdkim/base64.h,v 1.2 2009/06/10 07:34:05 tom Exp $ */
+
+#ifndef POLARSSL_BASE64_H
+#define POLARSSL_BASE64_H
+
+#define POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL               -0x0010
+#define POLARSSL_ERR_BASE64_INVALID_CHARACTER              -0x0012
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief          Encode a buffer into base64 format
+ *
+ * \param dst      destination buffer
+ * \param dlen     size of the buffer
+ * \param src      source buffer
+ * \param slen     amount of data to be encoded
+ *
+ * \return         0 if successful, or POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL.
+ *                 *dlen is always updated to reflect the amount
+ *                 of data that has (or would have) been written.
+ *
+ * \note           Call this function with *dlen = 0 to obtain the
+ *                 required buffer size in *dlen
+ */
+int base64_encode( unsigned char *dst, int *dlen,
+                   unsigned char *src, int  slen );
+
+/**
+ * \brief          Decode a base64-formatted buffer
+ *
+ * \param dst      destination buffer
+ * \param dlen     size of the buffer
+ * \param src      source buffer
+ * \param slen     amount of data to be decoded
+ *
+ * \return         0 if successful, POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL, or
+ *                 POLARSSL_ERR_BASE64_INVALID_DATA if the input data is not
+ *                 correct. *dlen is always updated to reflect the amount
+ *                 of data that has (or would have) been written.
+ *
+ * \note           Call this function with *dlen = 0 to obtain the
+ *                 required buffer size in *dlen
+ */
+int base64_decode( unsigned char *dst, int *dlen,
+                   unsigned char *src, int  slen );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* base64.h */
diff --git a/src/src/pdkim/bignum.c b/src/src/pdkim/bignum.c
new file mode 100644 (file)
index 0000000..23d7996
--- /dev/null
@@ -0,0 +1,1813 @@
+/*
+ *  Multi-precision integer library
+ *
+ *  Based on XySSL: Copyright (C) 2006-2008  Christophe Devine
+ *
+ *  Copyright (C) 2009  Paul Bakker <polarssl_maintainer at polarssl dot org>
+ *
+ *  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.
+ */
+/*
+ *  This MPI implementation is based on:
+ *
+ *  http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf
+ *  http://www.stillhq.com/extracted/gnupg-api/mpi/
+ *  http://math.libtomcrypt.com/files/tommath.pdf
+ */
+
+/* $Cambridge: exim/src/src/pdkim/bignum.c,v 1.2 2009/06/10 07:34:05 tom Exp $ */
+
+#include "bignum.h"
+#include "bn_mul.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#define ciL    ((int) sizeof(t_int))    /* chars in limb  */
+#define biL    (ciL << 3)               /* bits  in limb  */
+#define biH    (ciL << 2)               /* half limb size */
+
+/*
+ * Convert between bits/chars and number of limbs
+ */
+#define BITS_TO_LIMBS(i)  (((i) + biL - 1) / biL)
+#define CHARS_TO_LIMBS(i) (((i) + ciL - 1) / ciL)
+
+/*
+ * Initialize one or more mpi
+ */
+void mpi_init( mpi *X, ... )
+{
+    va_list args;
+
+    va_start( args, X );
+
+    while( X != NULL )
+    {
+        X->s = 1;
+        X->n = 0;
+        X->p = NULL;
+
+        X = va_arg( args, mpi* );
+    }
+
+    va_end( args );
+}
+
+/*
+ * Unallocate one or more mpi
+ */
+void mpi_free( mpi *X, ... )
+{
+    va_list args;
+
+    va_start( args, X );
+
+    while( X != NULL )
+    {
+        if( X->p != NULL )
+        {
+            memset( X->p, 0, X->n * ciL );
+            free( X->p );
+        }
+
+        X->s = 1;
+        X->n = 0;
+        X->p = NULL;
+
+        X = va_arg( args, mpi* );
+    }
+
+    va_end( args );
+}
+
+/*
+ * Enlarge to the specified number of limbs
+ */
+int mpi_grow( mpi *X, int nblimbs )
+{
+    t_int *p;
+
+    if( X->n < nblimbs )
+    {
+        if( ( p = (t_int *) malloc( nblimbs * ciL ) ) == NULL )
+            return( 1 );
+
+        memset( p, 0, nblimbs * ciL );
+
+        if( X->p != NULL )
+        {
+            memcpy( p, X->p, X->n * ciL );
+            memset( X->p, 0, X->n * ciL );
+            free( X->p );
+        }
+
+        X->n = nblimbs;
+        X->p = p;
+    }
+
+    return( 0 );
+}
+
+/*
+ * Copy the contents of Y into X
+ */
+int mpi_copy( mpi *X, mpi *Y )
+{
+    int ret, i;
+
+    if( X == Y )
+        return( 0 );
+
+    for( i = Y->n - 1; i > 0; i-- )
+        if( Y->p[i] != 0 )
+            break;
+    i++;
+
+    X->s = Y->s;
+
+    MPI_CHK( mpi_grow( X, i ) );
+
+    memset( X->p, 0, X->n * ciL );
+    memcpy( X->p, Y->p, i * ciL );
+
+cleanup:
+
+    return( ret );
+}
+
+/*
+ * Swap the contents of X and Y
+ */
+void mpi_swap( mpi *X, mpi *Y )
+{
+    mpi T;
+
+    memcpy( &T,  X, sizeof( mpi ) );
+    memcpy(  X,  Y, sizeof( mpi ) );
+    memcpy(  Y, &T, sizeof( mpi ) );
+}
+
+/*
+ * Set value from integer
+ */
+int mpi_lset( mpi *X, int z )
+{
+    int ret;
+
+    MPI_CHK( mpi_grow( X, 1 ) );
+    memset( X->p, 0, X->n * ciL );
+
+    X->p[0] = ( z < 0 ) ? -z : z;
+    X->s    = ( z < 0 ) ? -1 : 1;
+
+cleanup:
+
+    return( ret );
+}
+
+/*
+ * Return the number of least significant bits
+ */
+int mpi_lsb( mpi *X )
+{
+    int i, j, count = 0;
+
+    for( i = 0; i < X->n; i++ )
+        for( j = 0; j < (int) biL; j++, count++ )
+            if( ( ( X->p[i] >> j ) & 1 ) != 0 )
+                return( count );
+
+    return( 0 );
+}
+
+/*
+ * Return the number of most significant bits
+ */
+int mpi_msb( mpi *X )
+{
+    int i, j;
+
+    for( i = X->n - 1; i > 0; i-- )
+        if( X->p[i] != 0 )
+            break;
+
+    for( j = biL - 1; j >= 0; j-- )
+        if( ( ( X->p[i] >> j ) & 1 ) != 0 )
+            break;
+
+    return( ( i * biL ) + j + 1 );
+}
+
+/*
+ * Return the total size in bytes
+ */
+int mpi_size( mpi *X )
+{
+    return( ( mpi_msb( X ) + 7 ) >> 3 );
+}
+
+/*
+ * Convert an ASCII character to digit value
+ */
+static int mpi_get_digit( t_int *d, int radix, char c )
+{
+    *d = 255;
+
+    if( c >= 0x30 && c <= 0x39 ) *d = c - 0x30;
+    if( c >= 0x41 && c <= 0x46 ) *d = c - 0x37;
+    if( c >= 0x61 && c <= 0x66 ) *d = c - 0x57;
+
+    if( *d >= (t_int) radix )
+        return( POLARSSL_ERR_MPI_INVALID_CHARACTER );
+
+    return( 0 );
+}
+
+/*
+ * Import from an ASCII string
+ */
+int mpi_read_string( mpi *X, int radix, char *s )
+{
+    int ret, i, j, n;
+    t_int d;
+    mpi T;
+
+    if( radix < 2 || radix > 16 )
+        return( POLARSSL_ERR_MPI_BAD_INPUT_DATA );
+
+    mpi_init( &T, NULL );
+
+    if( radix == 16 )
+    {
+        n = BITS_TO_LIMBS( strlen( s ) << 2 );
+
+        MPI_CHK( mpi_grow( X, n ) );
+        MPI_CHK( mpi_lset( X, 0 ) );
+
+        for( i = strlen( s ) - 1, j = 0; i >= 0; i--, j++ )
+        {
+            if( i == 0 && s[i] == '-' )
+            {
+                X->s = -1;
+                break;
+            }
+
+            MPI_CHK( mpi_get_digit( &d, radix, s[i] ) );
+            X->p[j / (2 * ciL)] |= d << ( (j % (2 * ciL)) << 2 );
+        }
+    }
+    else
+    {
+        MPI_CHK( mpi_lset( X, 0 ) );
+
+        for( i = 0; i < (int) strlen( s ); i++ )
+        {
+            if( i == 0 && s[i] == '-' )
+            {
+                X->s = -1;
+                continue;
+            }
+
+            MPI_CHK( mpi_get_digit( &d, radix, s[i] ) );
+            MPI_CHK( mpi_mul_int( &T, X, radix ) );
+            MPI_CHK( mpi_add_int( X, &T, d ) );
+        }
+    }
+
+cleanup:
+
+    mpi_free( &T, NULL );
+
+    return( ret );
+}
+
+/*
+ * Helper to write the digits high-order first
+ */
+static int mpi_write_hlp( mpi *X, int radix, char **p )
+{
+    int ret;
+    t_int r;
+
+    if( radix < 2 || radix > 16 )
+        return( POLARSSL_ERR_MPI_BAD_INPUT_DATA );
+
+    MPI_CHK( mpi_mod_int( &r, X, radix ) );
+    MPI_CHK( mpi_div_int( X, NULL, X, radix ) );
+
+    if( mpi_cmp_int( X, 0 ) != 0 )
+        MPI_CHK( mpi_write_hlp( X, radix, p ) );
+
+    if( r < 10 )
+        *(*p)++ = (char)( r + 0x30 );
+    else
+        *(*p)++ = (char)( r + 0x37 );
+
+cleanup:
+
+    return( ret );
+}
+
+/*
+ * Export into an ASCII string
+ */
+int mpi_write_string( mpi *X, int radix, char *s, int *slen )
+{
+    int ret = 0, n;
+    char *p;
+    mpi T;
+
+    if( radix < 2 || radix > 16 )
+        return( POLARSSL_ERR_MPI_BAD_INPUT_DATA );
+
+    n = mpi_msb( X );
+    if( radix >=  4 ) n >>= 1;
+    if( radix >= 16 ) n >>= 1;
+    n += 3;
+
+    if( *slen < n )
+    {
+        *slen = n;
+        return( POLARSSL_ERR_MPI_BUFFER_TOO_SMALL );
+    }
+
+    p = s;
+    mpi_init( &T, NULL );
+
+    if( X->s == -1 )
+        *p++ = '-';
+
+    if( radix == 16 )
+    {
+        int c, i, j, k;
+
+        for( i = X->n - 1, k = 0; i >= 0; i-- )
+        {
+            for( j = ciL - 1; j >= 0; j-- )
+            {
+                c = ( X->p[i] >> (j << 3) ) & 0xFF;
+
+                if( c == 0 && k == 0 && (i + j) != 0 )
+                    continue;
+
+                p += sprintf( p, "%02X", c );
+                k = 1;
+            }
+        }
+    }
+    else
+    {
+        MPI_CHK( mpi_copy( &T, X ) );
+        MPI_CHK( mpi_write_hlp( &T, radix, &p ) );
+    }
+
+    *p++ = '\0';
+    *slen = p - s;
+
+cleanup:
+
+    mpi_free( &T, NULL );
+
+    return( ret );
+}
+
+/*
+ * Read X from an opened file
+ */
+int mpi_read_file( mpi *X, int radix, FILE *fin )
+{
+    t_int d;
+    int slen;
+    char *p;
+    char s[1024];
+
+    memset( s, 0, sizeof( s ) );
+    if( fgets( s, sizeof( s ) - 1, fin ) == NULL )
+        return( POLARSSL_ERR_MPI_FILE_IO_ERROR );
+
+    slen = strlen( s );
+    if( s[slen - 1] == '\n' ) { slen--; s[slen] = '\0'; }
+    if( s[slen - 1] == '\r' ) { slen--; s[slen] = '\0'; }
+
+    p = s + slen;
+    while( --p >= s )
+        if( mpi_get_digit( &d, radix, *p ) != 0 )
+            break;
+
+    return( mpi_read_string( X, radix, p + 1 ) );
+}
+
+/*
+ * Write X into an opened file (or stdout if fout == NULL)
+ */
+int mpi_write_file( char *p, mpi *X, int radix, FILE *fout )
+{
+    int n, ret;
+    size_t slen;
+    size_t plen;
+    char s[1024];
+
+    n = sizeof( s );
+    memset( s, 0, n );
+    n -= 2;
+
+    MPI_CHK( mpi_write_string( X, radix, s, (int *) &n ) );
+
+    if( p == NULL ) p = "";
+
+    plen = strlen( p );
+    slen = strlen( s );
+    s[slen++] = '\r';
+    s[slen++] = '\n';
+
+    if( fout != NULL )
+    {
+        if( fwrite( p, 1, plen, fout ) != plen ||
+            fwrite( s, 1, slen, fout ) != slen )
+            return( POLARSSL_ERR_MPI_FILE_IO_ERROR );
+    }
+    else
+        printf( "%s%s", p, s );
+
+cleanup:
+
+    return( ret );
+}
+
+/*
+ * Import X from unsigned binary data, big endian
+ */
+int mpi_read_binary( mpi *X, unsigned char *buf, int buflen )
+{
+    int ret, i, j, n;
+
+    for( n = 0; n < buflen; n++ )
+        if( buf[n] != 0 )
+            break;
+
+    MPI_CHK( mpi_grow( X, CHARS_TO_LIMBS( buflen - n ) ) );
+    MPI_CHK( mpi_lset( X, 0 ) );
+
+    for( i = buflen - 1, j = 0; i >= n; i--, j++ )
+        X->p[j / ciL] |= ((t_int) buf[i]) << ((j % ciL) << 3);
+
+cleanup:
+
+    return( ret );
+}
+
+/*
+ * Export X into unsigned binary data, big endian
+ */
+int mpi_write_binary( mpi *X, unsigned char *buf, int buflen )
+{
+    int i, j, n;
+
+    n = mpi_size( X );
+
+    if( buflen < n )
+        return( POLARSSL_ERR_MPI_BUFFER_TOO_SMALL );
+
+    memset( buf, 0, buflen );
+
+    for( i = buflen - 1, j = 0; n > 0; i--, j++, n-- )
+        buf[i] = (unsigned char)( X->p[j / ciL] >> ((j % ciL) << 3) );
+
+    return( 0 );
+}
+
+/*
+ * Left-shift: X <<= count
+ */
+int mpi_shift_l( mpi *X, int count )
+{
+    int ret, i, v0, t1;
+    t_int r0 = 0, r1;
+
+    v0 = count / (biL    );
+    t1 = count & (biL - 1);
+
+    i = mpi_msb( X ) + count;
+
+    if( X->n * (int) biL < i )
+        MPI_CHK( mpi_grow( X, BITS_TO_LIMBS( i ) ) );
+
+    ret = 0;
+
+    /*
+     * shift by count / limb_size
+     */
+    if( v0 > 0 )
+    {
+        for( i = X->n - 1; i >= v0; i-- )
+            X->p[i] = X->p[i - v0];
+
+        for( ; i >= 0; i-- )
+            X->p[i] = 0;
+    }
+
+    /*
+     * shift by count % limb_size
+     */
+    if( t1 > 0 )
+    {
+        for( i = v0; i < X->n; i++ )
+        {
+            r1 = X->p[i] >> (biL - t1);
+            X->p[i] <<= t1;
+            X->p[i] |= r0;
+            r0 = r1;
+        }
+    }
+
+cleanup:
+
+    return( ret );
+}
+
+/*
+ * Right-shift: X >>= count
+ */
+int mpi_shift_r( mpi *X, int count )
+{
+    int i, v0, v1;
+    t_int r0 = 0, r1;
+
+    v0 = count /  biL;
+    v1 = count & (biL - 1);
+
+    /*
+     * shift by count / limb_size
+     */
+    if( v0 > 0 )
+    {
+        for( i = 0; i < X->n - v0; i++ )
+            X->p[i] = X->p[i + v0];
+
+        for( ; i < X->n; i++ )
+            X->p[i] = 0;
+    }
+
+    /*
+     * shift by count % limb_size
+     */
+    if( v1 > 0 )
+    {
+        for( i = X->n - 1; i >= 0; i-- )
+        {
+            r1 = X->p[i] << (biL - v1);
+            X->p[i] >>= v1;
+            X->p[i] |= r0;
+            r0 = r1;
+        }
+    }
+
+    return( 0 );
+}
+
+/*
+ * Compare unsigned values
+ */
+int mpi_cmp_abs( mpi *X, mpi *Y )
+{
+    int i, j;
+
+    for( i = X->n - 1; i >= 0; i-- )
+        if( X->p[i] != 0 )
+            break;
+
+    for( j = Y->n - 1; j >= 0; j-- )
+        if( Y->p[j] != 0 )
+            break;
+
+    if( i < 0 && j < 0 )
+        return( 0 );
+
+    if( i > j ) return(  1 );
+    if( j > i ) return( -1 );
+
+    for( ; i >= 0; i-- )
+    {
+        if( X->p[i] > Y->p[i] ) return(  1 );
+        if( X->p[i] < Y->p[i] ) return( -1 );
+    }
+
+    return( 0 );
+}
+
+/*
+ * Compare signed values
+ */
+int mpi_cmp_mpi( mpi *X, mpi *Y )
+{
+    int i, j;
+
+    for( i = X->n - 1; i >= 0; i-- )
+        if( X->p[i] != 0 )
+            break;
+
+    for( j = Y->n - 1; j >= 0; j-- )
+        if( Y->p[j] != 0 )
+            break;
+
+    if( i < 0 && j < 0 )
+        return( 0 );
+
+    if( i > j ) return(  X->s );
+    if( j > i ) return( -X->s );
+
+    if( X->s > 0 && Y->s < 0 ) return(  1 );
+    if( Y->s > 0 && X->s < 0 ) return( -1 );
+
+    for( ; i >= 0; i-- )
+    {
+        if( X->p[i] > Y->p[i] ) return(  X->s );
+        if( X->p[i] < Y->p[i] ) return( -X->s );
+    }
+
+    return( 0 );
+}
+
+/*
+ * Compare signed values
+ */
+int mpi_cmp_int( mpi *X, int z )
+{
+    mpi Y;
+    t_int p[1];
+
+    *p  = ( z < 0 ) ? -z : z;
+    Y.s = ( z < 0 ) ? -1 : 1;
+    Y.n = 1;
+    Y.p = p;
+
+    return( mpi_cmp_mpi( X, &Y ) );
+}
+
+/*
+ * Unsigned addition: X = |A| + |B|  (HAC 14.7)
+ */
+int mpi_add_abs( mpi *X, mpi *A, mpi *B )
+{
+    int ret, i, j;
+    t_int *o, *p, c;
+
+    if( X == B )
+    {
+        mpi *T = A; A = X; B = T;
+    }
+
+    if( X != A )
+        MPI_CHK( mpi_copy( X, A ) );
+
+    for( j = B->n - 1; j >= 0; j-- )
+        if( B->p[j] != 0 )
+            break;
+
+    MPI_CHK( mpi_grow( X, j + 1 ) );
+
+    o = B->p; p = X->p; c = 0;
+
+    for( i = 0; i <= j; i++, o++, p++ )
+    {
+        *p +=  c; c  = ( *p <  c );
+        *p += *o; c += ( *p < *o );
+    }
+
+    while( c != 0 )
+    {
+        if( i >= X->n )
+        {
+            MPI_CHK( mpi_grow( X, i + 1 ) );
+            p = X->p + i;
+        }
+
+        *p += c; c = ( *p < c ); i++;
+    }
+
+cleanup:
+
+    return( ret );
+}
+
+/*
+ * Helper for mpi substraction
+ */
+static void mpi_sub_hlp( int n, t_int *s, t_int *d )
+{
+    int i;
+    t_int c, z;
+
+    for( i = c = 0; i < n; i++, s++, d++ )
+    {
+        z = ( *d <  c );     *d -=  c;
+        c = ( *d < *s ) + z; *d -= *s;
+    }
+
+    while( c != 0 )
+    {
+        z = ( *d < c ); *d -= c;
+        c = z; i++; d++;
+    }
+}
+
+/*
+ * Unsigned substraction: X = |A| - |B|  (HAC 14.9)
+ */
+int mpi_sub_abs( mpi *X, mpi *A, mpi *B )
+{
+    mpi TB;
+    int ret, n;
+
+    if( mpi_cmp_abs( A, B ) < 0 )
+        return( POLARSSL_ERR_MPI_NEGATIVE_VALUE );
+
+    mpi_init( &TB, NULL );
+
+    if( X == B )
+    {
+        MPI_CHK( mpi_copy( &TB, B ) );
+        B = &TB;
+    }
+
+    if( X != A )
+        MPI_CHK( mpi_copy( X, A ) );
+
+    ret = 0;
+
+    for( n = B->n - 1; n >= 0; n-- )
+        if( B->p[n] != 0 )
+            break;
+
+    mpi_sub_hlp( n + 1, B->p, X->p );
+
+cleanup:
+
+    mpi_free( &TB, NULL );
+
+    return( ret );
+}
+
+/*
+ * Signed addition: X = A + B
+ */
+int mpi_add_mpi( mpi *X, mpi *A, mpi *B )
+{
+    int ret, s = A->s;
+
+    if( A->s * B->s < 0 )
+    {
+        if( mpi_cmp_abs( A, B ) >= 0 )
+        {
+            MPI_CHK( mpi_sub_abs( X, A, B ) );
+            X->s =  s;
+        }
+        else
+        {
+            MPI_CHK( mpi_sub_abs( X, B, A ) );
+            X->s = -s;
+        }
+    }
+    else
+    {
+        MPI_CHK( mpi_add_abs( X, A, B ) );
+        X->s = s;
+    }
+
+cleanup:
+
+    return( ret );
+}
+
+/*
+ * Signed substraction: X = A - B
+ */
+int mpi_sub_mpi( mpi *X, mpi *A, mpi *B )
+{
+    int ret, s = A->s;
+
+    if( A->s * B->s > 0 )
+    {
+        if( mpi_cmp_abs( A, B ) >= 0 )
+        {
+            MPI_CHK( mpi_sub_abs( X, A, B ) );
+            X->s =  s;
+        }
+        else
+        {
+            MPI_CHK( mpi_sub_abs( X, B, A ) );
+            X->s = -s;
+        }
+    }
+    else
+    {
+        MPI_CHK( mpi_add_abs( X, A, B ) );
+        X->s = s;
+    }
+
+cleanup:
+
+    return( ret );
+}
+
+/*
+ * Signed addition: X = A + b
+ */
+int mpi_add_int( mpi *X, mpi *A, int b )
+{
+    mpi _B;
+    t_int p[1];
+
+    p[0] = ( b < 0 ) ? -b : b;
+    _B.s = ( b < 0 ) ? -1 : 1;
+    _B.n = 1;
+    _B.p = p;
+
+    return( mpi_add_mpi( X, A, &_B ) );
+}
+
+/*
+ * Signed substraction: X = A - b
+ */
+int mpi_sub_int( mpi *X, mpi *A, int b )
+{
+    mpi _B;
+    t_int p[1];
+
+    p[0] = ( b < 0 ) ? -b : b;
+    _B.s = ( b < 0 ) ? -1 : 1;
+    _B.n = 1;
+    _B.p = p;
+
+    return( mpi_sub_mpi( X, A, &_B ) );
+}
+
+/*
+ * Helper for mpi multiplication
+ */
+static void mpi_mul_hlp( int i, t_int *s, t_int *d, t_int b )
+{
+    t_int c = 0, t = 0;
+
+#if defined(MULADDC_HUIT)
+    for( ; i >= 8; i -= 8 )
+    {
+        MULADDC_INIT
+        MULADDC_HUIT
+        MULADDC_STOP
+    }
+
+    for( ; i > 0; i-- )
+    {
+        MULADDC_INIT
+        MULADDC_CORE
+        MULADDC_STOP
+    }
+#else
+    for( ; i >= 16; i -= 16 )
+    {
+        MULADDC_INIT
+        MULADDC_CORE   MULADDC_CORE
+        MULADDC_CORE   MULADDC_CORE
+        MULADDC_CORE   MULADDC_CORE
+        MULADDC_CORE   MULADDC_CORE
+
+        MULADDC_CORE   MULADDC_CORE
+        MULADDC_CORE   MULADDC_CORE
+        MULADDC_CORE   MULADDC_CORE
+        MULADDC_CORE   MULADDC_CORE
+        MULADDC_STOP
+    }
+
+    for( ; i >= 8; i -= 8 )
+    {
+        MULADDC_INIT
+        MULADDC_CORE   MULADDC_CORE
+        MULADDC_CORE   MULADDC_CORE
+
+        MULADDC_CORE   MULADDC_CORE
+        MULADDC_CORE   MULADDC_CORE
+        MULADDC_STOP
+    }
+
+    for( ; i > 0; i-- )
+    {
+        MULADDC_INIT
+        MULADDC_CORE
+        MULADDC_STOP
+    }
+#endif
+
+    t++;
+
+    do {
+        *d += c; c = ( *d < c ); d++;
+    }
+    while( c != 0 );
+}
+
+/*
+ * Baseline multiplication: X = A * B  (HAC 14.12)
+ */
+int mpi_mul_mpi( mpi *X, mpi *A, mpi *B )
+{
+    int ret, i, j;
+    mpi TA, TB;
+
+    mpi_init( &TA, &TB, NULL );
+
+    if( X == A ) { MPI_CHK( mpi_copy( &TA, A ) ); A = &TA; }
+    if( X == B ) { MPI_CHK( mpi_copy( &TB, B ) ); B = &TB; }
+
+    for( i = A->n - 1; i >= 0; i-- )
+        if( A->p[i] != 0 )
+            break;
+
+    for( j = B->n - 1; j >= 0; j-- )
+        if( B->p[j] != 0 )
+            break;
+
+    MPI_CHK( mpi_grow( X, i + j + 2 ) );
+    MPI_CHK( mpi_lset( X, 0 ) );
+
+    for( i++; j >= 0; j-- )
+        mpi_mul_hlp( i, A->p, X->p + j, B->p[j] );
+
+    X->s = A->s * B->s;
+
+cleanup:
+
+    mpi_free( &TB, &TA, NULL );
+
+    return( ret );
+}
+
+/*
+ * Baseline multiplication: X = A * b
+ */
+int mpi_mul_int( mpi *X, mpi *A, t_int b )
+{
+    mpi _B;
+    t_int p[1];
+
+    _B.s = 1;
+    _B.n = 1;
+    _B.p = p;
+    p[0] = b;
+
+    return( mpi_mul_mpi( X, A, &_B ) );
+}
+
+/*
+ * Division by mpi: A = Q * B + R  (HAC 14.20)
+ */
+int mpi_div_mpi( mpi *Q, mpi *R, mpi *A, mpi *B )
+{
+    int ret, i, n, t, k;
+    mpi X, Y, Z, T1, T2;
+
+    if( mpi_cmp_int( B, 0 ) == 0 )
+        return( POLARSSL_ERR_MPI_DIVISION_BY_ZERO );
+
+    mpi_init( &X, &Y, &Z, &T1, &T2, NULL );
+
+    if( mpi_cmp_abs( A, B ) < 0 )
+    {
+        if( Q != NULL ) MPI_CHK( mpi_lset( Q, 0 ) );
+        if( R != NULL ) MPI_CHK( mpi_copy( R, A ) );
+        return( 0 );
+    }
+
+    MPI_CHK( mpi_copy( &X, A ) );
+    MPI_CHK( mpi_copy( &Y, B ) );
+    X.s = Y.s = 1;
+
+    MPI_CHK( mpi_grow( &Z, A->n + 2 ) );
+    MPI_CHK( mpi_lset( &Z,  0 ) );
+    MPI_CHK( mpi_grow( &T1, 2 ) );
+    MPI_CHK( mpi_grow( &T2, 3 ) );
+
+    k = mpi_msb( &Y ) % biL;
+    if( k < (int) biL - 1 )
+    {
+        k = biL - 1 - k;
+        MPI_CHK( mpi_shift_l( &X, k ) );
+        MPI_CHK( mpi_shift_l( &Y, k ) );
+    }
+    else k = 0;
+
+    n = X.n - 1;
+    t = Y.n - 1;
+    mpi_shift_l( &Y, biL * (n - t) );
+
+    while( mpi_cmp_mpi( &X, &Y ) >= 0 )
+    {
+        Z.p[n - t]++;
+        mpi_sub_mpi( &X, &X, &Y );
+    }
+    mpi_shift_r( &Y, biL * (n - t) );
+
+    for( i = n; i > t ; i-- )
+    {
+        if( X.p[i] >= Y.p[t] )
+            Z.p[i - t - 1] = ~0;
+        else
+        {
+#if defined(POLARSSL_HAVE_LONGLONG)
+            t_dbl r;
+
+            r  = (t_dbl) X.p[i] << biL;
+            r |= (t_dbl) X.p[i - 1];
+            r /= Y.p[t];
+            if( r > ((t_dbl) 1 << biL) - 1)
+                r = ((t_dbl) 1 << biL) - 1;
+
+            Z.p[i - t - 1] = (t_int) r;
+#else
+            /*
+             * __udiv_qrnnd_c, from gmp/longlong.h
+             */
+            t_int q0, q1, r0, r1;
+            t_int d0, d1, d, m;
+
+            d  = Y.p[t];
+            d0 = ( d << biH ) >> biH;
+            d1 = ( d >> biH );
+
+            q1 = X.p[i] / d1;
+            r1 = X.p[i] - d1 * q1;
+            r1 <<= biH;
+            r1 |= ( X.p[i - 1] >> biH );
+
+            m = q1 * d0;
+            if( r1 < m )
+            {
+                q1--, r1 += d;
+                while( r1 >= d && r1 < m )
+                    q1--, r1 += d;
+            }
+            r1 -= m;
+
+            q0 = r1 / d1;
+            r0 = r1 - d1 * q0;
+            r0 <<= biH;
+            r0 |= ( X.p[i - 1] << biH ) >> biH;
+
+            m = q0 * d0;
+            if( r0 < m )
+            {
+                q0--, r0 += d;
+                while( r0 >= d && r0 < m )
+                    q0--, r0 += d;
+            }
+            r0 -= m;
+
+            Z.p[i - t - 1] = ( q1 << biH ) | q0;
+#endif
+        }
+
+        Z.p[i - t - 1]++;
+        do
+        {
+            Z.p[i - t - 1]--;
+
+            MPI_CHK( mpi_lset( &T1, 0 ) );
+            T1.p[0] = (t < 1) ? 0 : Y.p[t - 1];
+            T1.p[1] = Y.p[t];
+            MPI_CHK( mpi_mul_int( &T1, &T1, Z.p[i - t - 1] ) );
+
+            MPI_CHK( mpi_lset( &T2, 0 ) );
+            T2.p[0] = (i < 2) ? 0 : X.p[i - 2];
+            T2.p[1] = (i < 1) ? 0 : X.p[i - 1];
+            T2.p[2] = X.p[i];
+        }
+        while( mpi_cmp_mpi( &T1, &T2 ) > 0 );
+
+        MPI_CHK( mpi_mul_int( &T1, &Y, Z.p[i - t - 1] ) );
+        MPI_CHK( mpi_shift_l( &T1,  biL * (i - t - 1) ) );
+        MPI_CHK( mpi_sub_mpi( &X, &X, &T1 ) );
+
+        if( mpi_cmp_int( &X, 0 ) < 0 )
+        {
+            MPI_CHK( mpi_copy( &T1, &Y ) );
+            MPI_CHK( mpi_shift_l( &T1, biL * (i - t - 1) ) );
+            MPI_CHK( mpi_add_mpi( &X, &X, &T1 ) );
+            Z.p[i - t - 1]--;
+        }
+    }
+
+    if( Q != NULL )
+    {
+        mpi_copy( Q, &Z );
+        Q->s = A->s * B->s;
+    }
+
+    if( R != NULL )
+    {
+        mpi_shift_r( &X, k );
+        mpi_copy( R, &X );
+
+        R->s = A->s;
+        if( mpi_cmp_int( R, 0 ) == 0 )
+            R->s = 1;
+    }
+
+cleanup:
+
+    mpi_free( &X, &Y, &Z, &T1, &T2, NULL );
+
+    return( ret );
+}
+
+/*
+ * Division by int: A = Q * b + R
+ *
+ * Returns 0 if successful
+ *         1 if memory allocation failed
+ *         POLARSSL_ERR_MPI_DIVISION_BY_ZERO if b == 0
+ */
+int mpi_div_int( mpi *Q, mpi *R, mpi *A, int b )
+{
+    mpi _B;
+    t_int p[1];
+
+    p[0] = ( b < 0 ) ? -b : b;
+    _B.s = ( b < 0 ) ? -1 : 1;
+    _B.n = 1;
+    _B.p = p;
+
+    return( mpi_div_mpi( Q, R, A, &_B ) );
+}
+
+/*
+ * Modulo: R = A mod B
+ */
+int mpi_mod_mpi( mpi *R, mpi *A, mpi *B )
+{
+    int ret;
+
+    MPI_CHK( mpi_div_mpi( NULL, R, A, B ) );
+
+    while( mpi_cmp_int( R, 0 ) < 0 )
+      MPI_CHK( mpi_add_mpi( R, R, B ) );
+
+    while( mpi_cmp_mpi( R, B ) >= 0 )
+      MPI_CHK( mpi_sub_mpi( R, R, B ) );
+
+cleanup:
+
+    return( ret );
+}
+
+/*
+ * Modulo: r = A mod b
+ */
+int mpi_mod_int( t_int *r, mpi *A, int b )
+{
+    int i;
+    t_int x, y, z;
+
+    if( b == 0 )
+        return( POLARSSL_ERR_MPI_DIVISION_BY_ZERO );
+
+    if( b < 0 )
+        b = -b;
+
+    /*
+     * handle trivial cases
+     */
+    if( b == 1 )
+    {
+        *r = 0;
+        return( 0 );
+    }
+
+    if( b == 2 )
+    {
+        *r = A->p[0] & 1;
+        return( 0 );
+    }
+
+    /*
+     * general case
+     */
+    for( i = A->n - 1, y = 0; i >= 0; i-- )
+    {
+        x  = A->p[i];
+        y  = ( y << biH ) | ( x >> biH );
+        z  = y / b;
+        y -= z * b;
+
+        x <<= biH;
+        y  = ( y << biH ) | ( x >> biH );
+        z  = y / b;
+        y -= z * b;
+    }
+
+    *r = y;
+
+    return( 0 );
+}
+
+/*
+ * Fast Montgomery initialization (thanks to Tom St Denis)
+ */
+static void mpi_montg_init( t_int *mm, mpi *N )
+{
+    t_int x, m0 = N->p[0];
+
+    x  = m0;
+    x += ( ( m0 + 2 ) & 4 ) << 1;
+    x *= ( 2 - ( m0 * x ) );
+
+    if( biL >= 16 ) x *= ( 2 - ( m0 * x ) );
+    if( biL >= 32 ) x *= ( 2 - ( m0 * x ) );
+    if( biL >= 64 ) x *= ( 2 - ( m0 * x ) );
+
+    *mm = ~x + 1;
+}
+
+/*
+ * Montgomery multiplication: A = A * B * R^-1 mod N  (HAC 14.36)
+ */
+static void mpi_montmul( mpi *A, mpi *B, mpi *N, t_int mm, mpi *T )
+{
+    int i, n, m;
+    t_int u0, u1, *d;
+
+    memset( T->p, 0, T->n * ciL );
+
+    d = T->p;
+    n = N->n;
+    m = ( B->n < n ) ? B->n : n;
+
+    for( i = 0; i < n; i++ )
+    {
+        /*
+         * T = (T + u0*B + u1*N) / 2^biL
+         */
+        u0 = A->p[i];
+        u1 = ( d[0] + u0 * B->p[0] ) * mm;
+
+        mpi_mul_hlp( m, B->p, d, u0 );
+        mpi_mul_hlp( n, N->p, d, u1 );
+
+        *d++ = u0; d[n + 1] = 0;
+    }
+
+    memcpy( A->p, d, (n + 1) * ciL );
+
+    if( mpi_cmp_abs( A, N ) >= 0 )
+        mpi_sub_hlp( n, N->p, A->p );
+    else
+        /* prevent timing attacks */
+        mpi_sub_hlp( n, A->p, T->p );
+}
+
+/*
+ * Montgomery reduction: A = A * R^-1 mod N
+ */
+static void mpi_montred( mpi *A, mpi *N, t_int mm, mpi *T )
+{
+    t_int z = 1;
+    mpi U;
+
+    U.n = U.s = z;
+    U.p = &z;
+
+    mpi_montmul( A, &U, N, mm, T );
+}
+
+/*
+ * Sliding-window exponentiation: X = A^E mod N  (HAC 14.85)
+ */
+int mpi_exp_mod( mpi *X, mpi *A, mpi *E, mpi *N, mpi *_RR )
+{
+    int ret, i, j, wsize, wbits;
+    int bufsize, nblimbs, nbits;
+    t_int ei, mm, state;
+    mpi RR, T, W[64];
+
+    if( mpi_cmp_int( N, 0 ) < 0 || ( N->p[0] & 1 ) == 0 )
+        return( POLARSSL_ERR_MPI_BAD_INPUT_DATA );
+
+    /*
+     * Init temps and window size
+     */
+    mpi_montg_init( &mm, N );
+    mpi_init( &RR, &T, NULL );
+    memset( W, 0, sizeof( W ) );
+
+    i = mpi_msb( E );
+
+    wsize = ( i > 671 ) ? 6 : ( i > 239 ) ? 5 :
+            ( i >  79 ) ? 4 : ( i >  23 ) ? 3 : 1;
+
+    j = N->n + 1;
+    MPI_CHK( mpi_grow( X, j ) );
+    MPI_CHK( mpi_grow( &W[1],  j ) );
+    MPI_CHK( mpi_grow( &T, j * 2 ) );
+
+    /*
+     * If 1st call, pre-compute R^2 mod N
+     */
+    if( _RR == NULL || _RR->p == NULL )
+    {
+        MPI_CHK( mpi_lset( &RR, 1 ) );
+        MPI_CHK( mpi_shift_l( &RR, N->n * 2 * biL ) );
+        MPI_CHK( mpi_mod_mpi( &RR, &RR, N ) );
+
+        if( _RR != NULL )
+            memcpy( _RR, &RR, sizeof( mpi ) );
+    }
+    else
+        memcpy( &RR, _RR, sizeof( mpi ) );
+
+    /*
+     * W[1] = A * R^2 * R^-1 mod N = A * R mod N
+     */
+    if( mpi_cmp_mpi( A, N ) >= 0 )
+        mpi_mod_mpi( &W[1], A, N );
+    else   mpi_copy( &W[1], A );
+
+    mpi_montmul( &W[1], &RR, N, mm, &T );
+
+    /*
+     * X = R^2 * R^-1 mod N = R mod N
+     */
+    MPI_CHK( mpi_copy( X, &RR ) );
+    mpi_montred( X, N, mm, &T );
+
+    if( wsize > 1 )
+    {
+        /*
+         * W[1 << (wsize - 1)] = W[1] ^ (wsize - 1)
+         */
+        j =  1 << (wsize - 1);
+
+        MPI_CHK( mpi_grow( &W[j], N->n + 1 ) );
+        MPI_CHK( mpi_copy( &W[j], &W[1]    ) );
+
+        for( i = 0; i < wsize - 1; i++ )
+            mpi_montmul( &W[j], &W[j], N, mm, &T );
+
+        /*
+         * W[i] = W[i - 1] * W[1]
+         */
+        for( i = j + 1; i < (1 << wsize); i++ )
+        {
+            MPI_CHK( mpi_grow( &W[i], N->n + 1 ) );
+            MPI_CHK( mpi_copy( &W[i], &W[i - 1] ) );
+
+            mpi_montmul( &W[i], &W[1], N, mm, &T );
+        }
+    }
+
+    nblimbs = E->n;
+    bufsize = 0;
+    nbits   = 0;
+    wbits   = 0;
+    state   = 0;
+
+    while( 1 )
+    {
+        if( bufsize == 0 )
+        {
+            if( nblimbs-- == 0 )
+                break;
+
+            bufsize = sizeof( t_int ) << 3;
+        }
+
+        bufsize--;
+
+        ei = (E->p[nblimbs] >> bufsize) & 1;
+
+        /*
+         * skip leading 0s
+         */
+        if( ei == 0 && state == 0 )
+            continue;
+
+        if( ei == 0 && state == 1 )
+        {
+            /*
+             * out of window, square X
+             */
+            mpi_montmul( X, X, N, mm, &T );
+            continue;
+        }
+
+        /*
+         * add ei to current window
+         */
+        state = 2;
+
+        nbits++;
+        wbits |= (ei << (wsize - nbits));
+
+        if( nbits == wsize )
+        {
+            /*
+             * X = X^wsize R^-1 mod N
+             */
+            for( i = 0; i < wsize; i++ )
+                mpi_montmul( X, X, N, mm, &T );
+
+            /*
+             * X = X * W[wbits] R^-1 mod N
+             */
+            mpi_montmul( X, &W[wbits], N, mm, &T );
+
+            state--;
+            nbits = 0;
+            wbits = 0;
+        }
+    }
+
+    /*
+     * process the remaining bits
+     */
+    for( i = 0; i < nbits; i++ )
+    {
+        mpi_montmul( X, X, N, mm, &T );
+
+        wbits <<= 1;
+
+        if( (wbits & (1 << wsize)) != 0 )
+            mpi_montmul( X, &W[1], N, mm, &T );
+    }
+
+    /*
+     * X = A^E * R * R^-1 mod N = A^E mod N
+     */
+    mpi_montred( X, N, mm, &T );
+
+cleanup:
+
+    for( i = (1 << (wsize - 1)); i < (1 << wsize); i++ )
+        mpi_free( &W[i], NULL );
+
+    if( _RR != NULL )
+         mpi_free( &W[1], &T, NULL );
+    else mpi_free( &W[1], &T, &RR, NULL );
+
+    return( ret );
+}
+
+/*
+ * Greatest common divisor: G = gcd(A, B)  (HAC 14.54)
+ */
+int mpi_gcd( mpi *G, mpi *A, mpi *B )
+{
+    int ret;
+    mpi TG, TA, TB;
+
+    mpi_init( &TG, &TA, &TB, NULL );
+
+    MPI_CHK( mpi_lset( &TG, 1 ) );
+    MPI_CHK( mpi_copy( &TA, A ) );
+    MPI_CHK( mpi_copy( &TB, B ) );
+
+    TA.s = TB.s = 1;
+
+    while( mpi_cmp_int( &TA, 0 ) != 0 )
+    {
+        while( ( TA.p[0] & 1 ) == 0 ) MPI_CHK( mpi_shift_r( &TA, 1 ) );
+        while( ( TB.p[0] & 1 ) == 0 ) MPI_CHK( mpi_shift_r( &TB, 1 ) );
+
+        if( mpi_cmp_mpi( &TA, &TB ) >= 0 )
+        {
+            MPI_CHK( mpi_sub_abs( &TA, &TA, &TB ) );
+            MPI_CHK( mpi_shift_r( &TA, 1 ) );
+        }
+        else
+        {
+            MPI_CHK( mpi_sub_abs( &TB, &TB, &TA ) );
+            MPI_CHK( mpi_shift_r( &TB, 1 ) );
+        }
+    }
+
+    MPI_CHK( mpi_mul_mpi( G, &TG, &TB ) );
+
+cleanup:
+
+    mpi_free( &TB, &TA, &TG, NULL );
+
+    return( ret );
+}
+
+/*
+ * Modular inverse: X = A^-1 mod N  (HAC 14.61 / 14.64)
+ */
+int mpi_inv_mod( mpi *X, mpi *A, mpi *N )
+{
+    int ret;
+    mpi G, TA, TU, U1, U2, TB, TV, V1, V2;
+
+    if( mpi_cmp_int( N, 0 ) <= 0 )
+        return( POLARSSL_ERR_MPI_BAD_INPUT_DATA );
+
+    mpi_init( &TA, &TU, &U1, &U2, &G,
+              &TB, &TV, &V1, &V2, NULL );
+
+    MPI_CHK( mpi_gcd( &G, A, N ) );
+
+    if( mpi_cmp_int( &G, 1 ) != 0 )
+    {
+        ret = POLARSSL_ERR_MPI_NOT_ACCEPTABLE;
+        goto cleanup;
+    }
+
+    MPI_CHK( mpi_mod_mpi( &TA, A, N ) );
+    MPI_CHK( mpi_copy( &TU, &TA ) );
+    MPI_CHK( mpi_copy( &TB, N ) );
+    MPI_CHK( mpi_copy( &TV, N ) );
+
+    MPI_CHK( mpi_lset( &U1, 1 ) );
+    MPI_CHK( mpi_lset( &U2, 0 ) );
+    MPI_CHK( mpi_lset( &V1, 0 ) );
+    MPI_CHK( mpi_lset( &V2, 1 ) );
+
+    do
+    {
+        while( ( TU.p[0] & 1 ) == 0 )
+        {
+            MPI_CHK( mpi_shift_r( &TU, 1 ) );
+
+            if( ( U1.p[0] & 1 ) != 0 || ( U2.p[0] & 1 ) != 0 )
+            {
+                MPI_CHK( mpi_add_mpi( &U1, &U1, &TB ) );
+                MPI_CHK( mpi_sub_mpi( &U2, &U2, &TA ) );
+            }
+
+            MPI_CHK( mpi_shift_r( &U1, 1 ) );
+            MPI_CHK( mpi_shift_r( &U2, 1 ) );
+        }
+
+        while( ( TV.p[0] & 1 ) == 0 )
+        {
+            MPI_CHK( mpi_shift_r( &TV, 1 ) );
+
+            if( ( V1.p[0] & 1 ) != 0 || ( V2.p[0] & 1 ) != 0 )
+            {
+                MPI_CHK( mpi_add_mpi( &V1, &V1, &TB ) );
+                MPI_CHK( mpi_sub_mpi( &V2, &V2, &TA ) );
+            }
+
+            MPI_CHK( mpi_shift_r( &V1, 1 ) );
+            MPI_CHK( mpi_shift_r( &V2, 1 ) );
+        }
+
+        if( mpi_cmp_mpi( &TU, &TV ) >= 0 )
+        {
+            MPI_CHK( mpi_sub_mpi( &TU, &TU, &TV ) );
+            MPI_CHK( mpi_sub_mpi( &U1, &U1, &V1 ) );
+            MPI_CHK( mpi_sub_mpi( &U2, &U2, &V2 ) );
+        }
+        else
+        {
+            MPI_CHK( mpi_sub_mpi( &TV, &TV, &TU ) );
+            MPI_CHK( mpi_sub_mpi( &V1, &V1, &U1 ) );
+            MPI_CHK( mpi_sub_mpi( &V2, &V2, &U2 ) );
+        }
+    }
+    while( mpi_cmp_int( &TU, 0 ) != 0 );
+
+    while( mpi_cmp_int( &V1, 0 ) < 0 )
+        MPI_CHK( mpi_add_mpi( &V1, &V1, N ) );
+
+    while( mpi_cmp_mpi( &V1, N ) >= 0 )
+        MPI_CHK( mpi_sub_mpi( &V1, &V1, N ) );
+
+    MPI_CHK( mpi_copy( X, &V1 ) );
+
+cleanup:
+
+    mpi_free( &V2, &V1, &TV, &TB, &G,
+              &U2, &U1, &TU, &TA, NULL );
+
+    return( ret );
+}
+
+static const int small_prime[] =
+{
+        3,    5,    7,   11,   13,   17,   19,   23,
+       29,   31,   37,   41,   43,   47,   53,   59,
+       61,   67,   71,   73,   79,   83,   89,   97,
+      101,  103,  107,  109,  113,  127,  131,  137,
+      139,  149,  151,  157,  163,  167,  173,  179,
+      181,  191,  193,  197,  199,  211,  223,  227,
+      229,  233,  239,  241,  251,  257,  263,  269,
+      271,  277,  281,  283,  293,  307,  311,  313,
+      317,  331,  337,  347,  349,  353,  359,  367,
+      373,  379,  383,  389,  397,  401,  409,  419,
+      421,  431,  433,  439,  443,  449,  457,  461,
+      463,  467,  479,  487,  491,  499,  503,  509,
+      521,  523,  541,  547,  557,  563,  569,  571,
+      577,  587,  593,  599,  601,  607,  613,  617,
+      619,  631,  641,  643,  647,  653,  659,  661,
+      673,  677,  683,  691,  701,  709,  719,  727,
+      733,  739,  743,  751,  757,  761,  769,  773,
+      787,  797,  809,  811,  821,  823,  827,  829,
+      839,  853,  857,  859,  863,  877,  881,  883,
+      887,  907,  911,  919,  929,  937,  941,  947,
+      953,  967,  971,  977,  983,  991,  997, -103
+};
+
+/*
+ * Miller-Rabin primality test  (HAC 4.24)
+ */
+int mpi_is_prime( mpi *X, int (*f_rng)(void *), void *p_rng )
+{
+    int ret, i, j, n, s, xs;
+    mpi W, R, T, A, RR;
+    unsigned char *p;
+
+    if( mpi_cmp_int( X, 0 ) == 0 )
+        return( 0 );
+
+    mpi_init( &W, &R, &T, &A, &RR, NULL );
+
+    xs = X->s; X->s = 1;
+
+    /*
+     * test trivial factors first
+     */
+    if( ( X->p[0] & 1 ) == 0 )
+        return( POLARSSL_ERR_MPI_NOT_ACCEPTABLE );
+
+    for( i = 0; small_prime[i] > 0; i++ )
+    {
+        t_int r;
+
+        if( mpi_cmp_int( X, small_prime[i] ) <= 0 )
+            return( 0 );
+
+        MPI_CHK( mpi_mod_int( &r, X, small_prime[i] ) );
+
+        if( r == 0 )
+            return( POLARSSL_ERR_MPI_NOT_ACCEPTABLE );
+    }
+
+    /*
+     * W = |X| - 1
+     * R = W >> lsb( W )
+     */
+    s = mpi_lsb( &W );
+    MPI_CHK( mpi_sub_int( &W, X, 1 ) );
+    MPI_CHK( mpi_copy( &R, &W ) );
+    MPI_CHK( mpi_shift_r( &R, s ) );
+
+    i = mpi_msb( X );
+    /*
+     * HAC, table 4.4
+     */
+    n = ( ( i >= 1300 ) ?  2 : ( i >=  850 ) ?  3 :
+          ( i >=  650 ) ?  4 : ( i >=  350 ) ?  8 :
+          ( i >=  250 ) ? 12 : ( i >=  150 ) ? 18 : 27 );
+
+    for( i = 0; i < n; i++ )
+    {
+        /*
+         * pick a random A, 1 < A < |X| - 1
+         */
+        MPI_CHK( mpi_grow( &A, X->n ) );
+
+        p = (unsigned char *) A.p;
+        for( j = 0; j < A.n * ciL; j++ )
+            *p++ = (unsigned char) f_rng( p_rng );
+
+        j = mpi_msb( &A ) - mpi_msb( &W );
+        MPI_CHK( mpi_shift_r( &A, j + 1 ) );
+        A.p[0] |= 3;
+
+        /*
+         * A = A^R mod |X|
+         */
+        MPI_CHK( mpi_exp_mod( &A, &A, &R, X, &RR ) );
+
+        if( mpi_cmp_mpi( &A, &W ) == 0 ||
+            mpi_cmp_int( &A,  1 ) == 0 )
+            continue;
+
+        j = 1;
+        while( j < s && mpi_cmp_mpi( &A, &W ) != 0 )
+        {
+            /*
+             * A = A * A mod |X|
+             */
+            MPI_CHK( mpi_mul_mpi( &T, &A, &A ) );
+            MPI_CHK( mpi_mod_mpi( &A, &T, X  ) );
+
+            if( mpi_cmp_int( &A, 1 ) == 0 )
+                break;
+
+            j++;
+        }
+
+        /*
+         * not prime if A != |X| - 1 or A == 1
+         */
+        if( mpi_cmp_mpi( &A, &W ) != 0 ||
+            mpi_cmp_int( &A,  1 ) == 0 )
+        {
+            ret = POLARSSL_ERR_MPI_NOT_ACCEPTABLE;
+            break;
+        }
+    }
+
+cleanup:
+
+    X->s = xs;
+
+    mpi_free( &RR, &A, &T, &R, &W, NULL );
+
+    return( ret );
+}
+
+/*
+ * Prime number generation
+ */
+int mpi_gen_prime( mpi *X, int nbits, int dh_flag,
+                   int (*f_rng)(void *), void *p_rng )
+{
+    int ret, k, n;
+    unsigned char *p;
+    mpi Y;
+
+    if( nbits < 3 )
+        return( POLARSSL_ERR_MPI_BAD_INPUT_DATA );
+
+    mpi_init( &Y, NULL );
+
+    n = BITS_TO_LIMBS( nbits );
+
+    MPI_CHK( mpi_grow( X, n ) );
+    MPI_CHK( mpi_lset( X, 0 ) );
+
+    p = (unsigned char *) X->p;
+    for( k = 0; k < X->n * ciL; k++ )
+        *p++ = (unsigned char) f_rng( p_rng );
+
+    k = mpi_msb( X );
+    if( k < nbits ) MPI_CHK( mpi_shift_l( X, nbits - k ) );
+    if( k > nbits ) MPI_CHK( mpi_shift_r( X, k - nbits ) );
+
+    X->p[0] |= 3;
+
+    if( dh_flag == 0 )
+    {
+        while( ( ret = mpi_is_prime( X, f_rng, p_rng ) ) != 0 )
+        {
+            if( ret != POLARSSL_ERR_MPI_NOT_ACCEPTABLE )
+                goto cleanup;
+
+            MPI_CHK( mpi_add_int( X, X, 2 ) );
+        }
+    }
+    else
+    {
+        MPI_CHK( mpi_sub_int( &Y, X, 1 ) );
+        MPI_CHK( mpi_shift_r( &Y, 1 ) );
+
+        while( 1 )
+        {
+            if( ( ret = mpi_is_prime( X, f_rng, p_rng ) ) == 0 )
+            {
+                if( ( ret = mpi_is_prime( &Y, f_rng, p_rng ) ) == 0 )
+                    break;
+
+                if( ret != POLARSSL_ERR_MPI_NOT_ACCEPTABLE )
+                    goto cleanup;
+            }
+
+            if( ret != POLARSSL_ERR_MPI_NOT_ACCEPTABLE )
+                goto cleanup;
+
+            MPI_CHK( mpi_add_int( &Y, X, 1 ) );
+            MPI_CHK( mpi_add_int(  X, X, 2 ) );
+            MPI_CHK( mpi_shift_r( &Y, 1 ) );
+        }
+    }
+
+cleanup:
+
+    mpi_free( &Y, NULL );
+
+    return( ret );
+}
diff --git a/src/src/pdkim/bignum.h b/src/src/pdkim/bignum.h
new file mode 100644 (file)
index 0000000..28e746d
--- /dev/null
@@ -0,0 +1,395 @@
+/**
+ * \file bignum.h
+ *
+ *  Based on XySSL: Copyright (C) 2006-2008  Christophe Devine
+ *
+ *  Copyright (C) 2009  Paul Bakker <polarssl_maintainer at polarssl dot org>
+ *
+ *  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.
+ */
+
+/* $Cambridge: exim/src/src/pdkim/bignum.h,v 1.2 2009/06/10 07:34:05 tom Exp $ */
+
+#ifndef POLARSSL_BIGNUM_H
+#define POLARSSL_BIGNUM_H
+
+#include <stdio.h>
+
+#define POLARSSL_ERR_MPI_FILE_IO_ERROR                     -0x0002
+#define POLARSSL_ERR_MPI_BAD_INPUT_DATA                    -0x0004
+#define POLARSSL_ERR_MPI_INVALID_CHARACTER                 -0x0006
+#define POLARSSL_ERR_MPI_BUFFER_TOO_SMALL                  -0x0008
+#define POLARSSL_ERR_MPI_NEGATIVE_VALUE                    -0x000A
+#define POLARSSL_ERR_MPI_DIVISION_BY_ZERO                  -0x000C
+#define POLARSSL_ERR_MPI_NOT_ACCEPTABLE                    -0x000E
+
+#define MPI_CHK(f) if( ( ret = f ) != 0 ) goto cleanup
+
+/*
+ * Define the base integer type, architecture-wise
+ */
+#if defined(POLARSSL_HAVE_INT8)
+typedef unsigned char  t_int;
+typedef unsigned short t_dbl;
+#else
+#if defined(POLARSSL_HAVE_INT16)
+typedef unsigned short t_int;
+typedef unsigned long  t_dbl;
+#else
+  typedef unsigned long t_int;
+  #if defined(_MSC_VER) && defined(_M_IX86)
+  typedef unsigned __int64 t_dbl;
+  #else
+    #if defined(__amd64__) || defined(__x86_64__)    || \
+        defined(__ppc64__) || defined(__powerpc64__) || \
+        defined(__ia64__)  || defined(__alpha__)
+    typedef unsigned int t_dbl __attribute__((mode(TI)));
+    #else
+    typedef unsigned long long t_dbl;
+    #endif
+  #endif
+#endif
+#endif
+
+/**
+ * \brief          MPI structure
+ */
+typedef struct
+{
+    int s;              /*!<  integer sign      */
+    int n;              /*!<  total # of limbs  */
+    t_int *p;           /*!<  pointer to limbs  */
+}
+mpi;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief          Initialize one or more mpi
+ */
+void mpi_init( mpi *X, ... );
+
+/**
+ * \brief          Unallocate one or more mpi
+ */
+void mpi_free( mpi *X, ... );
+
+/**
+ * \brief          Enlarge to the specified number of limbs
+ *
+ * \return         0 if successful,
+ *                 1 if memory allocation failed
+ */
+int mpi_grow( mpi *X, int nblimbs );
+
+/**
+ * \brief          Copy the contents of Y into X
+ *
+ * \return         0 if successful,
+ *                 1 if memory allocation failed
+ */
+int mpi_copy( mpi *X, mpi *Y );
+
+/**
+ * \brief          Swap the contents of X and Y
+ */
+void mpi_swap( mpi *X, mpi *Y );
+
+/**
+ * \brief          Set value from integer
+ *
+ * \return         0 if successful,
+ *                 1 if memory allocation failed
+ */
+int mpi_lset( mpi *X, int z );
+
+/**
+ * \brief          Return the number of least significant bits
+ */
+int mpi_lsb( mpi *X );
+
+/**
+ * \brief          Return the number of most significant bits
+ */
+int mpi_msb( mpi *X );
+
+/**
+ * \brief          Return the total size in bytes
+ */
+int mpi_size( mpi *X );
+
+/**
+ * \brief          Import from an ASCII string
+ *
+ * \param X        destination mpi
+ * \param radix    input numeric base
+ * \param s        null-terminated string buffer
+ *
+ * \return         0 if successful, or an POLARSSL_ERR_MPI_XXX error code
+ */
+int mpi_read_string( mpi *X, int radix, char *s );
+
+/**
+ * \brief          Export into an ASCII string
+ *
+ * \param X        source mpi
+ * \param radix    output numeric base
+ * \param s        string buffer
+ * \param slen     string buffer size
+ *
+ * \return         0 if successful, or an POLARSSL_ERR_MPI_XXX error code
+ *
+ * \note           Call this function with *slen = 0 to obtain the
+ *                 minimum required buffer size in *slen.
+ */
+int mpi_write_string( mpi *X, int radix, char *s, int *slen );
+
+/**
+ * \brief          Read X from an opened file
+ *
+ * \param X        destination mpi
+ * \param radix    input numeric base
+ * \param fin      input file handle
+ *
+ * \return         0 if successful, or an POLARSSL_ERR_MPI_XXX error code
+ */
+int mpi_read_file( mpi *X, int radix, FILE *fin );
+
+/**
+ * \brief          Write X into an opened file, or stdout
+ *
+ * \param p        prefix, can be NULL
+ * \param X        source mpi
+ * \param radix    output numeric base
+ * \param fout     output file handle
+ *
+ * \return         0 if successful, or an POLARSSL_ERR_MPI_XXX error code
+ *
+ * \note           Set fout == NULL to print X on the console.
+ */
+int mpi_write_file( char *p, mpi *X, int radix, FILE *fout );
+
+/**
+ * \brief          Import X from unsigned binary data, big endian
+ *
+ * \param X        destination mpi
+ * \param buf      input buffer
+ * \param buflen   input buffer size
+ *
+ * \return         0 if successful,
+ *                 1 if memory allocation failed
+ */
+int mpi_read_binary( mpi *X, unsigned char *buf, int buflen );
+
+/**
+ * \brief          Export X into unsigned binary data, big endian
+ *
+ * \param X        source mpi
+ * \param buf      output buffer
+ * \param buflen   output buffer size
+ *
+ * \return         0 if successful,
+ *                 POLARSSL_ERR_MPI_BUFFER_TOO_SMALL if buf isn't large enough
+ *
+ * \note           Call this function with *buflen = 0 to obtain the
+ *                 minimum required buffer size in *buflen.
+ */
+int mpi_write_binary( mpi *X, unsigned char *buf, int buflen );
+
+/**
+ * \brief          Left-shift: X <<= count
+ *
+ * \return         0 if successful,
+ *                 1 if memory allocation failed
+ */
+int mpi_shift_l( mpi *X, int count );
+
+/**
+ * \brief          Right-shift: X >>= count
+ *
+ * \return         0 if successful,
+ *                 1 if memory allocation failed
+ */
+int mpi_shift_r( mpi *X, int count );
+
+/**
+ * \brief          Compare unsigned values
+ *
+ * \return         1 if |X| is greater than |Y|,
+ *                -1 if |X| is lesser  than |Y| or
+ *                 0 if |X| is equal to |Y|
+ */
+int mpi_cmp_abs( mpi *X, mpi *Y );
+
+/**
+ * \brief          Compare signed values
+ *
+ * \return         1 if X is greater than Y,
+ *                -1 if X is lesser  than Y or
+ *                 0 if X is equal to Y
+ */
+int mpi_cmp_mpi( mpi *X, mpi *Y );
+
+/**
+ * \brief          Compare signed values
+ *
+ * \return         1 if X is greater than z,
+ *                -1 if X is lesser  than z or
+ *                 0 if X is equal to z
+ */
+int mpi_cmp_int( mpi *X, int z );
+
+/**
+ * \brief          Unsigned addition: X = |A| + |B|
+ *
+ * \return         0 if successful,
+ *                 1 if memory allocation failed
+ */
+int mpi_add_abs( mpi *X, mpi *A, mpi *B );
+
+/**
+ * \brief          Unsigned substraction: X = |A| - |B|
+ *
+ * \return         0 if successful,
+ *                 POLARSSL_ERR_MPI_NEGATIVE_VALUE if B is greater than A
+ */
+int mpi_sub_abs( mpi *X, mpi *A, mpi *B );
+
+/**
+ * \brief          Signed addition: X = A + B
+ *
+ * \return         0 if successful,
+ *                 1 if memory allocation failed
+ */
+int mpi_add_mpi( mpi *X, mpi *A, mpi *B );
+
+/**
+ * \brief          Signed substraction: X = A - B
+ *
+ * \return         0 if successful,
+ *                 1 if memory allocation failed
+ */
+int mpi_sub_mpi( mpi *X, mpi *A, mpi *B );
+
+/**
+ * \brief          Signed addition: X = A + b
+ *
+ * \return         0 if successful,
+ *                 1 if memory allocation failed
+ */
+int mpi_add_int( mpi *X, mpi *A, int b );
+
+/**
+ * \brief          Signed substraction: X = A - b
+ *
+ * \return         0 if successful,
+ *                 1 if memory allocation failed
+ */
+int mpi_sub_int( mpi *X, mpi *A, int b );
+
+/**
+ * \brief          Baseline multiplication: X = A * B
+ *
+ * \return         0 if successful,
+ *                 1 if memory allocation failed
+ */
+int mpi_mul_mpi( mpi *X, mpi *A, mpi *B );
+
+/**
+ * \brief          Baseline multiplication: X = A * b
+ *
+ * \return         0 if successful,
+ *                 1 if memory allocation failed
+ */
+int mpi_mul_int( mpi *X, mpi *A, t_int b );
+
+/**
+ * \brief          Division by mpi: A = Q * B + R
+ *
+ * \return         0 if successful,
+ *                 1 if memory allocation failed,
+ *                 POLARSSL_ERR_MPI_DIVISION_BY_ZERO if B == 0
+ *
+ * \note           Either Q or R can be NULL.
+ */
+int mpi_div_mpi( mpi *Q, mpi *R, mpi *A, mpi *B );
+
+/**
+ * \brief          Division by int: A = Q * b + R
+ *
+ * \return         0 if successful,
+ *                 1 if memory allocation failed,
+ *                 POLARSSL_ERR_MPI_DIVISION_BY_ZERO if b == 0
+ *
+ * \note           Either Q or R can be NULL.
+ */
+int mpi_div_int( mpi *Q, mpi *R, mpi *A, int b );
+
+/**
+ * \brief          Modulo: R = A mod B
+ *
+ * \return         0 if successful,
+ *                 1 if memory allocation failed,
+ *                 POLARSSL_ERR_MPI_DIVISION_BY_ZERO if B == 0
+ */
+int mpi_mod_mpi( mpi *R, mpi *A, mpi *B );
+
+/**
+ * \brief          Modulo: r = A mod b
+ *
+ * \return         0 if successful,
+ *                 1 if memory allocation failed,
+ *                 POLARSSL_ERR_MPI_DIVISION_BY_ZERO if b == 0
+ */
+int mpi_mod_int( t_int *r, mpi *A, int b );
+
+/**
+ * \brief          Sliding-window exponentiation: X = A^E mod N
+ *
+ * \return         0 if successful,
+ *                 1 if memory allocation failed,
+ *                 POLARSSL_ERR_MPI_BAD_INPUT_DATA if N is negative or even
+ *
+ * \note           _RR is used to avoid re-computing R*R mod N across
+ *                 multiple calls, which speeds up things a bit. It can
+ *                 be set to NULL if the extra performance is unneeded.
+ */
+int mpi_exp_mod( mpi *X, mpi *A, mpi *E, mpi *N, mpi *_RR );
+
+/**
+ * \brief          Greatest common divisor: G = gcd(A, B)
+ *
+ * \return         0 if successful,
+ *                 1 if memory allocation failed
+ */
+int mpi_gcd( mpi *G, mpi *A, mpi *B );
+
+/**
+ * \brief          Modular inverse: X = A^-1 mod N
+ *
+ * \return         0 if successful,
+ *                 1 if memory allocation failed,
+ *                 POLARSSL_ERR_MPI_BAD_INPUT_DATA if N is negative or nil
+ *                 POLARSSL_ERR_MPI_NOT_ACCEPTABLE if A has no inverse mod N
+ */
+int mpi_inv_mod( mpi *X, mpi *A, mpi *N );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* bignum.h */
diff --git a/src/src/pdkim/bn_mul.h b/src/src/pdkim/bn_mul.h
new file mode 100644 (file)
index 0000000..8f8355b
--- /dev/null
@@ -0,0 +1,719 @@
+/**
+ * \file bn_mul.h
+ *
+ *  Based on XySSL: Copyright (C) 2006-2008  Christophe Devine
+ *
+ *  Copyright (C) 2009  Paul Bakker <polarssl_maintainer at polarssl dot org>
+ *
+ *  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.
+ */
+/*
+ *      Multiply source vector [s] with b, add result
+ *       to destination vector [d] and set carry c.
+ *
+ *      Currently supports:
+ *
+ *         . IA-32 (386+)         . AMD64 / EM64T
+ *         . IA-32 (SSE2)         . Motorola 68000
+ *         . PowerPC, 32-bit      . MicroBlaze
+ *         . PowerPC, 64-bit      . TriCore
+ *         . SPARC v8             . ARM v3+
+ *         . Alpha                . MIPS32
+ *         . C, longlong          . C, generic
+ */
+
+/* $Cambridge: exim/src/src/pdkim/bn_mul.h,v 1.2 2009/06/10 07:34:05 tom Exp $ */
+
+#ifndef POLARSSL_BN_MUL_H
+#define POLARSSL_BN_MUL_H
+
+#if defined(POLARSSL_HAVE_ASM)
+
+#if defined(__GNUC__)
+#if defined(__i386__)
+
+#define MULADDC_INIT                            \
+    asm( "movl   %%ebx, %0      " : "=m" (t));  \
+    asm( "movl   %0, %%esi      " :: "m" (s));  \
+    asm( "movl   %0, %%edi      " :: "m" (d));  \
+    asm( "movl   %0, %%ecx      " :: "m" (c));  \
+    asm( "movl   %0, %%ebx      " :: "m" (b));
+
+#define MULADDC_CORE                            \
+    asm( "lodsl                 " );            \
+    asm( "mull   %ebx           " );            \
+    asm( "addl   %ecx,   %eax   " );            \
+    asm( "adcl   $0,     %edx   " );            \
+    asm( "addl   (%edi), %eax   " );            \
+    asm( "adcl   $0,     %edx   " );            \
+    asm( "movl   %edx,   %ecx   " );            \
+    asm( "stosl                 " );
+
+#if defined(POLARSSL_HAVE_SSE2)
+
+#define MULADDC_HUIT                            \
+    asm( "movd     %ecx,     %mm1     " );      \
+    asm( "movd     %ebx,     %mm0     " );      \
+    asm( "movd     (%edi),   %mm3     " );      \
+    asm( "paddq    %mm3,     %mm1     " );      \
+    asm( "movd     (%esi),   %mm2     " );      \
+    asm( "pmuludq  %mm0,     %mm2     " );      \
+    asm( "movd     4(%esi),  %mm4     " );      \
+    asm( "pmuludq  %mm0,     %mm4     " );      \
+    asm( "movd     8(%esi),  %mm6     " );      \
+    asm( "pmuludq  %mm0,     %mm6     " );      \
+    asm( "movd     12(%esi), %mm7     " );      \
+    asm( "pmuludq  %mm0,     %mm7     " );      \
+    asm( "paddq    %mm2,     %mm1     " );      \
+    asm( "movd     4(%edi),  %mm3     " );      \
+    asm( "paddq    %mm4,     %mm3     " );      \
+    asm( "movd     8(%edi),  %mm5     " );      \
+    asm( "paddq    %mm6,     %mm5     " );      \
+    asm( "movd     12(%edi), %mm4     " );      \
+    asm( "paddq    %mm4,     %mm7     " );      \
+    asm( "movd     %mm1,     (%edi)   " );      \
+    asm( "movd     16(%esi), %mm2     " );      \
+    asm( "pmuludq  %mm0,     %mm2     " );      \
+    asm( "psrlq    $32,      %mm1     " );      \
+    asm( "movd     20(%esi), %mm4     " );      \
+    asm( "pmuludq  %mm0,     %mm4     " );      \
+    asm( "paddq    %mm3,     %mm1     " );      \
+    asm( "movd     24(%esi), %mm6     " );      \
+    asm( "pmuludq  %mm0,     %mm6     " );      \
+    asm( "movd     %mm1,     4(%edi)  " );      \
+    asm( "psrlq    $32,      %mm1     " );      \
+    asm( "movd     28(%esi), %mm3     " );      \
+    asm( "pmuludq  %mm0,     %mm3     " );      \
+    asm( "paddq    %mm5,     %mm1     " );      \
+    asm( "movd     16(%edi), %mm5     " );      \
+    asm( "paddq    %mm5,     %mm2     " );      \
+    asm( "movd     %mm1,     8(%edi)  " );      \
+    asm( "psrlq    $32,      %mm1     " );      \
+    asm( "paddq    %mm7,     %mm1     " );      \
+    asm( "movd     20(%edi), %mm5     " );      \
+    asm( "paddq    %mm5,     %mm4     " );      \
+    asm( "movd     %mm1,     12(%edi) " );      \
+    asm( "psrlq    $32,      %mm1     " );      \
+    asm( "paddq    %mm2,     %mm1     " );      \
+    asm( "movd     24(%edi), %mm5     " );      \
+    asm( "paddq    %mm5,     %mm6     " );      \
+    asm( "movd     %mm1,     16(%edi) " );      \
+    asm( "psrlq    $32,      %mm1     " );      \
+    asm( "paddq    %mm4,     %mm1     " );      \
+    asm( "movd     28(%edi), %mm5     " );      \
+    asm( "paddq    %mm5,     %mm3     " );      \
+    asm( "movd     %mm1,     20(%edi) " );      \
+    asm( "psrlq    $32,      %mm1     " );      \
+    asm( "paddq    %mm6,     %mm1     " );      \
+    asm( "movd     %mm1,     24(%edi) " );      \
+    asm( "psrlq    $32,      %mm1     " );      \
+    asm( "paddq    %mm3,     %mm1     " );      \
+    asm( "movd     %mm1,     28(%edi) " );      \
+    asm( "addl     $32,      %edi     " );      \
+    asm( "addl     $32,      %esi     " );      \
+    asm( "psrlq    $32,      %mm1     " );      \
+    asm( "movd     %mm1,     %ecx     " );
+
+#define MULADDC_STOP                            \
+    asm( "emms                        " );      \
+    asm( "movl   %0, %%ebx      " :: "m" (t));  \
+    asm( "movl   %%ecx, %0      " : "=m" (c));  \
+    asm( "movl   %%edi, %0      " : "=m" (d));  \
+    asm( "movl   %%esi, %0      " : "=m" (s) :: \
+    "eax", "ecx", "edx", "esi", "edi" );
+
+#else
+
+#define MULADDC_STOP                            \
+    asm( "movl   %0, %%ebx      " :: "m" (t));  \
+    asm( "movl   %%ecx, %0      " : "=m" (c));  \
+    asm( "movl   %%edi, %0      " : "=m" (d));  \
+    asm( "movl   %%esi, %0      " : "=m" (s) :: \
+    "eax", "ecx", "edx", "esi", "edi" );
+
+#endif /* SSE2 */
+#endif /* i386 */
+
+#if defined(__amd64__) || defined (__x86_64__)
+
+#define MULADDC_INIT                            \
+    asm( "movq   %0, %%rsi      " :: "m" (s));  \
+    asm( "movq   %0, %%rdi      " :: "m" (d));  \
+    asm( "movq   %0, %%rcx      " :: "m" (c));  \
+    asm( "movq   %0, %%rbx      " :: "m" (b));  \
+    asm( "xorq   %r8, %r8       " );
+
+#define MULADDC_CORE                            \
+    asm( "movq  (%rsi),%rax     " );            \
+    asm( "mulq   %rbx           " );            \
+    asm( "addq   $8,   %rsi     " );            \
+    asm( "addq   %rcx, %rax     " );            \
+    asm( "movq   %r8,  %rcx     " );            \
+    asm( "adcq   $0,   %rdx     " );            \
+    asm( "nop                   " );            \
+    asm( "addq   %rax, (%rdi)   " );            \
+    asm( "adcq   %rdx, %rcx     " );            \
+    asm( "addq   $8,   %rdi     " );
+
+#define MULADDC_STOP                            \
+    asm( "movq   %%rcx, %0      " : "=m" (c));  \
+    asm( "movq   %%rdi, %0      " : "=m" (d));  \
+    asm( "movq   %%rsi, %0      " : "=m" (s) :: \
+    "rax", "rcx", "rdx", "rbx", "rsi", "rdi", "r8" );
+
+#endif /* AMD64 */
+
+#if defined(__mc68020__) || defined(__mcpu32__)
+
+#define MULADDC_INIT                            \
+    asm( "movl   %0, %%a2       " :: "m" (s));  \
+    asm( "movl   %0, %%a3       " :: "m" (d));  \
+    asm( "movl   %0, %%d3       " :: "m" (c));  \
+    asm( "movl   %0, %%d2       " :: "m" (b));  \
+    asm( "moveq  #0, %d0        " );
+
+#define MULADDC_CORE                            \
+    asm( "movel  %a2@+, %d1     " );            \
+    asm( "mulul  %d2, %d4:%d1   " );            \
+    asm( "addl   %d3, %d1       " );            \
+    asm( "addxl  %d0, %d4       " );            \
+    asm( "moveq  #0,  %d3       " );            \
+    asm( "addl   %d1, %a3@+     " );            \
+    asm( "addxl  %d4, %d3       " );
+
+#define MULADDC_STOP                            \
+    asm( "movl   %%d3, %0       " : "=m" (c));  \
+    asm( "movl   %%a3, %0       " : "=m" (d));  \
+    asm( "movl   %%a2, %0       " : "=m" (s) :: \
+    "d0", "d1", "d2", "d3", "d4", "a2", "a3" );
+
+#define MULADDC_HUIT                            \
+    asm( "movel  %a2@+, %d1     " );            \
+    asm( "mulul  %d2, %d4:%d1   " );            \
+    asm( "addxl  %d3, %d1       " );            \
+    asm( "addxl  %d0, %d4       " );            \
+    asm( "addl   %d1, %a3@+     " );            \
+    asm( "movel  %a2@+, %d1     " );            \
+    asm( "mulul  %d2, %d3:%d1   " );            \
+    asm( "addxl  %d4, %d1       " );            \
+    asm( "addxl  %d0, %d3       " );            \
+    asm( "addl   %d1, %a3@+     " );            \
+    asm( "movel  %a2@+, %d1     " );            \
+    asm( "mulul  %d2, %d4:%d1   " );            \
+    asm( "addxl  %d3, %d1       " );            \
+    asm( "addxl  %d0, %d4       " );            \
+    asm( "addl   %d1, %a3@+     " );            \
+    asm( "movel  %a2@+, %d1     " );            \
+    asm( "mulul  %d2, %d3:%d1   " );            \
+    asm( "addxl  %d4, %d1       " );            \
+    asm( "addxl  %d0, %d3       " );            \
+    asm( "addl   %d1, %a3@+     " );            \
+    asm( "movel  %a2@+, %d1     " );            \
+    asm( "mulul  %d2, %d4:%d1   " );            \
+    asm( "addxl  %d3, %d1       " );            \
+    asm( "addxl  %d0, %d4       " );            \
+    asm( "addl   %d1, %a3@+     " );            \
+    asm( "movel  %a2@+, %d1     " );            \
+    asm( "mulul  %d2, %d3:%d1   " );            \
+    asm( "addxl  %d4, %d1       " );            \
+    asm( "addxl  %d0, %d3       " );            \
+    asm( "addl   %d1, %a3@+     " );            \
+    asm( "movel  %a2@+, %d1     " );            \
+    asm( "mulul  %d2, %d4:%d1   " );            \
+    asm( "addxl  %d3, %d1       " );            \
+    asm( "addxl  %d0, %d4       " );            \
+    asm( "addl   %d1, %a3@+     " );            \
+    asm( "movel  %a2@+, %d1     " );            \
+    asm( "mulul  %d2, %d3:%d1   " );            \
+    asm( "addxl  %d4, %d1       " );            \
+    asm( "addxl  %d0, %d3       " );            \
+    asm( "addl   %d1, %a3@+     " );            \
+    asm( "addxl  %d0, %d3       " );
+
+#endif /* MC68000 */
+
+#if defined(__powerpc__)   || defined(__ppc__)
+#if defined(__powerpc64__) || defined(__ppc64__)
+
+#if defined(__MACH__) && defined(__APPLE__)
+
+#define MULADDC_INIT                            \
+    asm( "ld     r3, %0         " :: "m" (s));  \
+    asm( "ld     r4, %0         " :: "m" (d));  \
+    asm( "ld     r5, %0         " :: "m" (c));  \
+    asm( "ld     r6, %0         " :: "m" (b));  \
+    asm( "addi   r3, r3, -8     " );            \
+    asm( "addi   r4, r4, -8     " );            \
+    asm( "addic  r5, r5,  0     " );
+
+#define MULADDC_CORE                            \
+    asm( "ldu    r7, 8(r3)      " );            \
+    asm( "mulld  r8, r7, r6     " );            \
+    asm( "mulhdu r9, r7, r6     " );            \
+    asm( "adde   r8, r8, r5     " );            \
+    asm( "ld     r7, 8(r4)      " );            \
+    asm( "addze  r5, r9         " );            \
+    asm( "addc   r8, r8, r7     " );            \
+    asm( "stdu   r8, 8(r4)      " );
+
+#define MULADDC_STOP                            \
+    asm( "addze  r5, r5         " );            \
+    asm( "addi   r4, r4, 8      " );            \
+    asm( "addi   r3, r3, 8      " );            \
+    asm( "std    r5, %0         " : "=m" (c));  \
+    asm( "std    r4, %0         " : "=m" (d));  \
+    asm( "std    r3, %0         " : "=m" (s) :: \
+    "r3", "r4", "r5", "r6", "r7", "r8", "r9" );
+
+#else
+
+#define MULADDC_INIT                            \
+    asm( "ld     %%r3, %0       " :: "m" (s));  \
+    asm( "ld     %%r4, %0       " :: "m" (d));  \
+    asm( "ld     %%r5, %0       " :: "m" (c));  \
+    asm( "ld     %%r6, %0       " :: "m" (b));  \
+    asm( "addi   %r3, %r3, -8   " );            \
+    asm( "addi   %r4, %r4, -8   " );            \
+    asm( "addic  %r5, %r5,  0   " );
+
+#define MULADDC_CORE                            \
+    asm( "ldu    %r7, 8(%r3)    " );            \
+    asm( "mulld  %r8, %r7, %r6  " );            \
+    asm( "mulhdu %r9, %r7, %r6  " );            \
+    asm( "adde   %r8, %r8, %r5  " );            \
+    asm( "ld     %r7, 8(%r4)    " );            \
+    asm( "addze  %r5, %r9       " );            \
+    asm( "addc   %r8, %r8, %r7  " );            \
+    asm( "stdu   %r8, 8(%r4)    " );
+
+#define MULADDC_STOP                            \
+    asm( "addze  %r5, %r5       " );            \
+    asm( "addi   %r4, %r4, 8    " );            \
+    asm( "addi   %r3, %r3, 8    " );            \
+    asm( "std    %%r5, %0       " : "=m" (c));  \
+    asm( "std    %%r4, %0       " : "=m" (d));  \
+    asm( "std    %%r3, %0       " : "=m" (s) :: \
+    "r3", "r4", "r5", "r6", "r7", "r8", "r9" );
+
+#endif
+
+#else /* PPC32 */
+
+#if defined(__MACH__) && defined(__APPLE__)
+
+#define MULADDC_INIT                            \
+    asm( "lwz    r3, %0         " :: "m" (s));  \
+    asm( "lwz    r4, %0         " :: "m" (d));  \
+    asm( "lwz    r5, %0         " :: "m" (c));  \
+    asm( "lwz    r6, %0         " :: "m" (b));  \
+    asm( "addi   r3, r3, -4     " );            \
+    asm( "addi   r4, r4, -4     " );            \
+    asm( "addic  r5, r5,  0     " );
+
+#define MULADDC_CORE                            \
+    asm( "lwzu   r7, 4(r3)      " );            \
+    asm( "mullw  r8, r7, r6     " );            \
+    asm( "mulhwu r9, r7, r6     " );            \
+    asm( "adde   r8, r8, r5     " );            \
+    asm( "lwz    r7, 4(r4)      " );            \
+    asm( "addze  r5, r9         " );            \
+    asm( "addc   r8, r8, r7     " );            \
+    asm( "stwu   r8, 4(r4)      " );
+
+#define MULADDC_STOP                            \
+    asm( "addze  r5, r5         " );            \
+    asm( "addi   r4, r4, 4      " );            \
+    asm( "addi   r3, r3, 4      " );            \
+    asm( "stw    r5, %0         " : "=m" (c));  \
+    asm( "stw    r4, %0         " : "=m" (d));  \
+    asm( "stw    r3, %0         " : "=m" (s) :: \
+    "r3", "r4", "r5", "r6", "r7", "r8", "r9" );
+
+#else
+
+#define MULADDC_INIT                            \
+    asm( "lwz    %%r3, %0       " :: "m" (s));  \
+    asm( "lwz    %%r4, %0       " :: "m" (d));  \
+    asm( "lwz    %%r5, %0       " :: "m" (c));  \
+    asm( "lwz    %%r6, %0       " :: "m" (b));  \
+    asm( "addi   %r3, %r3, -4   " );            \
+    asm( "addi   %r4, %r4, -4   " );            \
+    asm( "addic  %r5, %r5,  0   " );
+
+#define MULADDC_CORE                            \
+    asm( "lwzu   %r7, 4(%r3)    " );            \
+    asm( "mullw  %r8, %r7, %r6  " );            \
+    asm( "mulhwu %r9, %r7, %r6  " );            \
+    asm( "adde   %r8, %r8, %r5  " );            \
+    asm( "lwz    %r7, 4(%r4)    " );            \
+    asm( "addze  %r5, %r9       " );            \
+    asm( "addc   %r8, %r8, %r7  " );            \
+    asm( "stwu   %r8, 4(%r4)    " );
+
+#define MULADDC_STOP                            \
+    asm( "addze  %r5, %r5       " );            \
+    asm( "addi   %r4, %r4, 4    " );            \
+    asm( "addi   %r3, %r3, 4    " );            \
+    asm( "stw    %%r5, %0       " : "=m" (c));  \
+    asm( "stw    %%r4, %0       " : "=m" (d));  \
+    asm( "stw    %%r3, %0       " : "=m" (s) :: \
+    "r3", "r4", "r5", "r6", "r7", "r8", "r9" );
+
+#endif
+
+#endif /* PPC32 */
+#endif /* PPC64 */
+
+#if defined(__sparc__)
+
+#define MULADDC_INIT                            \
+    asm( "ld     %0, %%o0       " :: "m" (s));  \
+    asm( "ld     %0, %%o1       " :: "m" (d));  \
+    asm( "ld     %0, %%o2       " :: "m" (c));  \
+    asm( "ld     %0, %%o3       " :: "m" (b));
+
+#define MULADDC_CORE                            \
+    asm( "ld    [%o0], %o4      " );            \
+    asm( "inc      4,  %o0      " );            \
+    asm( "ld    [%o1], %o5      " );            \
+    asm( "umul   %o3,  %o4, %o4 " );            \
+    asm( "addcc  %o4,  %o2, %o4 " );            \
+    asm( "rd      %y,  %g1      " );            \
+    asm( "addx   %g1,    0, %g1 " );            \
+    asm( "addcc  %o4,  %o5, %o4 " );            \
+    asm( "st     %o4, [%o1]     " );            \
+    asm( "addx   %g1,    0, %o2 " );            \
+    asm( "inc      4,  %o1      " );
+
+#define MULADDC_STOP                            \
+    asm( "st     %%o2, %0       " : "=m" (c));  \
+    asm( "st     %%o1, %0       " : "=m" (d));  \
+    asm( "st     %%o0, %0       " : "=m" (s) :: \
+    "g1", "o0", "o1", "o2", "o3", "o4", "o5" );
+
+#endif /* SPARCv8 */
+
+#if defined(__microblaze__) || defined(microblaze)
+
+#define MULADDC_INIT                            \
+    asm( "lwi   r3,   %0        " :: "m" (s));  \
+    asm( "lwi   r4,   %0        " :: "m" (d));  \
+    asm( "lwi   r5,   %0        " :: "m" (c));  \
+    asm( "lwi   r6,   %0        " :: "m" (b));  \
+    asm( "andi  r7,   r6, 0xffff" );            \
+    asm( "bsrli r6,   r6, 16    " );
+
+#define MULADDC_CORE                            \
+    asm( "lhui  r8,   r3,   0   " );            \
+    asm( "addi  r3,   r3,   2   " );            \
+    asm( "lhui  r9,   r3,   0   " );            \
+    asm( "addi  r3,   r3,   2   " );            \
+    asm( "mul   r10,  r9,  r6   " );            \
+    asm( "mul   r11,  r8,  r7   " );            \
+    asm( "mul   r12,  r9,  r7   " );            \
+    asm( "mul   r13,  r8,  r6   " );            \
+    asm( "bsrli  r8, r10,  16   " );            \
+    asm( "bsrli  r9, r11,  16   " );            \
+    asm( "add   r13, r13,  r8   " );            \
+    asm( "add   r13, r13,  r9   " );            \
+    asm( "bslli r10, r10,  16   " );            \
+    asm( "bslli r11, r11,  16   " );            \
+    asm( "add   r12, r12, r10   " );            \
+    asm( "addc  r13, r13,  r0   " );            \
+    asm( "add   r12, r12, r11   " );            \
+    asm( "addc  r13, r13,  r0   " );            \
+    asm( "lwi   r10,  r4,   0   " );            \
+    asm( "add   r12, r12, r10   " );            \
+    asm( "addc  r13, r13,  r0   " );            \
+    asm( "add   r12, r12,  r5   " );            \
+    asm( "addc   r5, r13,  r0   " );            \
+    asm( "swi   r12,  r4,   0   " );            \
+    asm( "addi   r4,  r4,   4   " );
+
+#define MULADDC_STOP                            \
+    asm( "swi   r5,   %0        " : "=m" (c));  \
+    asm( "swi   r4,   %0        " : "=m" (d));  \
+    asm( "swi   r3,   %0        " : "=m" (s) :: \
+     "r3", "r4" , "r5" , "r6" , "r7" , "r8" ,   \
+     "r9", "r10", "r11", "r12", "r13" );
+
+#endif /* MicroBlaze */
+
+#if defined(__tricore__)
+
+#define MULADDC_INIT                            \
+    asm( "ld.a   %%a2, %0       " :: "m" (s));  \
+    asm( "ld.a   %%a3, %0       " :: "m" (d));  \
+    asm( "ld.w   %%d4, %0       " :: "m" (c));  \
+    asm( "ld.w   %%d1, %0       " :: "m" (b));  \
+    asm( "xor    %d5, %d5       " );
+
+#define MULADDC_CORE                            \
+    asm( "ld.w   %d0,   [%a2+]      " );        \
+    asm( "madd.u %e2, %e4, %d0, %d1 " );        \
+    asm( "ld.w   %d0,   [%a3]       " );        \
+    asm( "addx   %d2,    %d2,  %d0  " );        \
+    asm( "addc   %d3,    %d3,    0  " );        \
+    asm( "mov    %d4,    %d3        " );        \
+    asm( "st.w  [%a3+],  %d2        " );
+
+#define MULADDC_STOP                            \
+    asm( "st.w   %0, %%d4       " : "=m" (c));  \
+    asm( "st.a   %0, %%a3       " : "=m" (d));  \
+    asm( "st.a   %0, %%a2       " : "=m" (s) :: \
+    "d0", "d1", "e2", "d4", "a2", "a3" );
+
+#endif /* TriCore */
+
+#if defined(__arm__)
+
+#define MULADDC_INIT                            \
+    asm( "ldr    r0, %0         " :: "m" (s));  \
+    asm( "ldr    r1, %0         " :: "m" (d));  \
+    asm( "ldr    r2, %0         " :: "m" (c));  \
+    asm( "ldr    r3, %0         " :: "m" (b));
+
+#define MULADDC_CORE                            \
+    asm( "ldr    r4, [r0], #4   " );            \
+    asm( "mov    r5, #0         " );            \
+    asm( "ldr    r6, [r1]       " );            \
+    asm( "umlal  r2, r5, r3, r4 " );            \
+    asm( "adds   r7, r6, r2     " );            \
+    asm( "adc    r2, r5, #0     " );            \
+    asm( "str    r7, [r1], #4   " );
+
+#define MULADDC_STOP                            \
+    asm( "str    r2, %0         " : "=m" (c));  \
+    asm( "str    r1, %0         " : "=m" (d));  \
+    asm( "str    r0, %0         " : "=m" (s) :: \
+    "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7" );
+
+#endif /* ARMv3 */
+
+#if defined(__alpha__)
+
+#define MULADDC_INIT                            \
+    asm( "ldq    $1, %0         " :: "m" (s));  \
+    asm( "ldq    $2, %0         " :: "m" (d));  \
+    asm( "ldq    $3, %0         " :: "m" (c));  \
+    asm( "ldq    $4, %0         " :: "m" (b));
+
+#define MULADDC_CORE                            \
+    asm( "ldq    $6,  0($1)     " );            \
+    asm( "addq   $1,  8, $1     " );            \
+    asm( "mulq   $6, $4, $7     " );            \
+    asm( "umulh  $6, $4, $6     " );            \
+    asm( "addq   $7, $3, $7     " );            \
+    asm( "cmpult $7, $3, $3     " );            \
+    asm( "ldq    $5,  0($2)     " );            \
+    asm( "addq   $7, $5, $7     " );            \
+    asm( "cmpult $7, $5, $5     " );            \
+    asm( "stq    $7,  0($2)     " );            \
+    asm( "addq   $2,  8, $2     " );            \
+    asm( "addq   $6, $3, $3     " );            \
+    asm( "addq   $5, $3, $3     " );
+
+#define MULADDC_STOP                            \
+    asm( "stq    $3, %0         " : "=m" (c));  \
+    asm( "stq    $2, %0         " : "=m" (d));  \
+    asm( "stq    $1, %0         " : "=m" (s) :: \
+    "$1", "$2", "$3", "$4", "$5", "$6", "$7" );
+
+#endif /* Alpha */
+
+#if defined(__mips__)
+
+#define MULADDC_INIT                            \
+    asm( "lw     $10, %0        " :: "m" (s));  \
+    asm( "lw     $11, %0        " :: "m" (d));  \
+    asm( "lw     $12, %0        " :: "m" (c));  \
+    asm( "lw     $13, %0        " :: "m" (b));
+
+#define MULADDC_CORE                            \
+    asm( "lw     $14, 0($10)    " );            \
+    asm( "multu  $13, $14       " );            \
+    asm( "addi   $10, $10, 4    " );            \
+    asm( "mflo   $14            " );            \
+    asm( "mfhi   $9             " );            \
+    asm( "addu   $14, $12, $14  " );            \
+    asm( "lw     $15, 0($11)    " );            \
+    asm( "sltu   $12, $14, $12  " );            \
+    asm( "addu   $15, $14, $15  " );            \
+    asm( "sltu   $14, $15, $14  " );            \
+    asm( "addu   $12, $12, $9   " );            \
+    asm( "sw     $15, 0($11)    " );            \
+    asm( "addu   $12, $12, $14  " );            \
+    asm( "addi   $11, $11, 4    " );
+
+#define MULADDC_STOP                            \
+    asm( "sw     $12, %0        " : "=m" (c));  \
+    asm( "sw     $11, %0        " : "=m" (d));  \
+    asm( "sw     $10, %0        " : "=m" (s) :: \
+    "$9", "$10", "$11", "$12", "$13", "$14", "$15" );
+
+#endif /* MIPS */
+#endif /* GNUC */
+
+#if (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__)
+
+#define MULADDC_INIT                            \
+    __asm   mov     esi, s                      \
+    __asm   mov     edi, d                      \
+    __asm   mov     ecx, c                      \
+    __asm   mov     ebx, b
+
+#define MULADDC_CORE                            \
+    __asm   lodsd                               \
+    __asm   mul     ebx                         \
+    __asm   add     eax, ecx                    \
+    __asm   adc     edx, 0                      \
+    __asm   add     eax, [edi]                  \
+    __asm   adc     edx, 0                      \
+    __asm   mov     ecx, edx                    \
+    __asm   stosd
+
+#if defined(POLARSSL_HAVE_SSE2)
+
+#define EMIT __asm _emit
+
+#define MULADDC_HUIT                            \
+    EMIT 0x0F  EMIT 0x6E  EMIT 0xC9             \
+    EMIT 0x0F  EMIT 0x6E  EMIT 0xC3             \
+    EMIT 0x0F  EMIT 0x6E  EMIT 0x1F             \
+    EMIT 0x0F  EMIT 0xD4  EMIT 0xCB             \
+    EMIT 0x0F  EMIT 0x6E  EMIT 0x16             \
+    EMIT 0x0F  EMIT 0xF4  EMIT 0xD0             \
+    EMIT 0x0F  EMIT 0x6E  EMIT 0x66  EMIT 0x04  \
+    EMIT 0x0F  EMIT 0xF4  EMIT 0xE0             \
+    EMIT 0x0F  EMIT 0x6E  EMIT 0x76  EMIT 0x08  \
+    EMIT 0x0F  EMIT 0xF4  EMIT 0xF0             \
+    EMIT 0x0F  EMIT 0x6E  EMIT 0x7E  EMIT 0x0C  \
+    EMIT 0x0F  EMIT 0xF4  EMIT 0xF8             \
+    EMIT 0x0F  EMIT 0xD4  EMIT 0xCA             \
+    EMIT 0x0F  EMIT 0x6E  EMIT 0x5F  EMIT 0x04  \
+    EMIT 0x0F  EMIT 0xD4  EMIT 0xDC             \
+    EMIT 0x0F  EMIT 0x6E  EMIT 0x6F  EMIT 0x08  \
+    EMIT 0x0F  EMIT 0xD4  EMIT 0xEE             \
+    EMIT 0x0F  EMIT 0x6E  EMIT 0x67  EMIT 0x0C  \
+    EMIT 0x0F  EMIT 0xD4  EMIT 0xFC             \
+    EMIT 0x0F  EMIT 0x7E  EMIT 0x0F             \
+    EMIT 0x0F  EMIT 0x6E  EMIT 0x56  EMIT 0x10  \
+    EMIT 0x0F  EMIT 0xF4  EMIT 0xD0             \
+    EMIT 0x0F  EMIT 0x73  EMIT 0xD1  EMIT 0x20  \
+    EMIT 0x0F  EMIT 0x6E  EMIT 0x66  EMIT 0x14  \
+    EMIT 0x0F  EMIT 0xF4  EMIT 0xE0             \
+    EMIT 0x0F  EMIT 0xD4  EMIT 0xCB             \
+    EMIT 0x0F  EMIT 0x6E  EMIT 0x76  EMIT 0x18  \
+    EMIT 0x0F  EMIT 0xF4  EMIT 0xF0             \
+    EMIT 0x0F  EMIT 0x7E  EMIT 0x4F  EMIT 0x04  \
+    EMIT 0x0F  EMIT 0x73  EMIT 0xD1  EMIT 0x20  \
+    EMIT 0x0F  EMIT 0x6E  EMIT 0x5E  EMIT 0x1C  \
+    EMIT 0x0F  EMIT 0xF4  EMIT 0xD8             \
+    EMIT 0x0F  EMIT 0xD4  EMIT 0xCD             \
+    EMIT 0x0F  EMIT 0x6E  EMIT 0x6F  EMIT 0x10  \
+    EMIT 0x0F  EMIT 0xD4  EMIT 0xD5             \
+    EMIT 0x0F  EMIT 0x7E  EMIT 0x4F  EMIT 0x08  \
+    EMIT 0x0F  EMIT 0x73  EMIT 0xD1  EMIT 0x20  \
+    EMIT 0x0F  EMIT 0xD4  EMIT 0xCF             \
+    EMIT 0x0F  EMIT 0x6E  EMIT 0x6F  EMIT 0x14  \
+    EMIT 0x0F  EMIT 0xD4  EMIT 0xE5             \
+    EMIT 0x0F  EMIT 0x7E  EMIT 0x4F  EMIT 0x0C  \
+    EMIT 0x0F  EMIT 0x73  EMIT 0xD1  EMIT 0x20  \
+    EMIT 0x0F  EMIT 0xD4  EMIT 0xCA             \
+    EMIT 0x0F  EMIT 0x6E  EMIT 0x6F  EMIT 0x18  \
+    EMIT 0x0F  EMIT 0xD4  EMIT 0xF5             \
+    EMIT 0x0F  EMIT 0x7E  EMIT 0x4F  EMIT 0x10  \
+    EMIT 0x0F  EMIT 0x73  EMIT 0xD1  EMIT 0x20  \
+    EMIT 0x0F  EMIT 0xD4  EMIT 0xCC             \
+    EMIT 0x0F  EMIT 0x6E  EMIT 0x6F  EMIT 0x1C  \
+    EMIT 0x0F  EMIT 0xD4  EMIT 0xDD             \
+    EMIT 0x0F  EMIT 0x7E  EMIT 0x4F  EMIT 0x14  \
+    EMIT 0x0F  EMIT 0x73  EMIT 0xD1  EMIT 0x20  \
+    EMIT 0x0F  EMIT 0xD4  EMIT 0xCE             \
+    EMIT 0x0F  EMIT 0x7E  EMIT 0x4F  EMIT 0x18  \
+    EMIT 0x0F  EMIT 0x73  EMIT 0xD1  EMIT 0x20  \
+    EMIT 0x0F  EMIT 0xD4  EMIT 0xCB             \
+    EMIT 0x0F  EMIT 0x7E  EMIT 0x4F  EMIT 0x1C  \
+    EMIT 0x83  EMIT 0xC7  EMIT 0x20             \
+    EMIT 0x83  EMIT 0xC6  EMIT 0x20             \
+    EMIT 0x0F  EMIT 0x73  EMIT 0xD1  EMIT 0x20  \
+    EMIT 0x0F  EMIT 0x7E  EMIT 0xC9
+
+#define MULADDC_STOP                            \
+    EMIT 0x0F  EMIT 0x77                        \
+    __asm   mov     c, ecx                      \
+    __asm   mov     d, edi                      \
+    __asm   mov     s, esi                      \
+
+#else
+
+#define MULADDC_STOP                            \
+    __asm   mov     c, ecx                      \
+    __asm   mov     d, edi                      \
+    __asm   mov     s, esi                      \
+
+#endif /* SSE2 */
+#endif /* MSVC */
+
+#endif /* POLARSSL_HAVE_ASM */
+
+#if !defined(MULADDC_CORE)
+#if defined(POLARSSL_HAVE_LONGLONG)
+
+#define MULADDC_INIT                    \
+{                                       \
+    t_dbl r;                            \
+    t_int r0, r1;
+
+#define MULADDC_CORE                    \
+    r   = *(s++) * (t_dbl) b;           \
+    r0  = r;                            \
+    r1  = r >> biL;                     \
+    r0 += c;  r1 += (r0 <  c);          \
+    r0 += *d; r1 += (r0 < *d);          \
+    c = r1; *(d++) = r0;
+
+#define MULADDC_STOP                    \
+}
+
+#else
+#define MULADDC_INIT                    \
+{                                       \
+    t_int s0, s1, b0, b1;               \
+    t_int r0, r1, rx, ry;               \
+    b0 = ( b << biH ) >> biH;           \
+    b1 = ( b >> biH );
+
+#define MULADDC_CORE                    \
+    s0 = ( *s << biH ) >> biH;          \
+    s1 = ( *s >> biH ); s++;            \
+    rx = s0 * b1; r0 = s0 * b0;         \
+    ry = s1 * b0; r1 = s1 * b1;         \
+    r1 += ( rx >> biH );                \
+    r1 += ( ry >> biH );                \
+    rx <<= biH; ry <<= biH;             \
+    r0 += rx; r1 += (r0 < rx);          \
+    r0 += ry; r1 += (r0 < ry);          \
+    r0 +=  c; r1 += (r0 <  c);          \
+    r0 += *d; r1 += (r0 < *d);          \
+    c = r1; *(d++) = r0;
+
+#define MULADDC_STOP                    \
+}
+
+#endif /* C (generic)  */
+#endif /* C (longlong) */
+
+#endif /* bn_mul.h */
diff --git a/src/src/pdkim/pdkim.c b/src/src/pdkim/pdkim.c
new file mode 100644 (file)
index 0000000..e269f77
--- /dev/null
@@ -0,0 +1,1714 @@
+/*
+ *  PDKIM - a RFC4871 (DKIM) implementation
+ *
+ *  Copyright (C) 2009  Tom Kistner <tom@duncanthrax.net>
+ *
+ *  http://duncanthrax.net/pdkim/
+ *
+ *  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.
+ */
+
+/* $Cambridge: exim/src/src/pdkim/pdkim.c,v 1.2 2009/06/10 07:34:05 tom Exp $ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "pdkim.h"
+
+#include "sha1.h"
+#include "sha2.h"
+#include "rsa.h"
+#include "base64.h"
+
+#define PDKIM_SIGNATURE_VERSION     "1"
+#define PDKIM_PUB_RECORD_VERSION    "DKIM1"
+
+#define PDKIM_MAX_HEADER_LEN        65536
+#define PDKIM_MAX_HEADERS           512
+#define PDKIM_MAX_BODY_LINE_LEN     16384
+#define PDKIM_DNS_TXT_MAX_NAMELEN   1024
+#define PDKIM_DEFAULT_SIGN_HEADERS "From:Sender:Reply-To:Subject:Date:"\
+                             "Message-ID:To:Cc:MIME-Version:Content-Type:"\
+                             "Content-Transfer-Encoding:Content-ID:"\
+                             "Content-Description:Resent-Date:Resent-From:"\
+                             "Resent-Sender:Resent-To:Resent-Cc:"\
+                             "Resent-Message-ID:In-Reply-To:References:"\
+                             "List-Id:List-Help:List-Unsubscribe:"\
+                             "List-Subscribe:List-Post:List-Owner:List-Archive"
+
+/* -------------------------------------------------------------------------- */
+struct pdkim_stringlist {
+  char *value;
+  void *next;
+};
+
+#define PDKIM_STR_ALLOC_FRAG 256
+struct pdkim_str {
+  char         *str;
+  unsigned int  len;
+  unsigned int  allocated;
+};
+
+/* -------------------------------------------------------------------------- */
+/* A bunch of list constants */
+char *pdkim_querymethods[] = {
+  "dns/txt",
+  NULL
+};
+char *pdkim_algos[] = {
+  "rsa-sha256",
+  "rsa-sha1",
+  NULL
+};
+char *pdkim_canons[] = {
+  "simple",
+  "relaxed",
+  NULL
+};
+char *pdkim_hashes[] = {
+  "sha256",
+  "sha1",
+  NULL
+};
+char *pdkim_keytypes[] = {
+  "rsa",
+  NULL
+};
+
+typedef struct pdkim_combined_canon_entry {
+  char *str;
+  int canon_headers;
+  int canon_body;
+} pdkim_combined_canon_entry;
+pdkim_combined_canon_entry pdkim_combined_canons[] = {
+  { "simple/simple",    PDKIM_CANON_SIMPLE,   PDKIM_CANON_SIMPLE },
+  { "simple/relaxed",   PDKIM_CANON_SIMPLE,   PDKIM_CANON_RELAXED },
+  { "relaxed/simple",   PDKIM_CANON_RELAXED,  PDKIM_CANON_SIMPLE },
+  { "relaxed/relaxed",  PDKIM_CANON_RELAXED,  PDKIM_CANON_RELAXED },
+  { "simple",           PDKIM_CANON_SIMPLE,   PDKIM_CANON_SIMPLE },
+  { "relaxed",          PDKIM_CANON_RELAXED,  PDKIM_CANON_SIMPLE },
+  { NULL,               0,                    0 }
+};
+
+
+/* -------------------------------------------------------------------------- */
+/* Print debugging functions */
+#ifdef PDKIM_DEBUG
+void pdkim_quoteprint(FILE *stream, char *data, int len, int lf) {
+  int i;
+  unsigned char *p = (unsigned char *)data;
+
+  for (i=0;i<len;i++) {
+    int c = p[i];
+    switch (c) {
+      case ' ' : fprintf(stream,"{SP}"); break;
+      case '\t': fprintf(stream,"{TB}"); break;
+      case '\r': fprintf(stream,"{CR}"); break;
+      case '\n': fprintf(stream,"{LF}"); break;
+      case '{' : fprintf(stream,"{BO}"); break;
+      case '}' : fprintf(stream,"{BC}"); break;
+      default:
+        if ( (c < 32) || (c > 127) )
+          fprintf(stream,"{%02x}",c);
+        else
+          fputc(c,stream);
+      break;
+    }
+  }
+  if (lf)
+    fputc('\n',stream);
+}
+void pdkim_hexprint(FILE *stream, char *data, int len, int lf) {
+  int i;
+  unsigned char *p = (unsigned char *)data;
+
+  for (i=0;i<len;i++) {
+    int c = p[i];
+    fprintf(stream,"%02x",c);
+  }
+  if (lf)
+    fputc('\n',stream);
+}
+#endif
+
+
+/* -------------------------------------------------------------------------- */
+/* Simple string list implementation for convinience */
+pdkim_stringlist *pdkim_append_stringlist(pdkim_stringlist *base, char *str) {
+  pdkim_stringlist *new_entry = malloc(sizeof(pdkim_stringlist));
+  if (new_entry == NULL) return NULL;
+  memset(new_entry,0,sizeof(pdkim_stringlist));
+  new_entry->value = strdup(str);
+  if (new_entry->value == NULL) return NULL;
+  if (base != NULL) {
+    pdkim_stringlist *last = base;
+    while (last->next != NULL) { last = last->next; };
+    last->next = new_entry;
+    return base;
+  }
+  else return new_entry;
+};
+
+
+/* -------------------------------------------------------------------------- */
+/* A small "growing string" implementation to escape malloc/realloc hell */
+pdkim_str *pdkim_strnew (char *cstr) {
+  unsigned int len = cstr?strlen(cstr):0;
+  pdkim_str *p = malloc(sizeof(pdkim_str));
+  if (p == NULL) return NULL;
+  memset(p,0,sizeof(pdkim_str));
+  p->str = malloc(len+1);
+  if (p->str == NULL) {
+    free(p);
+    return NULL;
+  }
+  p->allocated=(len+1);
+  p->len=len;
+  if (cstr) strcpy(p->str,cstr);
+  return p;
+};
+char *pdkim_strncat(pdkim_str *str, char *data, int len) {
+  if ((str->allocated - str->len) < (len+1)) {
+    /* Extend the buffer */
+    int num_frags = ((len+1)/PDKIM_STR_ALLOC_FRAG)+1;
+    char *n = realloc(str->str,
+                      (str->allocated+(num_frags*PDKIM_STR_ALLOC_FRAG)));
+    if (n == NULL) return NULL;
+    str->str = n;
+    str->allocated += (num_frags*PDKIM_STR_ALLOC_FRAG);
+  }
+  strncpy(&(str->str[str->len]),data,len);
+  str->len+=len;
+  str->str[str->len] = '\0';
+  return str->str;
+};
+char *pdkim_strcat(pdkim_str *str, char *cstr) {
+  return pdkim_strncat(str, cstr, strlen(cstr));
+};
+char *pdkim_numcat(pdkim_str *str, unsigned long num) {
+  char minibuf[20];
+  snprintf(minibuf,20,"%lu",num);
+  return pdkim_strcat(str,minibuf);
+};
+char *pdkim_strtrim(pdkim_str *str) {
+  char *p = str->str;
+  char *q = str->str;
+  while ( (*p != '\0') && ((*p == '\t') || (*p == ' ')) ) p++;
+  while (*p != '\0') {*q = *p; q++; p++;};
+  *q = '\0';
+  while ( (q != str->str) && ( (*q == '\0') || (*q == '\t') || (*q == ' ') ) ) {
+    *q = '\0';
+    q--;
+  }
+  str->len = strlen(str->str);
+  return str->str;
+};
+char *pdkim_strclear(pdkim_str *str) {
+  str->str[0] = '\0';
+  str->len = 0;
+  return str->str;
+};
+void pdkim_strfree(pdkim_str *str) {
+  if (str == NULL) return;
+  if (str->str != NULL) free(str->str);
+  free(str);
+};
+
+
+
+/* -------------------------------------------------------------------------- */
+void pdkim_free_pubkey(pdkim_pubkey *pub) {
+  if (pub) {
+    if (pub->version        != NULL) free(pub->version);
+    if (pub->granularity    != NULL) free(pub->granularity);
+    if (pub->hashes         != NULL) free(pub->hashes);
+    if (pub->keytype        != NULL) free(pub->keytype);
+    if (pub->srvtype        != NULL) free(pub->srvtype);
+    if (pub->notes          != NULL) free(pub->notes);
+    if (pub->key            != NULL) free(pub->key);
+    free(pub);
+  }
+}
+
+
+/* -------------------------------------------------------------------------- */
+void pdkim_free_sig(pdkim_signature *sig) {
+  if (sig) {
+    pdkim_signature *next = (pdkim_signature *)sig->next;
+
+    pdkim_stringlist *e = sig->headers;
+    while(e != NULL) {
+      pdkim_stringlist *c = e;
+      if (e->value != NULL) free(e->value);
+      e = e->next;
+      free(c);
+    }
+
+    if (sig->sigdata          != NULL) free(sig->sigdata);
+    if (sig->bodyhash         != NULL) free(sig->bodyhash);
+    if (sig->selector         != NULL) free(sig->selector);
+    if (sig->domain           != NULL) free(sig->domain);
+    if (sig->identity         != NULL) free(sig->identity);
+    if (sig->headernames      != NULL) free(sig->headernames);
+    if (sig->copiedheaders    != NULL) free(sig->copiedheaders);
+    if (sig->rsa_privkey      != NULL) free(sig->rsa_privkey);
+    if (sig->sign_headers     != NULL) free(sig->sign_headers);
+    if (sig->signature_header != NULL) free(sig->signature_header);
+    if (sig->sha1_body        != NULL) free(sig->sha1_body);
+    if (sig->sha2_body        != NULL) free(sig->sha2_body);
+    if (sig->hnames_check     != NULL) free(sig->hnames_check);
+
+    if (sig->pubkey != NULL) pdkim_free_pubkey(sig->pubkey);
+
+    free(sig);
+    if (next != NULL) pdkim_free_sig(next);
+  }
+};
+
+
+/* -------------------------------------------------------------------------- */
+DLLEXPORT void pdkim_free_ctx(pdkim_ctx *ctx) {
+  if (ctx) {
+    pdkim_free_sig(ctx->sig);
+    pdkim_strfree(ctx->cur_header);
+    free(ctx);
+  }
+};
+
+
+/* -------------------------------------------------------------------------- */
+/* Matches the name of the passed raw "header" against
+   the passed colon-separated "list", starting at entry
+   "start". Returns the position of the header name in
+   the list. */
+int header_name_match(char *header,
+                      char *tick,
+                      int   do_tick) {
+  char *hname;
+  char *lcopy;
+  char *p;
+  char *q;
+  int rc = PDKIM_FAIL;
+
+  /* Get header name */
+  char *hcolon = strchr(header,':');
+  if (hcolon == NULL) return rc; /* This isn't a header */
+  hname = malloc((hcolon-header)+1);
+  if (hname == NULL) return PDKIM_ERR_OOM;
+  memset(hname,0,(hcolon-header)+1);
+  strncpy(hname,header,(hcolon-header));
+
+  /* Copy tick-off list locally, so we can punch zeroes into it */
+  lcopy = strdup(tick);
+  if (lcopy == NULL) {
+    free(hname);
+    return PDKIM_ERR_OOM;
+  }
+  p = lcopy;
+  q = strchr(p,':');
+  while (q != NULL) {
+    *q = '\0';
+
+    if (strcasecmp(p,hname) == 0) {
+      rc = PDKIM_OK;
+      /* Invalidate header name instance in tick-off list */
+      if (do_tick) tick[p-lcopy] = '_';
+      goto BAIL;
+    }
+
+    p = q+1;
+    q = strchr(p,':');
+  }
+
+  if (strcasecmp(p,hname) == 0) {
+    rc = PDKIM_OK;
+    /* Invalidate header name instance in tick-off list */
+    if (do_tick) tick[p-lcopy] = '_';
+  }
+
+  BAIL:
+  free(hname);
+  free(lcopy);
+  return rc;
+}
+
+
+/* -------------------------------------------------------------------------- */
+/* Performs "relaxed" canonicalization of a header. The returned pointer needs
+   to be free()d. */
+char *pdkim_relax_header (char *header, int crlf) {
+  int past_field_name = 0;
+  int seen_wsp = 0;
+  char *p = header;
+  char *q;
+  char *relaxed = malloc(strlen(header));
+  if (relaxed == NULL) return NULL;
+  q = relaxed;
+  while (*p != '\0') {
+    int c = *p;
+    /* Ignore CR & LF */
+    if ( (c == '\r') || (c == '\n') ) {
+      p++;
+      continue;
+    }
+    if ( (c == '\t') || (c == ' ') ) {
+      c = ' '; /* Turns WSP into SP */
+      if (seen_wsp) {
+        p++;
+        continue;
+      }
+      else seen_wsp = 1;
+    }
+    else {
+      if ( (!past_field_name) && (c == ':') ) {
+        if (seen_wsp) q--;   /* This removes WSP before the colon */
+        seen_wsp = 1;        /* This removes WSP after the colon */
+        past_field_name = 1;
+      }
+      else seen_wsp = 0;
+    }
+    /* Lowercase header name */
+    if (!past_field_name) c = tolower(c);
+    *q = c;
+    p++;
+    q++;
+  }
+  *q = '\0';
+  if (crlf) strcat(relaxed,"\r\n");
+  return relaxed;
+};
+
+
+/* -------------------------------------------------------------------------- */
+#define PDKIM_QP_ERROR_DECODE -1
+char *pdkim_decode_qp_char(char *qp_p, int *c) {
+  char *initial_pos = qp_p;
+
+  /* Advance one char */
+  qp_p++;
+
+  /* Check for two hex digits and decode them */
+  if (isxdigit(*qp_p) && isxdigit(qp_p[1])) {
+    /* Do hex conversion */
+    if (isdigit(*qp_p)) {*c = *qp_p - '0';}
+    else {*c = toupper(*qp_p) - 'A' + 10;};
+    *c <<= 4;
+    if (isdigit(qp_p[1])) {*c |= qp_p[1] - '0';}
+    else {*c |= toupper(qp_p[1]) - 'A' + 10;};
+    return qp_p + 2;
+  };
+
+  /* Illegal char here */
+  *c = PDKIM_QP_ERROR_DECODE;
+  return initial_pos;
+}
+
+
+/* -------------------------------------------------------------------------- */
+char *pdkim_decode_qp(char *str) {
+  int nchar = 0;
+  char *q;
+  char *p = str;
+  char *n = malloc(strlen(p)+1);
+  if (n == NULL) return NULL;
+  *n = '\0';
+  q = n;
+  while (*p != '\0') {
+    if (*p == '=') {
+      p = pdkim_decode_qp_char(p,&nchar);
+      if (nchar >= 0) {
+        *q = nchar;
+        q++;
+        continue;
+      }
+    }
+    else {
+      *q = *p;
+      q++;
+    }
+    p++;
+  }
+  *q = '\0';
+  return n;
+}
+
+
+/* -------------------------------------------------------------------------- */
+char *pdkim_decode_base64(char *str, int *num_decoded) {
+  int dlen = 0;
+  char *res;
+
+  base64_decode(NULL, &dlen, (unsigned char *)str, strlen(str));
+  res = malloc(dlen+1);
+  if (res == NULL) return NULL;
+  if (base64_decode((unsigned char *)res,&dlen,(unsigned char *)str,strlen(str)) != 0) {
+    free(res);
+    return NULL;
+  }
+  if (num_decoded != NULL) *num_decoded = dlen;
+  return res;
+}
+
+/* -------------------------------------------------------------------------- */
+char *pdkim_encode_base64(char *str, int num) {
+  int dlen = 0;
+  char *res;
+
+  base64_encode(NULL, &dlen, (unsigned char *)str, num);
+  res = malloc(dlen+1);
+  if (res == NULL) return NULL;
+  if (base64_encode((unsigned char *)res,&dlen,(unsigned char *)str,num) != 0) {
+    free(res);
+    return NULL;
+  }
+  return res;
+}
+
+
+/* -------------------------------------------------------------------------- */
+#define PDKIM_HDR_LIMBO 0
+#define PDKIM_HDR_TAG   1
+#define PDKIM_HDR_VALUE 2
+pdkim_signature *pdkim_parse_sig_header(pdkim_ctx *ctx, char *raw_hdr) {
+  pdkim_signature *sig ;
+  char *p,*q;
+  pdkim_str *cur_tag = NULL;
+  pdkim_str *cur_val = NULL;
+  int past_hname = 0;
+  int in_b_val = 0;
+  int where = PDKIM_HDR_LIMBO;
+  int i;
+
+  sig = malloc(sizeof(pdkim_signature));
+  if (sig == NULL) return NULL;
+  memset(sig,0,sizeof(pdkim_signature));
+  sig->bodylength = -1;
+
+  sig->rawsig_no_b_val = malloc(strlen(raw_hdr)+1);
+  if (sig->rawsig_no_b_val == NULL) {
+    free(sig);
+    return NULL;
+  }
+
+  p = raw_hdr;
+  q = sig->rawsig_no_b_val;
+
+  while (1) {
+
+    /* Ignore FWS */
+    if ( (*p == '\r') || (*p == '\n') )
+      goto NEXT_CHAR;
+
+    /* Fast-forward through header name */
+    if (!past_hname) {
+      if (*p == ':') past_hname = 1;
+      goto NEXT_CHAR;
+    }
+
+    if (where == PDKIM_HDR_LIMBO) {
+      /* In limbo, just wait for a tag-char to appear */
+      if (!((*p >= 'a') && (*p <= 'z')))
+        goto NEXT_CHAR;
+
+      where = PDKIM_HDR_TAG;
+    }
+
+    if (where == PDKIM_HDR_TAG) {
+      if (cur_tag == NULL)
+        cur_tag = pdkim_strnew(NULL);
+
+      if ((*p >= 'a') && (*p <= 'z'))
+        pdkim_strncat(cur_tag,p,1);
+
+      if (*p == '=') {
+        if (strcmp(cur_tag->str,"b") == 0) {
+          *q = '='; q++;
+          in_b_val = 1;
+        }
+        where = PDKIM_HDR_VALUE;
+        goto NEXT_CHAR;
+      }
+    }
+
+    if (where == PDKIM_HDR_VALUE) {
+      if (cur_val == NULL)
+        cur_val = pdkim_strnew(NULL);
+
+      if ( (*p == '\r') || (*p == '\n') || (*p == ' ') || (*p == '\t') )
+        goto NEXT_CHAR;
+
+      if ( (*p == ';') || (*p == '\0') ) {
+        if (cur_tag->len > 0) {
+          pdkim_strtrim(cur_val);
+          #ifdef PDKIM_DEBUG
+          if (ctx->debug_stream)
+            fprintf(ctx->debug_stream, "%s=%s\n", cur_tag->str, cur_val->str);
+          #endif
+          switch (cur_tag->str[0]) {
+            case 'b':
+              switch (cur_tag->str[1]) {
+                case 'h':
+                  sig->bodyhash = pdkim_decode_base64(cur_val->str,&(sig->bodyhash_len));
+                break;
+                default:
+                  sig->sigdata = pdkim_decode_base64(cur_val->str,&(sig->sigdata_len));
+                break;
+              }
+            break;
+            case 'v':
+              if (strcmp(cur_val->str,PDKIM_SIGNATURE_VERSION) == 0) {
+                /* We only support version 1, and that is currently the
+                   only version there is. */
+                sig->version = 1;
+              }
+            break;
+            case 'a':
+              i = 0;
+              while (pdkim_algos[i] != NULL) {
+                if (strcmp(cur_val->str,pdkim_algos[i]) == 0 ) {
+                  sig->algo = i;
+                  break;
+                }
+                i++;
+              }
+            break;
+            case 'c':
+              i = 0;
+              while (pdkim_combined_canons[i].str != NULL) {
+                if (strcmp(cur_val->str,pdkim_combined_canons[i].str) == 0 ) {
+                  sig->canon_headers = pdkim_combined_canons[i].canon_headers;
+                  sig->canon_body    = pdkim_combined_canons[i].canon_body;
+                  break;
+                }
+                i++;
+              }
+            break;
+            case 'q':
+              i = 0;
+              while (pdkim_querymethods[i] != NULL) {
+                if (strcmp(cur_val->str,pdkim_querymethods[i]) == 0 ) {
+                  sig->querymethod = i;
+                  break;
+                }
+                i++;
+              }
+            break;
+            case 's':
+              sig->selector = strdup(cur_val->str);
+            break;
+            case 'd':
+              sig->domain = strdup(cur_val->str);
+            break;
+            case 'i':
+              sig->identity = pdkim_decode_qp(cur_val->str);
+            break;
+            case 't':
+              sig->created = strtoul(cur_val->str,NULL,10);
+            break;
+            case 'x':
+              sig->expires = strtoul(cur_val->str,NULL,10);
+            break;
+            case 'l':
+              sig->bodylength = strtol(cur_val->str,NULL,10);
+            break;
+            case 'h':
+              sig->headernames = strdup(cur_val->str);
+            break;
+            case 'z':
+              sig->copiedheaders = pdkim_decode_qp(cur_val->str);
+            break;
+            default:
+              #ifdef PDKIM_DEBUG
+              if (ctx->debug_stream)
+                fprintf(ctx->debug_stream, "Unknown tag encountered\n");
+              #endif
+            break;
+          }
+        }
+        pdkim_strclear(cur_tag);
+        pdkim_strclear(cur_val);
+        in_b_val = 0;
+        where = PDKIM_HDR_LIMBO;
+        goto NEXT_CHAR;
+      }
+      else pdkim_strncat(cur_val,p,1);
+    }
+
+    NEXT_CHAR:
+    if (*p == '\0') break;
+
+    if (!in_b_val) {
+      *q = *p;
+      q++;
+    }
+    p++;
+  }
+
+  /* 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);
+    return NULL;
+  }
+
+  /* Copy header list to 'tick-off' header list */
+  sig->hnames_check = strdup(sig->headernames);
+
+  *q = '\0';
+  /* Chomp raw header. The final newline must not be added to the signature. */
+  q--;
+  while( (q > sig->rawsig_no_b_val) && ((*q == '\r') || (*q == '\n')) ) {
+    *q = '\0'; q--;
+  }
+
+  #ifdef PDKIM_DEBUG
+  if (ctx->debug_stream) {
+    fprintf(ctx->debug_stream,
+            "PDKIM >> Raw signature w/o b= tag value >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
+    pdkim_quoteprint(ctx->debug_stream,
+                     sig->rawsig_no_b_val,
+                     strlen(sig->rawsig_no_b_val), 1);
+    fprintf(ctx->debug_stream,
+            "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
+  }
+  #endif
+
+  sig->sha1_body = malloc(sizeof(sha1_context));
+  if (sig->sha1_body == NULL) {
+    pdkim_free_sig(sig);
+    return NULL;
+  }
+  sig->sha2_body = malloc(sizeof(sha2_context));
+  if (sig->sha2_body == NULL) {
+    pdkim_free_sig(sig);
+    return NULL;
+  }
+
+  sha1_starts(sig->sha1_body);
+  sha2_starts(sig->sha2_body,0);
+
+  return sig;
+}
+
+
+/* -------------------------------------------------------------------------- */
+pdkim_pubkey *pdkim_parse_pubkey_record(pdkim_ctx *ctx, char *raw_record) {
+  pdkim_pubkey *pub ;
+  char *p;
+  pdkim_str *cur_tag = NULL;
+  pdkim_str *cur_val = NULL;
+  int where = PDKIM_HDR_LIMBO;
+
+  pub = malloc(sizeof(pdkim_pubkey));
+  if (pub == NULL) return NULL;
+  memset(pub,0,sizeof(pdkim_pubkey));
+
+  p = raw_record;
+
+  while (1) {
+
+    /* Ignore FWS */
+    if ( (*p == '\r') || (*p == '\n') )
+      goto NEXT_CHAR;
+
+    if (where == PDKIM_HDR_LIMBO) {
+      /* In limbo, just wait for a tag-char to appear */
+      if (!((*p >= 'a') && (*p <= 'z')))
+        goto NEXT_CHAR;
+
+      where = PDKIM_HDR_TAG;
+    }
+
+    if (where == PDKIM_HDR_TAG) {
+      if (cur_tag == NULL)
+        cur_tag = pdkim_strnew(NULL);
+
+      if ((*p >= 'a') && (*p <= 'z'))
+        pdkim_strncat(cur_tag,p,1);
+
+      if (*p == '=') {
+        where = PDKIM_HDR_VALUE;
+        goto NEXT_CHAR;
+      }
+    }
+
+    if (where == PDKIM_HDR_VALUE) {
+      if (cur_val == NULL)
+        cur_val = pdkim_strnew(NULL);
+
+      if ( (*p == '\r') || (*p == '\n') )
+        goto NEXT_CHAR;
+
+      if ( (*p == ';') || (*p == '\0') ) {
+        if (cur_tag->len > 0) {
+          pdkim_strtrim(cur_val);
+          #ifdef PDKIM_DEBUG
+          if (ctx->debug_stream)
+            fprintf(ctx->debug_stream, "%s=%s\n", cur_tag->str, cur_val->str);
+          #endif
+          switch (cur_tag->str[0]) {
+            case 'v':
+              /* This tag isn't evaluated because:
+                 - We only support version DKIM1.
+                 - Which is the default for this value (set below)
+                 - Other versions are currently not specified.      */
+            break;
+            case 'h':
+              pub->hashes = strdup(cur_val->str);
+            break;
+            case 'g':
+              pub->granularity = strdup(cur_val->str);
+            break;
+            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;
+            case 'k':
+              pub->hashes = strdup(cur_val->str);
+            break;
+            case 's':
+              pub->srvtype = strdup(cur_val->str);
+            break;
+            case 't':
+              if (strchr(cur_val->str,'t') != NULL) pub->testing = 1;
+              if (strchr(cur_val->str,'s') != NULL) pub->no_subdomaining = 1;
+            break;
+            default:
+              #ifdef PDKIM_DEBUG
+              if (ctx->debug_stream)
+                fprintf(ctx->debug_stream, "Unknown tag encountered\n");
+              #endif
+            break;
+          }
+        }
+        pdkim_strclear(cur_tag);
+        pdkim_strclear(cur_val);
+        where = PDKIM_HDR_LIMBO;
+        goto NEXT_CHAR;
+      }
+      else pdkim_strncat(cur_val,p,1);
+    }
+
+    NEXT_CHAR:
+    if (*p == '\0') break;
+    p++;
+  }
+
+  /* Set fallback defaults */
+  if (pub->version     == NULL) pub->version     = strdup(PDKIM_PUB_RECORD_VERSION);
+  if (pub->granularity == NULL) pub->granularity = strdup("*");
+  if (pub->keytype     == NULL) pub->keytype     = strdup("rsa");
+  if (pub->srvtype     == NULL) pub->srvtype     = strdup("*");
+
+  /* p= is required */
+  if (pub->key == NULL) {
+    pdkim_free_pubkey(pub);
+    return NULL;
+  }
+
+  return pub;
+}
+
+
+/* -------------------------------------------------------------------------- */
+int pdkim_update_bodyhash(pdkim_ctx *ctx, char *data, int len) {
+  pdkim_signature *sig = ctx->sig;
+  /* Cache relaxed version of data */
+  char *relaxed_data = NULL;
+  int   relaxed_len  = 0;
+
+  /* Traverse all signatures, updating their hashes. */
+  while (sig != NULL) {
+    /* Defaults to simple canon (no further treatment necessary) */
+    char *canon_data = data;
+    int   canon_len = len;
+
+    if (sig->canon_body == PDKIM_CANON_RELAXED) {
+      /* Relax the line if not done already */
+      if (relaxed_data == NULL) {
+        int seen_wsp = 0;
+        char *p = data;
+        int q = 0;
+        relaxed_data = malloc(len+1);
+        if (relaxed_data == NULL) return PDKIM_ERR_OOM;
+        while (*p != '\0') {
+          char c = *p;
+          if ( (c == '\t') || (c == ' ') ) {
+            c = ' '; /* Turns WSP into SP */
+            if (seen_wsp) {
+              p++;
+              continue;
+            }
+            else seen_wsp = 1;
+          }
+          else seen_wsp = 0;
+          relaxed_data[q++] = c;
+          p++;
+        }
+        relaxed_data[q] = '\0';
+        relaxed_len = q;
+      }
+      canon_data = relaxed_data;
+      canon_len  = relaxed_len;
+    }
+
+    /* Make sure we don't exceed the to-be-signed body length */
+    if ((sig->bodylength >= 0) &&
+        ((sig->signed_body_bytes+(unsigned long)canon_len) > sig->bodylength))
+      canon_len = (sig->bodylength - sig->signed_body_bytes);
+
+    if (canon_len > 0) {
+      if (sig->algo == PDKIM_ALGO_RSA_SHA1)
+        sha1_update(sig->sha1_body,(unsigned char *)canon_data,canon_len);
+      else
+        sha2_update(sig->sha2_body,(unsigned char *)canon_data,canon_len);
+      sig->signed_body_bytes += canon_len;
+#ifdef PDKIM_DEBUG
+      if (ctx->debug_stream!=NULL)
+        pdkim_quoteprint(ctx->debug_stream,canon_data,canon_len,0);
+#endif
+    }
+
+    sig = sig->next;
+  }
+
+  if (relaxed_data != NULL) free(relaxed_data);
+  return PDKIM_OK;
+};
+
+
+/* -------------------------------------------------------------------------- */
+int pdkim_finish_bodyhash(pdkim_ctx *ctx) {
+  pdkim_signature *sig = ctx->sig;
+
+  /* Traverse all signatures */
+  while (sig != NULL) {
+
+    /* Finish hashes */
+    unsigned char bh[32]; /* SHA-256 = 32 Bytes,  SHA-1 = 20 Bytes */
+    if (sig->algo == PDKIM_ALGO_RSA_SHA1)
+      sha1_finish(sig->sha1_body,bh);
+    else
+      sha2_finish(sig->sha2_body,bh);
+
+    #ifdef PDKIM_DEBUG
+    if (ctx->debug_stream) {
+      fprintf(ctx->debug_stream, "PDKIM [%s] Body bytes hashed: %lu\n",
+        sig->domain, sig->signed_body_bytes);
+      fprintf(ctx->debug_stream, "PDKIM [%s] bh  computed: ", sig->domain);
+      pdkim_hexprint(ctx->debug_stream, (char *)bh,
+                     (sig->algo == PDKIM_ALGO_RSA_SHA1)?20:32,1);
+    }
+    #endif
+
+    /* SIGNING -------------------------------------------------------------- */
+    if (ctx->mode == PDKIM_MODE_SIGN) {
+      sig->bodyhash_len = (sig->algo == PDKIM_ALGO_RSA_SHA1)?20:32;
+      sig->bodyhash = malloc(sig->bodyhash_len);
+      if (sig->bodyhash == NULL) return PDKIM_ERR_OOM;
+      memcpy(sig->bodyhash,bh,sig->bodyhash_len);
+
+      /* If bodylength limit is set, and we have received less bytes
+         than the requested amount, effectively remove the limit tag. */
+      if (sig->signed_body_bytes < sig->bodylength) sig->bodylength = -1;
+    }
+    /* VERIFICATION --------------------------------------------------------- */
+    else {
+      /* Compare bodyhash */
+      if (memcmp(bh,sig->bodyhash,
+                 (sig->algo == PDKIM_ALGO_RSA_SHA1)?20:32) == 0) {
+        #ifdef PDKIM_DEBUG
+        if (ctx->debug_stream)
+          fprintf(ctx->debug_stream, "PDKIM [%s] Body hash verified OK\n",
+                  sig->domain);
+        #endif
+      }
+      else {
+        #ifdef PDKIM_DEBUG
+        if (ctx->debug_stream) {
+          fprintf(ctx->debug_stream, "PDKIM [%s] Body hash did NOT verify\n",
+                  sig->domain);
+          fprintf(ctx->debug_stream, "PDKIM [%s] bh signature: ", sig->domain);
+          pdkim_hexprint(ctx->debug_stream, sig->bodyhash,
+                           (sig->algo == PDKIM_ALGO_RSA_SHA1)?20:32,1);
+        }
+        #endif
+        sig->verify_status     = PDKIM_VERIFY_FAIL;
+        sig->verify_ext_status = PDKIM_VERIFY_FAIL_BODY;
+      }
+    }
+
+    sig = sig->next;
+  }
+
+  return PDKIM_OK;
+};
+
+
+
+/* -------------------------------------------------------------------------- */
+/* Callback from pdkim_feed below for processing complete body lines */
+int pdkim_bodyline_complete(pdkim_ctx *ctx) {
+  char *p = ctx->linebuf;
+  int   n = ctx->linebuf_offset;
+
+  /* Ignore extra data if we've seen the end-of-data marker */
+  if (ctx->seen_eod) goto BAIL;
+
+  /* We've always got one extra byte to stuff a zero ... */
+  ctx->linebuf[(ctx->linebuf_offset)] = '\0';
+
+  if (ctx->input_mode == PDKIM_INPUT_SMTP) {
+    /* Terminate on EOD marker */
+    if (memcmp(p,".\r\n",3) == 0) {
+      ctx->seen_eod = 1;
+      goto BAIL;
+    }
+    /* Unstuff dots */
+    if (memcmp(p,"..",2) == 0) {
+      p++;
+      n--;
+    }
+  }
+
+  /* Empty lines need to be buffered until we find a non-empty line */
+  if (memcmp(p,"\r\n",2) == 0) {
+    ctx->num_buffered_crlf++;
+    goto BAIL;
+  }
+
+  /* At this point, we have a non-empty line, so release the buffered ones. */
+  while (ctx->num_buffered_crlf) {
+    pdkim_update_bodyhash(ctx,"\r\n",2);
+    ctx->num_buffered_crlf--;
+  }
+
+  pdkim_update_bodyhash(ctx,p,n);
+
+  BAIL:
+  ctx->linebuf_offset = 0;
+  return PDKIM_OK;
+}
+
+
+/* -------------------------------------------------------------------------- */
+/* Callback from pdkim_feed below for processing complete headers */
+#define DKIM_SIGNATURE_HEADERNAME "DKIM-Signature:"
+int pdkim_header_complete(pdkim_ctx *ctx) {
+  pdkim_signature *sig = ctx->sig;
+
+  /* Special case: The last header can have an extra \r appended */
+  if ( (ctx->cur_header->len > 1) &&
+       (ctx->cur_header->str[(ctx->cur_header->len)-1] == '\r') ) {
+    ctx->cur_header->str[(ctx->cur_header->len)-1] = '\0';
+    ctx->cur_header->len--;
+  }
+
+  ctx->num_headers++;
+  if (ctx->num_headers > PDKIM_MAX_HEADERS) goto BAIL;
+
+  /* Traverse all signatures */
+  while (sig != NULL) {
+    pdkim_stringlist *list;
+
+    /* SIGNING -------------------------------------------------------------- */
+    if (ctx->mode == PDKIM_MODE_SIGN) {
+      if (header_name_match(ctx->cur_header->str,
+                            sig->sign_headers?
+                              sig->sign_headers:
+                              PDKIM_DEFAULT_SIGN_HEADERS, 0) != PDKIM_OK) goto NEXT_SIG;
+    }
+    /* VERIFICATION --------------------------------------------------------- */
+    else {
+      /* Header is not included or all instances were already 'ticked off' */
+      if (header_name_match(ctx->cur_header->str,
+                            sig->hnames_check, 1) != PDKIM_OK) goto NEXT_SIG;
+    }
+
+    /* Add header to the signed headers list */
+    list = pdkim_append_stringlist(sig->headers,
+                                   ctx->cur_header->str);
+    if (list == NULL) return PDKIM_ERR_OOM;
+    sig->headers = list;
+
+    NEXT_SIG:
+    sig = sig->next;
+  }
+
+  /* DKIM-Signature: headers are added to the verification list */
+  if ( (ctx->mode == PDKIM_MODE_VERIFY) &&
+       (strncasecmp(ctx->cur_header->str,
+                    DKIM_SIGNATURE_HEADERNAME,
+                    strlen(DKIM_SIGNATURE_HEADERNAME)) == 0) ) {
+     pdkim_signature *new_sig;
+    /* Create and chain new signature block */
+    #ifdef PDKIM_DEBUG
+    if (ctx->debug_stream)
+      fprintf(ctx->debug_stream,
+        "PDKIM >> Found sig, trying to parse >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
+    #endif
+    new_sig = pdkim_parse_sig_header(ctx, ctx->cur_header->str);
+    if (new_sig != NULL) {
+      pdkim_signature *last_sig = ctx->sig;
+      if (last_sig == NULL) {
+        ctx->sig = new_sig;
+      }
+      else {
+        while (last_sig->next != NULL) { last_sig = last_sig->next; };
+        last_sig->next = new_sig;
+      }
+    }
+    else {
+      #ifdef PDKIM_DEBUG
+      if (ctx->debug_stream) {
+        fprintf(ctx->debug_stream,"Error while parsing signature header\n");
+        fprintf(ctx->debug_stream,
+          "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
+      }
+      #endif
+    }
+  }
+
+  BAIL:
+  pdkim_strclear(ctx->cur_header); /* Re-use existing pdkim_str */
+  return PDKIM_OK;
+};
+
+
+
+/* -------------------------------------------------------------------------- */
+#define HEADER_BUFFER_FRAG_SIZE 256
+DLLEXPORT int pdkim_feed (pdkim_ctx *ctx,
+                char *data,
+                int   len) {
+  int p;
+  for (p=0;p<len;p++) {
+    char c = data[p];
+    if (ctx->past_headers) {
+      /* Processing body byte */
+      ctx->linebuf[(ctx->linebuf_offset)++] = c;
+      if (c == '\n') {
+        int rc = pdkim_bodyline_complete(ctx); /* End of line */
+        if (rc != PDKIM_OK) return rc;
+      }
+      if (ctx->linebuf_offset == (PDKIM_MAX_BODY_LINE_LEN-1))
+        return PDKIM_ERR_LONG_LINE;
+    }
+    else {
+      /* Processing header byte */
+      if (c != '\r') {
+        if (c == '\n') {
+          if (ctx->seen_lf) {
+            int rc = pdkim_header_complete(ctx); /* Seen last header line */
+            if (rc != PDKIM_OK) return rc;
+            ctx->past_headers = 1;
+            ctx->seen_lf = 0;
+#ifdef PDKIM_DEBUG
+            if (ctx->debug_stream)
+              fprintf(ctx->debug_stream,
+                "PDKIM >> Hashed body data, canonicalized >>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
+#endif
+            continue;
+          }
+          else ctx->seen_lf = 1;
+        }
+        else if (ctx->seen_lf) {
+          if (! ((c == '\t') || (c == ' '))) {
+            int rc = pdkim_header_complete(ctx); /* End of header */
+            if (rc != PDKIM_OK) return rc;
+          }
+          ctx->seen_lf = 0;
+        }
+      }
+      if (ctx->cur_header == NULL) {
+        ctx->cur_header = pdkim_strnew(NULL);
+        if (ctx->cur_header == NULL) return PDKIM_ERR_OOM;
+      }
+      if (ctx->cur_header->len < PDKIM_MAX_HEADER_LEN)
+        if (pdkim_strncat(ctx->cur_header,&data[p],1) == NULL)
+          return PDKIM_ERR_OOM;
+    }
+  }
+  return PDKIM_OK;
+};
+
+
+/* -------------------------------------------------------------------------- */
+char *pdkim_create_header(pdkim_signature *sig, int final) {
+  char *rc = NULL;
+  char *base64_bh = NULL;
+  char *base64_b  = NULL;
+  pdkim_str *hdr = pdkim_strnew("DKIM-Signature: v="PDKIM_SIGNATURE_VERSION);
+  if (hdr == NULL) return NULL;
+
+  base64_bh = pdkim_encode_base64(sig->bodyhash, sig->bodyhash_len);
+  if (base64_bh == NULL) goto BAIL;
+
+  /* Required and static bits */
+  if (
+        pdkim_strcat(hdr,"; a=")                                &&
+        pdkim_strcat(hdr,pdkim_algos[sig->algo])                &&
+        pdkim_strcat(hdr,"; q=")                                &&
+        pdkim_strcat(hdr,pdkim_querymethods[sig->querymethod])  &&
+        pdkim_strcat(hdr,"; c=")                                &&
+        pdkim_strcat(hdr,pdkim_canons[sig->canon_headers])      &&
+        pdkim_strcat(hdr,"/")                                   &&
+        pdkim_strcat(hdr,pdkim_canons[sig->canon_body])         &&
+        pdkim_strcat(hdr,"; d=")                                &&
+        pdkim_strcat(hdr,sig->domain)                           &&
+        pdkim_strcat(hdr,"; s=")                                &&
+        pdkim_strcat(hdr,sig->selector)                         &&
+        pdkim_strcat(hdr,";\r\n\th=")                           &&
+        pdkim_strcat(hdr,sig->headernames)                      &&
+        pdkim_strcat(hdr,"; bh=")                               &&
+        pdkim_strcat(hdr,base64_bh)                             &&
+        pdkim_strcat(hdr,";\r\n\t")
+     ) {
+    /* Optional bits */
+    if (sig->identity != NULL) {
+      if (!( pdkim_strcat(hdr,"i=")                             &&
+             pdkim_strcat(hdr,sig->identity)                    &&
+             pdkim_strcat(hdr,";") ) ) {
+        goto BAIL;
+      }
+    }
+    if (sig->created > 0) {
+      if (!( pdkim_strcat(hdr,"t=")                             &&
+             pdkim_numcat(hdr,sig->created)                     &&
+             pdkim_strcat(hdr,";") ) ) {
+        goto BAIL;
+      }
+    }
+    if (sig->expires > 0) {
+      if (!( pdkim_strcat(hdr,"x=")                             &&
+             pdkim_numcat(hdr,sig->expires)                     &&
+             pdkim_strcat(hdr,";") ) ) {
+        goto BAIL;
+      }
+    }
+    if (sig->bodylength >= 0) {
+      if (!( pdkim_strcat(hdr,"l=")                             &&
+             pdkim_numcat(hdr,sig->bodylength)                  &&
+             pdkim_strcat(hdr,";") ) ) {
+        goto BAIL;
+      }
+    }
+    /* Extra linebreak */
+    if (hdr->str[(hdr->len)-1] == ';') {
+      if (!pdkim_strcat(hdr," \r\n\t")) goto BAIL;
+    }
+    /* Preliminary or final version? */
+    if (final) {
+      base64_b = pdkim_encode_base64(sig->sigdata, sig->sigdata_len);
+      if (base64_b == NULL) goto BAIL;
+      if (
+            pdkim_strcat(hdr,"b=")                              &&
+            pdkim_strcat(hdr,base64_b)                          &&
+            pdkim_strcat(hdr,";")
+         ) goto DONE;
+    }
+    else {
+      if (pdkim_strcat(hdr,"b=;")) goto DONE;
+    }
+
+    goto BAIL;
+  }
+
+  DONE:
+  rc = strdup(hdr->str);
+
+  BAIL:
+  pdkim_strfree(hdr);
+  if (base64_bh != NULL) free(base64_bh);
+  if (base64_b  != NULL) free(base64_b);
+  return rc;
+}
+
+
+/* -------------------------------------------------------------------------- */
+DLLEXPORT int pdkim_feed_finish(pdkim_ctx *ctx, pdkim_signature **return_signatures) {
+  pdkim_signature *sig = ctx->sig;
+  pdkim_str *headernames = NULL;             /* Collected signed header names */
+
+  /* Check if we must still flush a (partial) header. If that is the
+     case, the message has no body, and we must compute a body hash
+     out of '<CR><LF>' */
+  if (ctx->cur_header->len) {
+    int rc = pdkim_header_complete(ctx);
+    if (rc != PDKIM_OK) return rc;
+    pdkim_update_bodyhash(ctx,"\r\n",2);
+  }
+  else {
+    /* For non-smtp input, check if there's an unfinished line in the
+       body line buffer. If that is the case, we must add a CRLF to the
+       hash to properly terminate the message. */
+    if ((ctx->input_mode == PDKIM_INPUT_NORMAL) && ctx->linebuf_offset) {
+      pdkim_update_bodyhash(ctx, ctx->linebuf, ctx->linebuf_offset);
+      pdkim_update_bodyhash(ctx,"\r\n",2);
+    }
+    #ifdef PDKIM_DEBUG
+    if (ctx->debug_stream)
+      fprintf(ctx->debug_stream,
+        "\nPDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
+    #endif
+  }
+
+  /* Build (and/or evaluate) body hash */
+  if (pdkim_finish_bodyhash(ctx) != PDKIM_OK) return PDKIM_ERR_OOM;
+
+  /* SIGNING -------------------------------------------------------------- */
+  if (ctx->mode == PDKIM_MODE_SIGN) {
+    headernames = pdkim_strnew(NULL);
+    if (headernames == NULL) return PDKIM_ERR_OOM;
+  }
+  /* ---------------------------------------------------------------------- */
+
+  while (sig != NULL) {
+    sha1_context sha1_headers;
+    sha2_context sha2_headers;
+    char *sig_hdr;
+    char headerhash[32];
+
+    if (sig->algo == PDKIM_ALGO_RSA_SHA1)
+      sha1_starts(&sha1_headers);
+    else
+      sha2_starts(&sha2_headers,0);
+
+    #ifdef PDKIM_DEBUG
+    if (ctx->debug_stream)
+      fprintf(ctx->debug_stream,
+              "PDKIM >> Hashed header data, canonicalized, in sequence >>>>>>>>>>>>>>\n");
+    #endif
+
+    /* SIGNING ---------------------------------------------------------------- */
+    /* When signing, walk through our header list and add them to the hash. As we
+       go, construct a list of the header's names to use for the h= parameter. */
+    if (ctx->mode == PDKIM_MODE_SIGN) {
+      pdkim_stringlist *p = sig->headers;
+      while (p != NULL) {
+        char *rh = NULL;
+        /* Collect header names (Note: colon presence is guaranteed here) */
+        char *q = strchr(p->value,':');
+        if (pdkim_strncat(headernames, p->value,
+                          (q-(p->value))+((p->next==NULL)?0:1)) == NULL)
+          return PDKIM_ERR_OOM;
+
+        if (sig->canon_headers == PDKIM_CANON_RELAXED)
+          rh = pdkim_relax_header(p->value,1); /* cook header for relaxed canon */
+        else
+          rh = strdup(p->value);               /* just copy it for simple canon */
+
+        if (rh == NULL) return PDKIM_ERR_OOM;
+
+        /* Feed header to the hash algorithm */
+        if (sig->algo == PDKIM_ALGO_RSA_SHA1)
+          sha1_update(&(sha1_headers),(unsigned char *)rh,strlen(rh));
+        else
+          sha2_update(&(sha2_headers),(unsigned char *)rh,strlen(rh));
+        #ifdef PDKIM_DEBUG
+        if (ctx->debug_stream)
+          pdkim_quoteprint(ctx->debug_stream, rh, strlen(rh), 1);
+        #endif
+        free(rh);
+        p = p->next;
+      }
+    }
+    /* VERIFICATION ----------------------------------------------------------- */
+    /* When verifying, walk through the header name list in the h= parameter and
+       add the headers to the hash in that order. */
+    else {
+      char *b = strdup(sig->headernames);
+      char *p = b;
+      char *q = NULL;
+      if (b == NULL) return PDKIM_ERR_OOM;
+
+      while(1) {
+        pdkim_stringlist *hdrs = sig->headers;
+        q = strchr(p,':');
+        if (q != NULL) *q = '\0';
+        while (hdrs != NULL) {
+          if (strncasecmp(hdrs->value,p,strlen(p)) == 0) {
+            char *rh = NULL;
+            if (sig->canon_headers == PDKIM_CANON_RELAXED)
+              rh = pdkim_relax_header(hdrs->value,1); /* cook header for relaxed canon */
+            else
+              rh = strdup(hdrs->value);               /* just copy it for simple canon */
+            if (rh == NULL) return PDKIM_ERR_OOM;
+            /* Feed header to the hash algorithm */
+            if (sig->algo == PDKIM_ALGO_RSA_SHA1)
+              sha1_update(&(sha1_headers),(unsigned char *)rh,strlen(rh));
+            else
+              sha2_update(&(sha2_headers),(unsigned char *)rh,strlen(rh));
+            #ifdef PDKIM_DEBUG
+            if (ctx->debug_stream)
+              pdkim_quoteprint(ctx->debug_stream, rh, strlen(rh), 1);
+            #endif
+            free(rh);
+          }
+          hdrs = hdrs->next;
+        }
+        if (q == NULL) break;
+        p = q+1;
+      }
+      free(b);
+    }
+
+    #ifdef PDKIM_DEBUG
+    if (ctx->debug_stream)
+      fprintf(ctx->debug_stream,
+              "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
+    #endif
+
+    /* SIGNING ---------------------------------------------------------------- */
+    if (ctx->mode == PDKIM_MODE_SIGN) {
+      /* Copy headernames to signature struct */
+      sig->headernames = strdup(headernames->str);
+      pdkim_strfree(headernames);
+
+      /* Create signature header with b= omitted */
+      sig_hdr = pdkim_create_header(ctx->sig,0);
+    }
+    /* VERIFICATION ----------------------------------------------------------- */
+    else {
+      sig_hdr = strdup(sig->rawsig_no_b_val);
+    }
+    /* ------------------------------------------------------------------------ */
+
+    if (sig_hdr == NULL) return PDKIM_ERR_OOM;
+
+    /* Relax header if necessary */
+    if (sig->canon_headers == PDKIM_CANON_RELAXED) {
+      char *relaxed_hdr = pdkim_relax_header(sig_hdr,0);
+      free(sig_hdr);
+      if (relaxed_hdr == NULL) return PDKIM_ERR_OOM;
+      sig_hdr = relaxed_hdr;
+    }
+
+    #ifdef PDKIM_DEBUG
+    if (ctx->debug_stream) {
+      fprintf(ctx->debug_stream,
+              "PDKIM >> Signed DKIM-Signature header, canonicalized >>>>>>>>>>>>>>>>>\n");
+      pdkim_quoteprint(ctx->debug_stream, sig_hdr, strlen(sig_hdr), 1);
+      fprintf(ctx->debug_stream,
+              "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
+    }
+    #endif
+
+    /* Finalize header hash */
+    if (sig->algo == PDKIM_ALGO_RSA_SHA1) {
+      sha1_update(&(sha1_headers),(unsigned char *)sig_hdr,strlen(sig_hdr));
+      sha1_finish(&(sha1_headers),(unsigned char *)headerhash);
+      #ifdef PDKIM_DEBUG
+      if (ctx->debug_stream) {
+        fprintf(ctx->debug_stream, "PDKIM [%s] hh computed: ", sig->domain);
+        pdkim_hexprint(ctx->debug_stream, headerhash, 20, 1);
+      }
+      #endif
+    }
+    else {
+      sha2_update(&(sha2_headers),(unsigned char *)sig_hdr,strlen(sig_hdr));
+      sha2_finish(&(sha2_headers),(unsigned char *)headerhash);
+      #ifdef PDKIM_DEBUG
+      if (ctx->debug_stream) {
+        fprintf(ctx->debug_stream, "PDKIM [%s] hh computed: ", sig->domain);
+        pdkim_hexprint(ctx->debug_stream, headerhash, 32, 1);
+      }
+      #endif
+    }
+
+    free(sig_hdr);
+
+    /* SIGNING ---------------------------------------------------------------- */
+    if (ctx->mode == PDKIM_MODE_SIGN) {
+      rsa_context rsa;
+
+      rsa_init(&rsa,RSA_PKCS_V15,0,NULL,NULL);
+
+      /* Perform private key operation */
+      if (rsa_parse_key(&rsa, (unsigned char *)sig->rsa_privkey,
+                        strlen(sig->rsa_privkey), NULL, 0) != 0) {
+        return PDKIM_ERR_RSA_PRIVKEY;
+      }
+
+      sig->sigdata_len = mpi_size(&(rsa.N));
+      sig->sigdata = malloc(sig->sigdata_len);
+      if (sig->sigdata == NULL) return PDKIM_ERR_OOM;
+
+      if (rsa_pkcs1_sign( &rsa, RSA_PRIVATE,
+                          ((sig->algo == PDKIM_ALGO_RSA_SHA1)?
+                             RSA_SHA1:RSA_SHA256),
+                          0,
+                          (unsigned char *)headerhash,
+                          (unsigned char *)sig->sigdata ) != 0) {
+        return PDKIM_ERR_RSA_SIGNING;
+      }
+
+      rsa_free(&rsa);
+
+      #ifdef PDKIM_DEBUG
+      if (ctx->debug_stream) {
+        fprintf(ctx->debug_stream, "PDKIM [%s] b computed: ",
+                sig->domain);
+        pdkim_hexprint(ctx->debug_stream, sig->sigdata, sig->sigdata_len, 1);
+      }
+      #endif
+
+      sig->signature_header = pdkim_create_header(ctx->sig,1);
+      if (sig->signature_header == NULL) return PDKIM_ERR_OOM;
+    }
+    /* VERIFICATION ----------------------------------------------------------- */
+    else {
+      rsa_context rsa;
+      char *dns_txt_name, *dns_txt_reply;
+
+      rsa_init(&rsa,RSA_PKCS_V15,0,NULL,NULL);
+
+      dns_txt_name  = malloc(PDKIM_DNS_TXT_MAX_NAMELEN);
+      if (dns_txt_name == NULL) return PDKIM_ERR_OOM;
+      dns_txt_reply = malloc(PDKIM_DNS_TXT_MAX_RECLEN);
+      if (dns_txt_reply == NULL) {
+        free(dns_txt_name);
+        return PDKIM_ERR_OOM;
+      }
+      memset(dns_txt_reply,0,PDKIM_DNS_TXT_MAX_RECLEN);
+      memset(dns_txt_name ,0,PDKIM_DNS_TXT_MAX_NAMELEN);
+
+      if (snprintf(dns_txt_name,PDKIM_DNS_TXT_MAX_NAMELEN,
+                   "%s._domainkey.%s.",
+                   sig->selector,sig->domain) >= PDKIM_DNS_TXT_MAX_NAMELEN) {
+        sig->verify_status =      PDKIM_VERIFY_INVALID;
+        sig->verify_ext_status =  PDKIM_VERIFY_INVALID_BUFFER_SIZE;
+        goto NEXT_VERIFY;
+      };
+
+      if ((ctx->dns_txt_callback(dns_txt_name, dns_txt_reply) != PDKIM_OK) ||
+          (dns_txt_reply[0] == '\0')) {
+        sig->verify_status =      PDKIM_VERIFY_INVALID;
+        sig->verify_ext_status =  PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE;
+        goto NEXT_VERIFY;
+      }
+
+      #ifdef PDKIM_DEBUG
+      if (ctx->debug_stream) {
+        fprintf(ctx->debug_stream,
+                "PDKIM >> Parsing public key record >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
+        fprintf(ctx->debug_stream,"Raw record: ");
+        pdkim_quoteprint(ctx->debug_stream, dns_txt_reply, strlen(dns_txt_reply), 1);
+      }
+      #endif
+
+      sig->pubkey = pdkim_parse_pubkey_record(ctx,dns_txt_reply);
+      if (sig->pubkey == NULL) {
+        sig->verify_status =      PDKIM_VERIFY_INVALID;
+        sig->verify_ext_status =  PDKIM_VERIFY_INVALID_PUBKEY_PARSING;
+        #ifdef PDKIM_DEBUG
+        if (ctx->debug_stream) {
+          fprintf(ctx->debug_stream,"Error while parsing public key record\n");
+          fprintf(ctx->debug_stream,
+            "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
+        }
+        #endif
+        goto NEXT_VERIFY;
+      }
+
+      #ifdef PDKIM_DEBUG
+      if (ctx->debug_stream) {
+        fprintf(ctx->debug_stream,
+          "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
+      }
+      #endif
+
+      if (rsa_parse_public_key(&rsa,
+                              (unsigned char *)sig->pubkey->key,
+                               sig->pubkey->key_len) != 0) {
+        sig->verify_status =      PDKIM_VERIFY_INVALID;
+        sig->verify_ext_status =  PDKIM_VERIFY_INVALID_PUBKEY_PARSING;
+        goto NEXT_VERIFY;
+      }
+
+      /* Check the signature */
+      if (rsa_pkcs1_verify(&rsa,
+                        RSA_PUBLIC,
+                        ((sig->algo == PDKIM_ALGO_RSA_SHA1)?
+                             RSA_SHA1:RSA_SHA256),
+                        0,
+                        (unsigned char *)headerhash,
+                        (unsigned char *)sig->sigdata) != 0) {
+        sig->verify_status =      PDKIM_VERIFY_FAIL;
+        sig->verify_ext_status =  PDKIM_VERIFY_FAIL_MESSAGE;
+        #ifdef PDKIM_DEBUG
+        if (ctx->debug_stream) {
+          fprintf(ctx->debug_stream, "PDKIM [%s] signature did NOT verify OK\n",
+                  sig->domain);
+        }
+        #endif
+        goto NEXT_VERIFY;
+      }
+
+      /* We have a winner! */
+      sig->verify_status = PDKIM_VERIFY_PASS;
+
+      #ifdef PDKIM_DEBUG
+      if (ctx->debug_stream) {
+        fprintf(ctx->debug_stream, "PDKIM [%s] signature verified OK\n",
+                sig->domain);
+      }
+      #endif
+
+      NEXT_VERIFY:
+      rsa_free(&rsa);
+      free(dns_txt_name);
+      free(dns_txt_reply);
+    }
+
+    sig = sig->next;
+  }
+
+  /* If requested, set return pointer to signature(s) */
+  if (return_signatures != NULL) {
+    *return_signatures = ctx->sig;
+  }
+
+  return PDKIM_OK;
+}
+
+
+/* -------------------------------------------------------------------------- */
+DLLEXPORT pdkim_ctx *pdkim_init_verify(int input_mode,
+                             int(*dns_txt_callback)(char *, char *)
+                             ) {
+  pdkim_ctx *ctx = malloc(sizeof(pdkim_ctx));
+  if (ctx == NULL) return NULL;
+  memset(ctx,0,sizeof(pdkim_ctx));
+
+  ctx->linebuf = malloc(PDKIM_MAX_BODY_LINE_LEN);
+  if (ctx->linebuf == NULL) {
+    free(ctx);
+    return NULL;
+  }
+
+  ctx->mode = PDKIM_MODE_VERIFY;
+  ctx->input_mode = input_mode;
+  ctx->dns_txt_callback = dns_txt_callback;
+
+  return ctx;
+}
+
+
+/* -------------------------------------------------------------------------- */
+DLLEXPORT pdkim_ctx *pdkim_init_sign(int input_mode,
+                           char *domain,
+                           char *selector,
+                           char *rsa_privkey) {
+  pdkim_ctx *ctx;
+  pdkim_signature *sig;
+
+  if (!domain || !selector || !rsa_privkey) return NULL;
+
+  ctx = malloc(sizeof(pdkim_ctx));
+  if (ctx == NULL) return NULL;
+  memset(ctx,0,sizeof(pdkim_ctx));
+
+  ctx->linebuf = malloc(PDKIM_MAX_BODY_LINE_LEN);
+  if (ctx->linebuf == NULL) {
+    free(ctx);
+    return NULL;
+  }
+
+  sig = malloc(sizeof(pdkim_signature));
+  if (sig == NULL) {
+    free(ctx->linebuf);
+    free(ctx);
+    return NULL;
+  }
+  memset(sig,0,sizeof(pdkim_signature));
+  sig->bodylength = -1;
+
+  ctx->mode = PDKIM_MODE_SIGN;
+  ctx->input_mode = input_mode;
+  ctx->sig = sig;
+
+  ctx->sig->domain = strdup(domain);
+  ctx->sig->selector = strdup(selector);
+  ctx->sig->rsa_privkey = strdup(rsa_privkey);
+
+  if (!ctx->sig->domain || !ctx->sig->selector || !ctx->sig->rsa_privkey) {
+    pdkim_free_ctx(ctx);
+    return NULL;
+  }
+
+  ctx->sig->sha1_body = malloc(sizeof(sha1_context));
+  if (ctx->sig->sha1_body == NULL) {
+    pdkim_free_ctx(ctx);
+    return NULL;
+  }
+  sha1_starts(ctx->sig->sha1_body);
+
+  ctx->sig->sha2_body = malloc(sizeof(sha2_context));
+  if (ctx->sig->sha2_body == NULL) {
+    pdkim_free_ctx(ctx);
+    return NULL;
+  }
+  sha2_starts(ctx->sig->sha2_body,0);
+
+  return ctx;
+};
+
+#ifdef PDKIM_DEBUG
+/* -------------------------------------------------------------------------- */
+DLLEXPORT void pdkim_set_debug_stream(pdkim_ctx *ctx,
+                            FILE *debug_stream) {
+  ctx->debug_stream = debug_stream;
+};
+#endif
+
+/* -------------------------------------------------------------------------- */
+DLLEXPORT int pdkim_set_optional(pdkim_ctx *ctx,
+                       char *sign_headers,
+                       char *identity,
+                       int canon_headers,
+                       int canon_body,
+                       long bodylength,
+                       int algo,
+                       unsigned long created,
+                       unsigned long expires) {
+
+  if (identity != NULL) {
+    ctx->sig->identity = strdup(identity);
+    if (ctx->sig->identity == NULL) return PDKIM_ERR_OOM;
+  }
+
+  if (sign_headers != NULL) {
+    ctx->sig->sign_headers = strdup(sign_headers);
+    if (ctx->sig->sign_headers == NULL) return PDKIM_ERR_OOM;
+  }
+
+  ctx->sig->canon_headers = canon_headers;
+  ctx->sig->canon_body = canon_body;
+  ctx->sig->bodylength = bodylength;
+  ctx->sig->algo = algo;
+  ctx->sig->created = created;
+  ctx->sig->expires = expires;
+
+  return PDKIM_OK;
+};
diff --git a/src/src/pdkim/pdkim.h b/src/src/pdkim/pdkim.h
new file mode 100644 (file)
index 0000000..a6a2f76
--- /dev/null
@@ -0,0 +1,325 @@
+/*
+ *  PDKIM - a RFC4871 (DKIM) implementation
+ *
+ *  Copyright (C) 2009  Tom Kistner <tom@duncanthrax.net>
+ *
+ *  http://duncanthrax.net/pdkim/
+ *
+ *  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.
+ */
+
+/* $Cambridge: exim/src/src/pdkim/pdkim.h,v 1.2 2009/06/10 07:34:05 tom Exp $ */
+
+/* -------------------------------------------------------------------------- */
+/* Debugging. This can also be enabled/disabled at run-time. I recommend to
+   leave it defined. */
+#define PDKIM_DEBUG
+
+/* -------------------------------------------------------------------------- */
+/* Length of the preallocated buffer for the "answer" from the dns/txt
+   callback function. */
+#define PDKIM_DNS_TXT_MAX_RECLEN    4096
+
+/* -------------------------------------------------------------------------- */
+/* Function success / error codes */
+#define PDKIM_OK                      0
+#define PDKIM_FAIL                   -1
+#define PDKIM_ERR_OOM              -100
+#define PDKIM_ERR_RSA_PRIVKEY      -101
+#define PDKIM_ERR_RSA_SIGNING      -102
+#define PDKIM_ERR_LONG_LINE        -103
+#define PDKIM_ERR_BUFFER_TOO_SMALL -104
+
+/* -------------------------------------------------------------------------- */
+/* Main/Extended verification status */
+#define PDKIM_VERIFY_NONE      0
+#define PDKIM_VERIFY_INVALID   1
+#define PDKIM_VERIFY_FAIL      2
+#define PDKIM_VERIFY_PASS      3
+
+#define PDKIM_VERIFY_FAIL_BODY                  1
+#define PDKIM_VERIFY_FAIL_MESSAGE               2
+#define PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE 3
+#define PDKIM_VERIFY_INVALID_BUFFER_SIZE        4
+#define PDKIM_VERIFY_INVALID_PUBKEY_PARSING     5
+
+/* -------------------------------------------------------------------------- */
+/* Some parameter values */
+#define PDKIM_QUERYMETHOD_DNS_TXT 0
+
+#define PDKIM_ALGO_RSA_SHA256     0
+#define PDKIM_ALGO_RSA_SHA1       1
+
+#define PDKIM_CANON_SIMPLE        0
+#define PDKIM_CANON_RELAXED       1
+
+#define PDKIM_HASH_SHA256         0
+#define PDKIM_HASH_SHA1           1
+
+#define PDKIM_KEYTYPE_RSA         0
+
+/* -------------------------------------------------------------------------- */
+/* Some required forward declarations, please ignore */
+typedef struct pdkim_stringlist pdkim_stringlist;
+typedef struct pdkim_str pdkim_str;
+typedef struct sha1_context sha1_context;
+typedef struct sha2_context sha2_context;
+#define HAVE_SHA1_CONTEXT
+#define HAVE_SHA2_CONTEXT
+
+/* -------------------------------------------------------------------------- */
+/* Some concessions towards Redmond */
+#ifdef WINDOWS
+#define snprintf _snprintf
+#define strcasecmp _stricmp
+#define strncasecmp _strnicmp
+#define DLLEXPORT __declspec(dllexport)
+#else
+#define DLLEXPORT
+#endif
+
+
+/* -------------------------------------------------------------------------- */
+/* Public key as (usually) fetched from DNS */
+typedef struct pdkim_pubkey {
+  char *version;                  /* v=  */
+  char *granularity;              /* g=  */
+
+  char *hashes;                   /* h=  */
+  char *keytype;                  /* k=  */
+  char *srvtype;                  /* s=  */
+  char *notes;                    /* n=  */
+
+  char *key;                      /* p=  */
+  int   key_len;
+
+  int   testing;                  /* t=y */
+  int   no_subdomaining;          /* t=s */
+} pdkim_pubkey;
+
+/* -------------------------------------------------------------------------- */
+/* Signature as it appears in a DKIM-Signature header */
+typedef struct pdkim_signature {
+
+  /* Bits stored in a DKIM signature header --------------------------- */
+
+  /* (v=) The version, as an integer. Currently, always "1" */
+  int version;
+
+  /* (a=) The signature algorithm. Either PDKIM_ALGO_RSA_SHA256
+     or PDKIM_ALGO_RSA_SHA1 */
+  int algo;
+
+  /* (c=x/) Header canonicalization method. Either PDKIM_CANON_SIMPLE
+     or PDKIM_CANON_RELAXED */
+  int canon_headers;
+
+  /* (c=/x) Body canonicalization method. Either PDKIM_CANON_SIMPLE
+     or PDKIM_CANON_RELAXED */
+  int canon_body;
+
+  /* (q=) Query Method. Currently, only PDKIM_QUERYMETHOD_DNS_TXT
+     is specified */
+  int querymethod;
+
+  /* (s=) The selector string as given in the signature */
+  char *selector;
+
+  /* (d=) The domain as given in the signature */
+  char *domain;
+
+  /* (i=) The identity as given in the signature */
+  char *identity;
+
+  /* (t=) Timestamp of signature creation */
+  unsigned long created;
+
+  /* (x=) Timestamp of expiry of signature */
+  unsigned long expires;
+
+  /* (l=) Amount of hashed body bytes (after canonicalization). Default
+     is -1. Note: a value of 0 means that the body is unsigned! */
+  long bodylength;
+
+  /* (h=) Colon-separated list of header names that are included in the
+     signature */
+  char *headernames;
+
+  /* (z=) */
+  char *copiedheaders;
+
+  /* (b=) Raw signature data, along with its length in bytes */
+  char *sigdata;
+  int   sigdata_len;
+
+  /* (bh=) Raw body hash data, along with its length in bytes */
+  char *bodyhash;
+  int   bodyhash_len;
+
+  /* Folded DKIM-Signature: header. Singing only, NULL for verifying.
+     Ready for insertion into the message. Note: Folded using CRLFTB,
+     but final line terminator is NOT included. Note2: This buffer is
+     free()d when you call pdkim_free_ctx(). */
+  char *signature_header;
+
+  /* The main verification status. Verification only. One of:
+
+     PDKIM_VERIFY_NONE      Verification was not attempted. This status
+                            should not appear.
+
+     PDKIM_VERIFY_INVALID   There was an error while trying to verify
+                            the signature. A more precise description
+                            is available in verify_ext_status.
+
+     PDKIM_VERIFY_FAIL      Verification failed because either the body
+                            hash did not match, or the signature verification
+                            failed. This means the message was modified.
+                            Check verify_ext_status for the exact reason.
+
+     PDKIM_VERIFY_PASS      Verification succeeded.
+  */
+  int verify_status;
+
+  /* Extended verification status. Verification only. Depending on the value
+     of verify_status, it can contain:
+
+     For verify_status == PDKIM_VERIFY_INVALID:
+
+        PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE
+          Unable to retrieve a public key container.
+
+        PDKIM_VERIFY_INVALID_BUFFER_SIZE
+          Either the DNS name constructed to retrieve the public key record
+          does not fit into PDKIM_DNS_TXT_MAX_NAMELEN bytes, or the retrieved
+          record is longer than PDKIM_DNS_TXT_MAX_RECLEN bytes.
+
+        PDKIM_VERIFY_INVALID_PUBKEY_PARSING
+          (Syntax) error while parsing the retrieved public key record.
+
+
+     For verify_status == PDKIM_VERIFY_FAIL:
+
+        PDKIM_VERIFY_FAIL_BODY
+          The calculated body hash does not match the advertised body hash
+          from the bh= tag of the signature.
+
+        PDKIM_VERIFY_FAIL_MESSAGE
+          RSA verification of the signature (b= tag) failed.
+  */
+  int verify_ext_status;
+
+  /* Pointer to a public key record that was used to verify the signature.
+     See pdkim_pubkey declaration above for more information.
+     Caution: is NULL if signing or if no record was retrieved. */
+  pdkim_pubkey *pubkey;
+
+  /* Pointer to the next pdkim_signature signature. NULL if signing or if
+     this is the last signature. */
+  void *next;
+
+  /* Properties below this point are used internally only ------------- */
+
+  /* Per-signature helper variables ----------------------------------- */
+  sha1_context *sha1_body; /* SHA1 block                                */
+  sha2_context *sha2_body; /* SHA256 block                              */
+  unsigned long signed_body_bytes; /* How many body bytes we hashed     */
+  pdkim_stringlist *headers; /* Raw headers included in the sig         */
+  /* Signing specific ------------------------------------------------- */
+  char *rsa_privkey;     /* Private RSA key                             */
+  char *sign_headers;    /* To-be-signed header names                   */
+  /* Verification specific -------------------------------------------- */
+  char *hnames_check;    /* Tick-off header list that we use to keep
+                            track of header names that we have already
+                            added to the signature candidates.          */
+  char *rawsig_no_b_val; /* Original signature header w/o b= tag value. */
+} pdkim_signature;
+
+
+/* -------------------------------------------------------------------------- */
+/* Context to keep state between all operations. */
+#define PDKIM_MODE_SIGN     0
+#define PDKIM_MODE_VERIFY   1
+#define PDKIM_INPUT_NORMAL  0
+#define PDKIM_INPUT_SMTP    1
+typedef struct pdkim_ctx {
+
+  /* PDKIM_MODE_VERIFY or PDKIM_MODE_SIGN */
+  int mode;
+
+  /* PDKIM_INPUT_SMTP or PDKIM_INPUT_NORMAL */
+  int input_mode;
+
+  /* One (signing) or several chained (verification) signatures */
+  pdkim_signature *sig;
+
+  /* Callback for dns/txt query method (verification only) */
+  int(*dns_txt_callback)(char *, char *);
+
+  /* Coder's little helpers */
+  pdkim_str *cur_header;
+  char      *linebuf;
+  int        linebuf_offset;
+  int        seen_lf;
+  int        seen_eod;
+  int        past_headers;
+  int        num_buffered_crlf;
+  int        num_headers;
+
+#ifdef PDKIM_DEBUG
+  /* A FILE pointer. When not NULL, debug output will be generated
+    and sent to this stream */
+  FILE *debug_stream;
+#endif
+
+} pdkim_ctx;
+
+
+/* -------------------------------------------------------------------------- */
+/* API functions. Please see the sample code in sample/test_sign.c and
+   sample/test_verify.c for documentation.
+*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+DLLEXPORT
+pdkim_ctx *pdkim_init_sign    (int, char *, char *, char *);
+
+DLLEXPORT
+pdkim_ctx *pdkim_init_verify  (int, int(*)(char *, char *));
+
+DLLEXPORT
+int        pdkim_set_optional (pdkim_ctx *, char *, char *,int, int,
+                               long, int,
+                               unsigned long,
+                               unsigned long);
+
+DLLEXPORT
+int        pdkim_feed         (pdkim_ctx *, char *, int);
+DLLEXPORT
+int        pdkim_feed_finish  (pdkim_ctx *, pdkim_signature **);
+
+DLLEXPORT
+void       pdkim_free_ctx     (pdkim_ctx *);
+
+#ifdef PDKIM_DEBUG
+DLLEXPORT
+void       pdkim_set_debug_stream(pdkim_ctx *, FILE *);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/src/src/pdkim/rsa.c b/src/src/pdkim/rsa.c
new file mode 100644 (file)
index 0000000..992a396
--- /dev/null
@@ -0,0 +1,822 @@
+/*
+ *  The RSA public-key cryptosystem
+ *
+ *  Based on XySSL: Copyright (C) 2006-2008  Christophe Devine
+ *
+ *  Copyright (C) 2009  Paul Bakker <polarssl_maintainer at polarssl dot org>
+ *
+ *  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.
+ */
+/*
+ *  RSA was designed by Ron Rivest, Adi Shamir and Len Adleman.
+ *
+ *  http://theory.lcs.mit.edu/~rivest/rsapaper.pdf
+ *  http://www.cacr.math.uwaterloo.ca/hac/about/chap8.pdf
+ */
+
+/* $Cambridge: exim/src/src/pdkim/rsa.c,v 1.2 2009/06/10 07:34:05 tom Exp $ */
+
+#include "rsa.h"
+#include "base64.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+
+/*
+ * ASN.1 DER decoding routines
+ */
+static int asn1_get_len( unsigned char **p,
+                         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 );
+}
+
+static int asn1_get_tag( unsigned char **p,
+                         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 ) );
+}
+
+static int asn1_get_int( unsigned char **p,
+                         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 );
+}
+
+static int asn1_get_mpi( unsigned char **p,
+                         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 );
+}
+
+
+/*
+ * Initialize an RSA context
+ */
+void rsa_init( rsa_context *ctx,
+               int padding,
+               int hash_id,
+               int (*f_rng)(void *),
+               void *p_rng )
+{
+    memset( ctx, 0, sizeof( rsa_context ) );
+
+    ctx->padding = padding;
+    ctx->hash_id = hash_id;
+
+    ctx->f_rng = f_rng;
+    ctx->p_rng = p_rng;
+}
+
+
+/*
+ * Check a public RSA key
+ */
+int rsa_check_pubkey( rsa_context *ctx )
+{
+    if( ( ctx->N.p[0] & 1 ) == 0 ||
+        ( ctx->E.p[0] & 1 ) == 0 )
+        return( POLARSSL_ERR_RSA_KEY_CHECK_FAILED );
+
+    if( mpi_msb( &ctx->N ) < 128 ||
+        mpi_msb( &ctx->N ) > 4096 )
+        return( POLARSSL_ERR_RSA_KEY_CHECK_FAILED );
+
+    if( mpi_msb( &ctx->E ) < 2 ||
+        mpi_msb( &ctx->E ) > 64 )
+        return( POLARSSL_ERR_RSA_KEY_CHECK_FAILED );
+
+    return( 0 );
+}
+
+/*
+ * Check a private RSA key
+ */
+int rsa_check_privkey( rsa_context *ctx )
+{
+    int ret;
+    mpi PQ, DE, P1, Q1, H, I, G;
+
+    if( ( ret = rsa_check_pubkey( ctx ) ) != 0 )
+        return( ret );
+
+    mpi_init( &PQ, &DE, &P1, &Q1, &H, &I, &G, NULL );
+
+    MPI_CHK( mpi_mul_mpi( &PQ, &ctx->P, &ctx->Q ) );
+    MPI_CHK( mpi_mul_mpi( &DE, &ctx->D, &ctx->E ) );
+    MPI_CHK( mpi_sub_int( &P1, &ctx->P, 1 ) );
+    MPI_CHK( mpi_sub_int( &Q1, &ctx->Q, 1 ) );
+    MPI_CHK( mpi_mul_mpi( &H, &P1, &Q1 ) );
+    MPI_CHK( mpi_mod_mpi( &I, &DE, &H  ) );
+    MPI_CHK( mpi_gcd( &G, &ctx->E, &H  ) );
+
+    if( mpi_cmp_mpi( &PQ, &ctx->N ) == 0 &&
+        mpi_cmp_int( &I, 1 ) == 0 &&
+        mpi_cmp_int( &G, 1 ) == 0 )
+    {
+        mpi_free( &G, &I, &H, &Q1, &P1, &DE, &PQ, NULL );
+        return( 0 );
+    }
+
+cleanup:
+
+    mpi_free( &G, &I, &H, &Q1, &P1, &DE, &PQ, NULL );
+    return( POLARSSL_ERR_RSA_KEY_CHECK_FAILED | ret );
+}
+
+/*
+ * Do an RSA public key operation
+ */
+int rsa_public( rsa_context *ctx,
+                unsigned char *input,
+                unsigned char *output )
+{
+    int ret, olen;
+    mpi T;
+
+    mpi_init( &T, NULL );
+
+    MPI_CHK( mpi_read_binary( &T, input, ctx->len ) );
+
+    if( mpi_cmp_mpi( &T, &ctx->N ) >= 0 )
+    {
+        mpi_free( &T, NULL );
+        return( POLARSSL_ERR_RSA_BAD_INPUT_DATA );
+    }
+
+    olen = ctx->len;
+    MPI_CHK( mpi_exp_mod( &T, &T, &ctx->E, &ctx->N, &ctx->RN ) );
+    MPI_CHK( mpi_write_binary( &T, output, olen ) );
+
+cleanup:
+
+    mpi_free( &T, NULL );
+
+    if( ret != 0 )
+        return( POLARSSL_ERR_RSA_PUBLIC_FAILED | ret );
+
+    return( 0 );
+}
+
+/*
+ * Do an RSA private key operation
+ */
+int rsa_private( rsa_context *ctx,
+                 unsigned char *input,
+                 unsigned char *output )
+{
+    int ret, olen;
+    mpi T, T1, T2;
+
+    mpi_init( &T, &T1, &T2, NULL );
+
+    MPI_CHK( mpi_read_binary( &T, input, ctx->len ) );
+
+    if( mpi_cmp_mpi( &T, &ctx->N ) >= 0 )
+    {
+        mpi_free( &T, NULL );
+        return( POLARSSL_ERR_RSA_BAD_INPUT_DATA );
+    }
+
+#if 0
+    MPI_CHK( mpi_exp_mod( &T, &T, &ctx->D, &ctx->N, &ctx->RN ) );
+#else
+    /*
+     * faster decryption using the CRT
+     *
+     * T1 = input ^ dP mod P
+     * T2 = input ^ dQ mod Q
+     */
+    MPI_CHK( mpi_exp_mod( &T1, &T, &ctx->DP, &ctx->P, &ctx->RP ) );
+    MPI_CHK( mpi_exp_mod( &T2, &T, &ctx->DQ, &ctx->Q, &ctx->RQ ) );
+
+    /*
+     * T = (T1 - T2) * (Q^-1 mod P) mod P
+     */
+    MPI_CHK( mpi_sub_mpi( &T, &T1, &T2 ) );
+    MPI_CHK( mpi_mul_mpi( &T1, &T, &ctx->QP ) );
+    MPI_CHK( mpi_mod_mpi( &T, &T1, &ctx->P ) );
+
+    /*
+     * output = T2 + T * Q
+     */
+    MPI_CHK( mpi_mul_mpi( &T1, &T, &ctx->Q ) );
+    MPI_CHK( mpi_add_mpi( &T, &T2, &T1 ) );
+#endif
+
+    olen = ctx->len;
+    MPI_CHK( mpi_write_binary( &T, output, olen ) );
+
+cleanup:
+
+    mpi_free( &T, &T1, &T2, NULL );
+
+    if( ret != 0 )
+        return( POLARSSL_ERR_RSA_PRIVATE_FAILED | ret );
+
+    return( 0 );
+}
+
+/*
+ * Add the message padding, then do an RSA operation
+ */
+int rsa_pkcs1_encrypt( rsa_context *ctx,
+                       int mode, int  ilen,
+                       unsigned char *input,
+                       unsigned char *output )
+{
+    int nb_pad, olen;
+    unsigned char *p = output;
+
+    olen = ctx->len;
+
+    switch( ctx->padding )
+    {
+        case RSA_PKCS_V15:
+
+            if( ilen < 0 || olen < ilen + 11 )
+                return( POLARSSL_ERR_RSA_BAD_INPUT_DATA );
+
+            nb_pad = olen - 3 - ilen;
+
+            *p++ = 0;
+            *p++ = RSA_CRYPT;
+
+            while( nb_pad-- > 0 )
+            {
+                do {
+                    *p = (unsigned char) rand();
+                } while( *p == 0 );
+                p++;
+            }
+            *p++ = 0;
+            memcpy( p, input, ilen );
+            break;
+
+        default:
+
+            return( POLARSSL_ERR_RSA_INVALID_PADDING );
+    }
+
+    return( ( mode == RSA_PUBLIC )
+            ? rsa_public(  ctx, output, output )
+            : rsa_private( ctx, output, output ) );
+}
+
+/*
+ * Do an RSA operation, then remove the message padding
+ */
+int rsa_pkcs1_decrypt( rsa_context *ctx,
+                       int mode, int *olen,
+                       unsigned char *input,
+                       unsigned char *output,
+               int output_max_len)
+{
+    int ret, ilen;
+    unsigned char *p;
+    unsigned char buf[512];
+
+    ilen = ctx->len;
+
+    if( ilen < 16 || ilen > (int) sizeof( buf ) )
+        return( POLARSSL_ERR_RSA_BAD_INPUT_DATA );
+
+    ret = ( mode == RSA_PUBLIC )
+          ? rsa_public(  ctx, input, buf )
+          : rsa_private( ctx, input, buf );
+
+    if( ret != 0 )
+        return( ret );
+
+    p = buf;
+
+    switch( ctx->padding )
+    {
+        case RSA_PKCS_V15:
+
+            if( *p++ != 0 || *p++ != RSA_CRYPT )
+                return( POLARSSL_ERR_RSA_INVALID_PADDING );
+
+            while( *p != 0 )
+            {
+                if( p >= buf + ilen - 1 )
+                    return( POLARSSL_ERR_RSA_INVALID_PADDING );
+                p++;
+            }
+            p++;
+            break;
+
+        default:
+
+            return( POLARSSL_ERR_RSA_INVALID_PADDING );
+    }
+
+    if (ilen - (int)(p - buf) > output_max_len)
+        return( POLARSSL_ERR_RSA_OUTPUT_TO_LARGE );
+
+    *olen = ilen - (int)(p - buf);
+    memcpy( output, p, *olen );
+
+    return( 0 );
+}
+
+/*
+ * Do an RSA operation to sign the message digest
+ */
+int rsa_pkcs1_sign( rsa_context *ctx,
+                    int mode,
+                    int hash_id,
+                    int hashlen,
+                    unsigned char *hash,
+                    unsigned char *sig )
+{
+    int nb_pad, olen;
+    unsigned char *p = sig;
+
+    olen = ctx->len;
+
+    switch( ctx->padding )
+    {
+        case RSA_PKCS_V15:
+
+            switch( hash_id )
+            {
+                case RSA_RAW:
+                    nb_pad = olen - 3 - hashlen;
+                    break;
+
+                case RSA_MD2:
+                case RSA_MD4:
+                case RSA_MD5:
+                    nb_pad = olen - 3 - 16 - 18;
+                    break;
+
+                case RSA_SHA1:
+                    nb_pad = olen - 3 - 20 - 15;
+                    break;
+
+                case RSA_SHA256:
+                    nb_pad = olen - 3 - 32 - 19;
+                    break;
+
+                default:
+                    return( POLARSSL_ERR_RSA_BAD_INPUT_DATA );
+            }
+
+            if( nb_pad < 8 )
+                return( POLARSSL_ERR_RSA_BAD_INPUT_DATA );
+
+            *p++ = 0;
+            *p++ = RSA_SIGN;
+            memset( p, 0xFF, nb_pad );
+            p += nb_pad;
+            *p++ = 0;
+            break;
+
+        default:
+
+            return( POLARSSL_ERR_RSA_INVALID_PADDING );
+    }
+
+    switch( hash_id )
+    {
+        case RSA_RAW:
+            memcpy( p, hash, hashlen );
+            break;
+
+        case RSA_MD2:
+            memcpy( p, ASN1_HASH_MDX, 18 );
+            memcpy( p + 18, hash, 16 );
+            p[13] = 2; break;
+
+        case RSA_MD4:
+            memcpy( p, ASN1_HASH_MDX, 18 );
+            memcpy( p + 18, hash, 16 );
+            p[13] = 4; break;
+
+        case RSA_MD5:
+            memcpy( p, ASN1_HASH_MDX, 18 );
+            memcpy( p + 18, hash, 16 );
+            p[13] = 5; break;
+
+        case RSA_SHA1:
+            memcpy( p, ASN1_HASH_SHA1, 15 );
+            memcpy( p + 15, hash, 20 );
+            break;
+
+        case RSA_SHA256:
+            memcpy( p, ASN1_HASH_SHA256, 19 );
+            memcpy( p + 19, hash, 32 );
+            break;
+
+        default:
+            return( POLARSSL_ERR_RSA_BAD_INPUT_DATA );
+    }
+
+    return( ( mode == RSA_PUBLIC )
+            ? rsa_public(  ctx, sig, sig )
+            : rsa_private( ctx, sig, sig ) );
+}
+
+/*
+ * Do an RSA operation and check the message digest
+ */
+int rsa_pkcs1_verify( rsa_context *ctx,
+                      int mode,
+                      int hash_id,
+                      int hashlen,
+                      unsigned char *hash,
+                      unsigned char *sig )
+{
+    int ret, len, siglen;
+    unsigned char *p, c;
+    unsigned char buf[512];
+
+    siglen = ctx->len;
+
+    if( siglen < 16 || siglen > (int) sizeof( buf ) )
+        return( POLARSSL_ERR_RSA_BAD_INPUT_DATA );
+
+    ret = ( mode == RSA_PUBLIC )
+          ? rsa_public(  ctx, sig, buf )
+          : rsa_private( ctx, sig, buf );
+
+    if( ret != 0 )
+        return( ret );
+
+    p = buf;
+
+    switch( ctx->padding )
+    {
+        case RSA_PKCS_V15:
+
+            if( *p++ != 0 || *p++ != RSA_SIGN )
+                return( POLARSSL_ERR_RSA_INVALID_PADDING );
+
+            while( *p != 0 )
+            {
+                if( p >= buf + siglen - 1 || *p != 0xFF )
+                    return( POLARSSL_ERR_RSA_INVALID_PADDING );
+                p++;
+            }
+            p++;
+            break;
+
+        default:
+
+            return( POLARSSL_ERR_RSA_INVALID_PADDING );
+    }
+
+    len = siglen - (int)( p - buf );
+
+    if( len == 34 )
+    {
+        c = p[13];
+        p[13] = 0;
+
+        if( memcmp( p, ASN1_HASH_MDX, 18 ) != 0 )
+            return( POLARSSL_ERR_RSA_VERIFY_FAILED );
+
+        if( ( c == 2 && hash_id == RSA_MD2 ) ||
+            ( c == 4 && hash_id == RSA_MD4 ) ||
+            ( c == 5 && hash_id == RSA_MD5 ) )
+        {
+            if( memcmp( p + 18, hash, 16 ) == 0 )
+                return( 0 );
+            else
+                return( POLARSSL_ERR_RSA_VERIFY_FAILED );
+        }
+    }
+
+    if( len == 35 && hash_id == RSA_SHA1 )
+    {
+        if( memcmp( p, ASN1_HASH_SHA1, 15 ) == 0 &&
+            memcmp( p + 15, hash, 20 ) == 0 )
+            return( 0 );
+        else
+            return( POLARSSL_ERR_RSA_VERIFY_FAILED );
+    }
+
+    if( len == 51 && hash_id == RSA_SHA256 )
+    {
+        if( memcmp( p, ASN1_HASH_SHA256, 19 ) == 0 &&
+            memcmp( p + 19, hash, 32 ) == 0 )
+            return( 0 );
+        else
+            return( POLARSSL_ERR_RSA_VERIFY_FAILED );
+    }
+
+    if( len == hashlen && hash_id == RSA_RAW )
+    {
+        if( memcmp( p, hash, hashlen ) == 0 )
+            return( 0 );
+        else
+            return( POLARSSL_ERR_RSA_VERIFY_FAILED );
+    }
+
+    return( POLARSSL_ERR_RSA_INVALID_PADDING );
+}
+
+/*
+ * Free the components of an RSA key
+ */
+void rsa_free( rsa_context *ctx )
+{
+    mpi_free( &ctx->RQ, &ctx->RP, &ctx->RN,
+              &ctx->QP, &ctx->DQ, &ctx->DP,
+              &ctx->Q,  &ctx->P,  &ctx->D,
+              &ctx->E,  &ctx->N,  NULL );
+}
+
+
+/*
+ * Parse a public RSA key
+
+OpenSSL RSA public key ASN1 container
+  0:d=0  hl=3 l= 159 cons: SEQUENCE
+  3:d=1  hl=2 l=  13 cons: SEQUENCE
+  5:d=2  hl=2 l=   9 prim: OBJECT:rsaEncryption
+ 16:d=2  hl=2 l=   0 prim: NULL
+ 18:d=1  hl=3 l= 141 prim: BIT STRING:RSAPublicKey (below)
+
+RSAPublicKey ASN1 container
+  0:d=0  hl=3 l= 137 cons: SEQUENCE
+  3:d=1  hl=3 l= 129 prim: INTEGER:Public modulus
+135:d=1  hl=2 l=   3 prim: INTEGER:Public exponent
+*/
+
+int rsa_parse_public_key( rsa_context *rsa, unsigned char *buf, int buflen )
+{
+    unsigned char *p, *end;
+    int ret, len;
+
+    p = buf;
+    end = buf+buflen;
+
+    if( ( ret = asn1_get_tag( &p, end, &len,
+            ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) {
+        return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT | ret );
+    }
+
+    if( ( ret = asn1_get_tag( &p, end, &len,
+            ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) == 0 ) {
+        /* Skip over embedded rsaEncryption Object */
+        p+=len;
+
+        /* The RSAPublicKey ASN1 container is wrapped in a BIT STRING */
+        if( ( ret = asn1_get_tag( &p, end, &len,
+                ASN1_BIT_STRING ) ) != 0 ) {
+            return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT | ret );
+        }
+
+        /* Limit range to that BIT STRING */
+        end = p + len;
+        p++;
+
+        if( ( ret = asn1_get_tag( &p, end, &len,
+                ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) {
+            return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT | ret );
+        }
+    }
+
+    if ( ( ( ret = asn1_get_mpi( &p, end, &(rsa->N)  ) ) == 0 ) &&
+         ( ( ret = asn1_get_mpi( &p, end, &(rsa->E)  ) ) == 0 ) ) {
+        rsa->len = mpi_size( &rsa->N );
+        return 0;
+    }
+
+    return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT | ret );
+}
+
+/*
+ * Parse a private RSA key
+ */
+int rsa_parse_key( rsa_context *rsa, unsigned char *buf, int buflen,
+                                     unsigned char *pwd, int pwdlen )
+{
+    int ret, len, enc;
+    unsigned char *s1, *s2;
+    unsigned char *p, *end;
+
+    s1 = (unsigned char *) strstr( (char *) buf,
+        "-----BEGIN RSA PRIVATE KEY-----" );
+
+    if( s1 != NULL )
+    {
+        s2 = (unsigned char *) strstr( (char *) buf,
+            "-----END RSA PRIVATE KEY-----" );
+
+        if( s2 == NULL || s2 <= s1 )
+            return( POLARSSL_ERR_X509_KEY_INVALID_PEM );
+
+        s1 += 31;
+        if( *s1 == '\r' ) s1++;
+        if( *s1 == '\n' ) s1++;
+            else return( POLARSSL_ERR_X509_KEY_INVALID_PEM );
+
+        enc = 0;
+
+        if( memcmp( s1, "Proc-Type: 4,ENCRYPTED", 22 ) == 0 )
+        {
+            return( POLARSSL_ERR_X509_FEATURE_UNAVAILABLE );
+        }
+
+        len = 0;
+        ret = base64_decode( NULL, &len, s1, s2 - s1 );
+
+        if( ret == POLARSSL_ERR_BASE64_INVALID_CHARACTER )
+            return( ret | POLARSSL_ERR_X509_KEY_INVALID_PEM );
+
+        if( ( buf = (unsigned char *) malloc( len ) ) == NULL )
+            return( 1 );
+
+        if( ( ret = base64_decode( buf, &len, s1, s2 - s1 ) ) != 0 )
+        {
+            free( buf );
+            return( ret | POLARSSL_ERR_X509_KEY_INVALID_PEM );
+        }
+
+        buflen = len;
+
+        if( enc != 0 )
+        {
+            return( POLARSSL_ERR_X509_FEATURE_UNAVAILABLE );
+        }
+    }
+
+    memset( rsa, 0, sizeof( rsa_context ) );
+
+    p = buf;
+    end = buf + buflen;
+
+    /*
+     *  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( ( ret = asn1_get_tag( &p, end, &len,
+            ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 )
+    {
+        if( s1 != NULL )
+            free( buf );
+
+        rsa_free( rsa );
+        return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT | ret );
+    }
+
+    end = p + len;
+
+    if( ( ret = asn1_get_int( &p, end, &rsa->ver ) ) != 0 )
+    {
+        if( s1 != NULL )
+            free( buf );
+
+        rsa_free( rsa );
+        return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT | ret );
+    }
+
+    if( rsa->ver != 0 )
+    {
+        if( s1 != NULL )
+            free( buf );
+
+        rsa_free( rsa );
+        return( ret | POLARSSL_ERR_X509_KEY_INVALID_VERSION );
+    }
+
+    if( ( ret = asn1_get_mpi( &p, end, &rsa->N  ) ) != 0 ||
+        ( ret = asn1_get_mpi( &p, end, &rsa->E  ) ) != 0 ||
+        ( ret = asn1_get_mpi( &p, end, &rsa->D  ) ) != 0 ||
+        ( ret = asn1_get_mpi( &p, end, &rsa->P  ) ) != 0 ||
+        ( ret = asn1_get_mpi( &p, end, &rsa->Q  ) ) != 0 ||
+        ( ret = asn1_get_mpi( &p, end, &rsa->DP ) ) != 0 ||
+        ( ret = asn1_get_mpi( &p, end, &rsa->DQ ) ) != 0 ||
+        ( ret = asn1_get_mpi( &p, end, &rsa->QP ) ) != 0 )
+    {
+        if( s1 != NULL )
+            free( buf );
+
+        rsa_free( rsa );
+        return( ret | POLARSSL_ERR_X509_KEY_INVALID_FORMAT );
+    }
+
+    rsa->len = mpi_size( &rsa->N );
+
+    if( p != end )
+    {
+        if( s1 != NULL )
+            free( buf );
+
+        rsa_free( rsa );
+        return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT |
+                POLARSSL_ERR_ASN1_LENGTH_MISMATCH );
+    }
+
+    if( ( ret = rsa_check_privkey( rsa ) ) != 0 )
+    {
+        if( s1 != NULL )
+            free( buf );
+
+        rsa_free( rsa );
+        return( ret );
+    }
+
+    if( s1 != NULL )
+        free( buf );
+
+    return( 0 );
+}
diff --git a/src/src/pdkim/rsa.h b/src/src/pdkim/rsa.h
new file mode 100644 (file)
index 0000000..57a7b24
--- /dev/null
@@ -0,0 +1,356 @@
+/**
+ * \file rsa.h
+ *
+ *  Based on XySSL: Copyright (C) 2006-2008  Christophe Devine
+ *
+ *  Copyright (C) 2009  Paul Bakker <polarssl_maintainer at polarssl dot org>
+ *
+ *  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.
+ */
+
+/* $Cambridge: exim/src/src/pdkim/rsa.h,v 1.2 2009/06/10 07:34:05 tom Exp $ */
+
+#ifndef POLARSSL_RSA_H
+#define POLARSSL_RSA_H
+
+#include "bignum.h"
+
+#define POLARSSL_ERR_RSA_BAD_INPUT_DATA                    -0x0400
+#define POLARSSL_ERR_RSA_INVALID_PADDING                   -0x0410
+#define POLARSSL_ERR_RSA_KEY_GEN_FAILED                    -0x0420
+#define POLARSSL_ERR_RSA_KEY_CHECK_FAILED                  -0x0430
+#define POLARSSL_ERR_RSA_PUBLIC_FAILED                     -0x0440
+#define POLARSSL_ERR_RSA_PRIVATE_FAILED                    -0x0450
+#define POLARSSL_ERR_RSA_VERIFY_FAILED                     -0x0460
+#define POLARSSL_ERR_RSA_OUTPUT_TO_LARGE                   -0x0470
+
+#define POLARSSL_ERR_ASN1_OUT_OF_DATA                      -0x0014
+#define POLARSSL_ERR_ASN1_UNEXPECTED_TAG                   -0x0016
+#define POLARSSL_ERR_ASN1_INVALID_LENGTH                   -0x0018
+#define POLARSSL_ERR_ASN1_LENGTH_MISMATCH                  -0x001A
+#define POLARSSL_ERR_ASN1_INVALID_DATA                     -0x001C
+
+#define POLARSSL_ERR_X509_FEATURE_UNAVAILABLE              -0x0020
+#define POLARSSL_ERR_X509_CERT_INVALID_PEM                 -0x0040
+#define POLARSSL_ERR_X509_CERT_INVALID_FORMAT              -0x0060
+#define POLARSSL_ERR_X509_CERT_INVALID_VERSION             -0x0080
+#define POLARSSL_ERR_X509_CERT_INVALID_SERIAL              -0x00A0
+#define POLARSSL_ERR_X509_CERT_INVALID_ALG                 -0x00C0
+#define POLARSSL_ERR_X509_CERT_INVALID_NAME                -0x00E0
+#define POLARSSL_ERR_X509_CERT_INVALID_DATE                -0x0100
+#define POLARSSL_ERR_X509_CERT_INVALID_PUBKEY              -0x0120
+#define POLARSSL_ERR_X509_CERT_INVALID_SIGNATURE           -0x0140
+#define POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS          -0x0160
+#define POLARSSL_ERR_X509_CERT_UNKNOWN_VERSION             -0x0180
+#define POLARSSL_ERR_X509_CERT_UNKNOWN_SIG_ALG             -0x01A0
+#define POLARSSL_ERR_X509_CERT_UNKNOWN_PK_ALG              -0x01C0
+#define POLARSSL_ERR_X509_CERT_SIG_MISMATCH                -0x01E0
+#define POLARSSL_ERR_X509_CERT_VERIFY_FAILED               -0x0200
+#define POLARSSL_ERR_X509_KEY_INVALID_PEM                  -0x0220
+#define POLARSSL_ERR_X509_KEY_INVALID_VERSION              -0x0240
+#define POLARSSL_ERR_X509_KEY_INVALID_FORMAT               -0x0260
+#define POLARSSL_ERR_X509_KEY_INVALID_ENC_IV               -0x0280
+#define POLARSSL_ERR_X509_KEY_UNKNOWN_ENC_ALG              -0x02A0
+#define POLARSSL_ERR_X509_KEY_PASSWORD_REQUIRED            -0x02C0
+#define POLARSSL_ERR_X509_KEY_PASSWORD_MISMATCH            -0x02E0
+#define POLARSSL_ERR_X509_POINT_ERROR                      -0x0300
+#define POLARSSL_ERR_X509_VALUE_TO_LENGTH                  -0x0320
+
+/*
+ * DER constants
+ */
+#define ASN1_BOOLEAN                 0x01
+#define ASN1_INTEGER                 0x02
+#define ASN1_BIT_STRING              0x03
+#define ASN1_OCTET_STRING            0x04
+#define ASN1_NULL                    0x05
+#define ASN1_OID                     0x06
+#define ASN1_UTF8_STRING             0x0C
+#define ASN1_SEQUENCE                0x10
+#define ASN1_SET                     0x11
+#define ASN1_PRINTABLE_STRING        0x13
+#define ASN1_T61_STRING              0x14
+#define ASN1_IA5_STRING              0x16
+#define ASN1_UTC_TIME                0x17
+#define ASN1_UNIVERSAL_STRING        0x1C
+#define ASN1_BMP_STRING              0x1E
+#define ASN1_PRIMITIVE               0x00
+#define ASN1_CONSTRUCTED             0x20
+#define ASN1_CONTEXT_SPECIFIC        0x80
+
+/*
+ * PKCS#1 constants
+ */
+#define RSA_RAW         0
+#define RSA_MD2         2
+#define RSA_MD4         3
+#define RSA_MD5         4
+#define RSA_SHA1        5
+#define RSA_SHA256      6
+
+#define RSA_PUBLIC      0
+#define RSA_PRIVATE     1
+
+#define RSA_PKCS_V15    0
+#define RSA_PKCS_V21    1
+
+#define RSA_SIGN        1
+#define RSA_CRYPT       2
+
+/*
+ * DigestInfo ::= SEQUENCE {
+ *   digestAlgorithm DigestAlgorithmIdentifier,
+ *   digest Digest }
+ *
+ * DigestAlgorithmIdentifier ::= AlgorithmIdentifier
+ *
+ * Digest ::= OCTET STRING
+ */
+#define ASN1_HASH_MDX                       \
+    "\x30\x20\x30\x0C\x06\x08\x2A\x86\x48"  \
+    "\x86\xF7\x0D\x02\x00\x05\x00\x04\x10"
+
+#define ASN1_HASH_SHA1                      \
+    "\x30\x21\x30\x09\x06\x05\x2B\x0E\x03"  \
+    "\x02\x1A\x05\x00\x04\x14"
+
+#define ASN1_HASH_SHA256                    \
+    "\x30\x31\x30\x0d\x06\x09\x60\x86\x48"  \
+    "\x01\x65\x03\x04\x02\x01\x05\x00\x04"  \
+    "\x20"
+
+/**
+ * \brief          RSA context structure
+ */
+typedef struct
+{
+    int ver;                    /*!<  always 0          */
+    int len;                    /*!<  size(N) in chars  */
+
+    mpi N;                      /*!<  public modulus    */
+    mpi E;                      /*!<  public exponent   */
+
+    mpi D;                      /*!<  private exponent  */
+    mpi P;                      /*!<  1st prime factor  */
+    mpi Q;                      /*!<  2nd prime factor  */
+    mpi DP;                     /*!<  D % (P - 1)       */
+    mpi DQ;                     /*!<  D % (Q - 1)       */
+    mpi QP;                     /*!<  1 / (Q % P)       */
+
+    mpi RN;                     /*!<  cached R^2 mod N  */
+    mpi RP;                     /*!<  cached R^2 mod P  */
+    mpi RQ;                     /*!<  cached R^2 mod Q  */
+
+    int padding;                /*!<  1.5 or OAEP/PSS   */
+    int hash_id;                /*!<  hash identifier   */
+    int (*f_rng)(void *);       /*!<  RNG function      */
+    void *p_rng;                /*!<  RNG parameter     */
+}
+rsa_context;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief          Initialize an RSA context
+ *
+ * \param ctx      RSA context to be initialized
+ * \param padding  RSA_PKCS_V15 or RSA_PKCS_V21
+ * \param hash_id  RSA_PKCS_V21 hash identifier
+ * \param f_rng    RNG function
+ * \param p_rng    RNG parameter
+ *
+ * \note           The hash_id parameter is actually ignored
+ *                 when using RSA_PKCS_V15 padding.
+ *
+ * \note           Currently (xyssl-0.8), RSA_PKCS_V21 padding
+ *                 is not supported.
+ */
+void rsa_init( rsa_context *ctx,
+               int padding,
+               int hash_id,
+               int (*f_rng)(void *),
+               void *p_rng );
+
+/**
+ * \brief          Generate an RSA keypair
+ *
+ * \param ctx      RSA context that will hold the key
+ * \param nbits    size of the public key in bits
+ * \param exponent public exponent (e.g., 65537)
+ *
+ * \note           rsa_init() must be called beforehand to setup
+ *                 the RSA context (especially f_rng and p_rng).
+ *
+ * \return         0 if successful, or an POLARSSL_ERR_RSA_XXX error code
+ */
+int rsa_gen_key( rsa_context *ctx, int nbits, int exponent );
+
+/**
+ * \brief          Check a public RSA key
+ *
+ * \param ctx      RSA context to be checked
+ *
+ * \return         0 if successful, or an POLARSSL_ERR_RSA_XXX error code
+ */
+int rsa_check_pubkey( rsa_context *ctx );
+
+/**
+ * \brief          Check a private RSA key
+ *
+ * \param ctx      RSA context to be checked
+ *
+ * \return         0 if successful, or an POLARSSL_ERR_RSA_XXX error code
+ */
+int rsa_check_privkey( rsa_context *ctx );
+
+/**
+ * \brief          Do an RSA public key operation
+ *
+ * \param ctx      RSA context
+ * \param input    input buffer
+ * \param output   output buffer
+ *
+ * \return         0 if successful, or an POLARSSL_ERR_RSA_XXX error code
+ *
+ * \note           This function does NOT take care of message
+ *                 padding. Also, be sure to set input[0] = 0.
+ *
+ * \note           The input and output buffers must be large
+ *                 enough (eg. 128 bytes if RSA-1024 is used).
+ */
+int rsa_public( rsa_context *ctx,
+                unsigned char *input,
+                unsigned char *output );
+
+/**
+ * \brief          Do an RSA private key operation
+ *
+ * \param ctx      RSA context
+ * \param input    input buffer
+ * \param output   output buffer
+ *
+ * \return         0 if successful, or an POLARSSL_ERR_RSA_XXX error code
+ *
+ * \note           The input and output buffers must be large
+ *                 enough (eg. 128 bytes if RSA-1024 is used).
+ */
+int rsa_private( rsa_context *ctx,
+                 unsigned char *input,
+                 unsigned char *output );
+
+/**
+ * \brief          Add the message padding, then do an RSA operation
+ *
+ * \param ctx      RSA context
+ * \param mode     RSA_PUBLIC or RSA_PRIVATE
+ * \param ilen     contains the the plaintext length
+ * \param input    buffer holding the data to be encrypted
+ * \param output   buffer that will hold the ciphertext
+ *
+ * \return         0 if successful, or an POLARSSL_ERR_RSA_XXX error code
+ *
+ * \note           The output buffer must be as large as the size
+ *                 of ctx->N (eg. 128 bytes if RSA-1024 is used).
+ */
+int rsa_pkcs1_encrypt( rsa_context *ctx,
+                       int mode, int  ilen,
+                       unsigned char *input,
+                       unsigned char *output );
+
+/**
+ * \brief          Do an RSA operation, then remove the message padding
+ *
+ * \param ctx      RSA context
+ * \param mode     RSA_PUBLIC or RSA_PRIVATE
+ * \param input    buffer holding the encrypted data
+ * \param output   buffer that will hold the plaintext
+ * \param olen     will contain the plaintext length
+ * \param output_max_len    maximum length of the output buffer
+ *
+ * \return         0 if successful, or an POLARSSL_ERR_RSA_XXX error code
+ *
+ * \note           The output buffer must be as large as the size
+ *                 of ctx->N (eg. 128 bytes if RSA-1024 is used) otherwise
+ *                 an error is thrown.
+ */
+int rsa_pkcs1_decrypt( rsa_context *ctx,
+                       int mode, int *olen,
+                       unsigned char *input,
+                       unsigned char *output,
+               int output_max_len);
+
+/**
+ * \brief          Do a private RSA to sign a message digest
+ *
+ * \param ctx      RSA context
+ * \param mode     RSA_PUBLIC or RSA_PRIVATE
+ * \param hash_id  RSA_RAW, RSA_MD{2,4,5} or RSA_SHA{1,256}
+ * \param hashlen  message digest length (for RSA_RAW only)
+ * \param hash     buffer holding the message digest
+ * \param sig      buffer that will hold the ciphertext
+ *
+ * \return         0 if the signing operation was successful,
+ *                 or an POLARSSL_ERR_RSA_XXX error code
+ *
+ * \note           The "sig" buffer must be as large as the size
+ *                 of ctx->N (eg. 128 bytes if RSA-1024 is used).
+ */
+int rsa_pkcs1_sign( rsa_context *ctx,
+                    int mode,
+                    int hash_id,
+                    int hashlen,
+                    unsigned char *hash,
+                    unsigned char *sig );
+
+/**
+ * \brief          Do a public RSA and check the message digest
+ *
+ * \param ctx      points to an RSA public key
+ * \param mode     RSA_PUBLIC or RSA_PRIVATE
+ * \param hash_id  RSA_RAW, RSA_MD{2,4,5} or RSA_SHA{1,256}
+ * \param hashlen  message digest length (for RSA_RAW only)
+ * \param hash     buffer holding the message digest
+ * \param sig      buffer holding the ciphertext
+ *
+ * \return         0 if the verify operation was successful,
+ *                 or an POLARSSL_ERR_RSA_XXX error code
+ *
+ * \note           The "sig" buffer must be as large as the size
+ *                 of ctx->N (eg. 128 bytes if RSA-1024 is used).
+ */
+int rsa_pkcs1_verify( rsa_context *ctx,
+                      int mode,
+                      int hash_id,
+                      int hashlen,
+                      unsigned char *hash,
+                      unsigned char *sig );
+
+/**
+ * \brief          Free the components of an RSA key
+ */
+void rsa_free( rsa_context *ctx );
+
+int rsa_parse_public_key( rsa_context *rsa, unsigned char *buf, int buflen );
+
+int rsa_parse_key( rsa_context *rsa, unsigned char *buf, int buflen,
+                                     unsigned char *pwd, int pwdlen );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* rsa.h */
diff --git a/src/src/pdkim/sha1.c b/src/src/pdkim/sha1.c
new file mode 100644 (file)
index 0000000..f118f4e
--- /dev/null
@@ -0,0 +1,424 @@
+/*
+ *  FIPS-180-1 compliant SHA-1 implementation
+ *
+ *  Based on XySSL: Copyright (C) 2006-2008  Christophe Devine
+ *
+ *  Copyright (C) 2009  Paul Bakker <polarssl_maintainer at polarssl dot org>
+ *
+ *  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
+ */
+
+/* $Cambridge: exim/src/src/pdkim/sha1.c,v 1.2 2009/06/10 07:34:05 tom Exp $ */
+
+#include "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 )       &n