[exim-cvs] cvs commit: exim/exim-src/src/pdkim pdkim.c pdkim…

Top Page
Delete this message
Reply to this message
Author: Tom Kistner
Date:  
To: exim-cvs
Subject: [exim-cvs] cvs commit: exim/exim-src/src/pdkim pdkim.c pdkim.h
tom 2009/02/26 16:07:36 GMT

  Modified files:        (Branch: DEVEL_PDKIM)
    exim-src/src/pdkim   pdkim.c pdkim.h 
  Log:
  wip


  Revision  Changes    Path
  1.1.2.4   +397 -39   exim/exim-src/src/pdkim/pdkim.c
  1.1.2.3   +16 -22    exim/exim-src/src/pdkim/pdkim.h


  Index: pdkim.c
  ===================================================================
  RCS file: /home/cvs/exim/exim-src/src/pdkim/Attic/pdkim.c,v
  retrieving revision 1.1.2.3
  retrieving revision 1.1.2.4
  diff -u -r1.1.2.3 -r1.1.2.4
  --- pdkim.c    25 Feb 2009 12:52:58 -0000    1.1.2.3
  +++ pdkim.c    26 Feb 2009 16:07:36 -0000    1.1.2.4
  @@ -1,4 +1,4 @@
  -/* $Cambridge: exim/exim-src/src/pdkim/pdkim.c,v 1.1.2.3 2009/02/25 12:52:58 tom Exp $ */
  +/* $Cambridge: exim/exim-src/src/pdkim/pdkim.c,v 1.1.2.4 2009/02/26 16:07:36 tom Exp $ */
   /* pdkim.c */


   #include <stdlib.h>
  @@ -13,15 +13,33 @@
   /* -------------------------------------------------------------------------- */
   /* A bunch of list constants */
   char *pdkim_querymethods[] = {
  -  "dns/txt"
  +  "dns/txt",
  +  NULL
   };
   char *pdkim_algos[] = {
     "rsa-sha256",
  -  "rsa-sha1"
  +  "rsa-sha1",
  +  NULL
   };
   char *pdkim_canons[] = {
     "simple",
  -  "relaxed"
  +  "relaxed",
  +  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 }
   };



  @@ -114,6 +132,24 @@
     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);
  @@ -210,6 +246,285 @@



   /* -------------------------------------------------------------------------- */
  +#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++;
  +  }
  +  return n;
  +}
  +
  +
  +/* -------------------------------------------------------------------------- */
  +char *pdkim_decode_base64(char *str) {
  +  int dlen = 0;
  +  char *res;
  +
  +  base64_decode(NULL, &dlen, str, strlen(str));
  +  res = malloc(dlen+1);
  +  if (res == NULL) return NULL;
  +  if (base64_decode(res,&dlen,str,strlen(str)) != 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 *rawsig_no_b_val;
  +  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->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 (*p != '\0') {
  +
  +    /* 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') )
  +        goto NEXT_CHAR;
  +
  +      if (*p == ';') {
  +        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);
  +                break;
  +                default:
  +                  sig->sigdata = pdkim_decode_base64(cur_val->str);
  +                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 = malloc(strlen(cur_val->str)+1);
  +              if (sig->selector == NULL) break;
  +              strcpy(sig->selector, cur_val->str);
  +            break;
  +            case 'd':
  +              sig->domain = malloc(strlen(cur_val->str)+1);
  +              if (sig->domain == NULL) break;
  +              strcpy(sig->domain, 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 = strtoul(cur_val->str,NULL,10);
  +            break;
  +            case 'h':
  +              sig->headernames = malloc(strlen(cur_val->str)+1);
  +              if (sig->headernames == NULL) break;
  +              strcpy(sig->headernames, 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 (!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->version)) {
  +    pdkim_free_signature(sig);
  +    return NULL;
  +  }
  +
  +  *q = '\0';
  +  #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
  +  return sig;
  +}
  +
  +
  +
  +/* -------------------------------------------------------------------------- */
   int pdkim_update_bodyhash(pdkim_ctx *ctx, char *data, int len) {
     pdkim_signature *sig = ctx->sig;
     /* Cache relaxed version of data */
  @@ -322,7 +637,6 @@
       /* VERIFICATION --------------------------------------------------------- */
       else {


  -
       }



@@ -381,6 +695,7 @@

   /* -------------------------------------------------------------------------- */
   /* 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;


@@ -412,7 +727,41 @@

       sig = sig->next;
     }
  -  ctx->cur_header->len = 0; /* Re-use existing pdkim_str */
  +
  +  /* 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) ) {
  +    /* 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
  +    pdkim_signature *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
  +    }
  +  }
  +
  +  pdkim_strclear(ctx->cur_header); /* Re-use existing pdkim_str */
     return PDKIM_OK;
   };


@@ -475,55 +824,55 @@


/* -------------------------------------------------------------------------- */
-pdkim_str *pdkim_create_header(pdkim_ctx *ctx, int final) {
+pdkim_str *pdkim_create_header(pdkim_signature *sig, int final) {

     pdkim_str *hdr = pdkim_strnew("DKIM-Signature: v="PDKIM_SIGNATURE_VERSION);
     if (hdr == NULL) return NULL;
     /* Required and static bits */
     if (
  -        pdkim_strcat(hdr,"; a=")                                     &&
  -        pdkim_strcat(hdr,pdkim_algos[ctx->sig->algo])                &&
  -        pdkim_strcat(hdr,"; q=")                                     &&
  -        pdkim_strcat(hdr,pdkim_querymethods[ctx->sig->querymethod])  &&
  -        pdkim_strcat(hdr,"; c=")                                     &&
  -        pdkim_strcat(hdr,pdkim_canons[ctx->sig->canon_headers])      &&
  -        pdkim_strcat(hdr,"/")                                        &&
  -        pdkim_strcat(hdr,pdkim_canons[ctx->sig->canon_body])         &&
  -        pdkim_strcat(hdr,"; d=")                                     &&
  -        pdkim_strcat(hdr,ctx->sig->domain)                           &&
  -        pdkim_strcat(hdr,"; s=")                                     &&
  -        pdkim_strcat(hdr,ctx->sig->selector)                         &&
  -        pdkim_strcat(hdr,";\r\n\th=")                                &&
  -        pdkim_strcat(hdr,ctx->sig->headernames)                      &&
  -        pdkim_strcat(hdr,"; bh=")                                    &&
  -        pdkim_strcat(hdr,ctx->sig->bodyhash)                         &&
  +        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,sig->bodyhash)                         &&
           pdkim_strcat(hdr,";\r\n\t")
        ) {
       /* Optional bits */
  -    if (ctx->sig->identity != NULL) {
  -      if (!( pdkim_strcat(hdr,"i=")                                  &&
  -             pdkim_strcat(hdr,ctx->sig->identity)                    &&
  +    if (sig->identity != NULL) {
  +      if (!( pdkim_strcat(hdr,"i=")                             &&
  +             pdkim_strcat(hdr,sig->identity)                    &&
                pdkim_strcat(hdr,";") ) ) {
           return NULL;
         }
       }
  -    if (ctx->sig->created > 0) {
  -      if (!( pdkim_strcat(hdr,"t=")                                  &&
  -             pdkim_numcat(hdr,ctx->sig->created)                     &&
  +    if (sig->created > 0) {
  +      if (!( pdkim_strcat(hdr,"t=")                             &&
  +             pdkim_numcat(hdr,sig->created)                     &&
                pdkim_strcat(hdr,";") ) ) {
           return NULL;
         }
       }
  -    if (ctx->sig->expires > 0) {
  -      if (!( pdkim_strcat(hdr,"x=")                                  &&
  -             pdkim_numcat(hdr,ctx->sig->expires)                     &&
  +    if (sig->expires > 0) {
  +      if (!( pdkim_strcat(hdr,"x=")                             &&
  +             pdkim_numcat(hdr,sig->expires)                     &&
                pdkim_strcat(hdr,";") ) ) {
           return NULL;
         }
       }
  -    if (ctx->sig->bodylength > 0) {
  -      if (!( pdkim_strcat(hdr,"l=")                                  &&
  -             pdkim_numcat(hdr,ctx->sig->bodylength)                  &&
  +    if (sig->bodylength > 0) {
  +      if (!( pdkim_strcat(hdr,"l=")                             &&
  +             pdkim_numcat(hdr,sig->bodylength)                  &&
                pdkim_strcat(hdr,";") ) ) {
           return NULL;
         }
  @@ -535,8 +884,8 @@
       /* Preliminary or final version? */
       if (final) {
         if (
  -            pdkim_strcat(hdr,"b=")                                   &&
  -            pdkim_strcat(hdr,ctx->sig->sigdata)                      &&
  +            pdkim_strcat(hdr,"b=")                              &&
  +            pdkim_strcat(hdr,sig->sigdata)                      &&
               pdkim_strcat(hdr,";")
            ) return hdr;
       }
  @@ -636,7 +985,7 @@
       pdkim_strfree(headernames);


       /* Create signature header with b= omitted */
  -    hdr = pdkim_create_header(ctx,0);
  +    hdr = pdkim_create_header(ctx->sig,0);
       if (hdr == NULL) return PDKIM_ERR_OOM;


       /* If necessary, perform relaxed canon */
  @@ -723,7 +1072,7 @@


       /* Recreate signature header with b= included */
       pdkim_strfree(hdr);
  -    hdr = pdkim_create_header(ctx,1);
  +    hdr = pdkim_create_header(ctx->sig,1);
       if (hdr == NULL) return PDKIM_ERR_OOM;


#ifdef PDKIM_DEBUG
@@ -748,6 +1097,16 @@


   /* -------------------------------------------------------------------------- */
  +pdkim_ctx *pdkim_init_verify(void) {
  +  pdkim_ctx *ctx = malloc(sizeof(pdkim_ctx));
  +  if (ctx == NULL) return NULL;
  +  memset(ctx,0,sizeof(pdkim_ctx));
  +  ctx->mode = PDKIM_MODE_VERIFY;
  +  return ctx;
  +}
  +
  +
  +/* -------------------------------------------------------------------------- */
   pdkim_ctx *pdkim_init_sign(char *domain,
                              char *selector,
                              char *rsa_privkey) {
  @@ -785,7 +1144,6 @@
     sha2_starts(&(ctx->sig->sha2_body),0);


     return ctx;
  -
   };


#ifdef PDKIM_DEBUG

  Index: pdkim.h
  ===================================================================
  RCS file: /home/cvs/exim/exim-src/src/pdkim/Attic/pdkim.h,v
  retrieving revision 1.1.2.2
  retrieving revision 1.1.2.3
  diff -u -r1.1.2.2 -r1.1.2.3
  --- pdkim.h    24 Feb 2009 15:57:55 -0000    1.1.2.2
  +++ pdkim.h    26 Feb 2009 16:07:36 -0000    1.1.2.3
  @@ -1,4 +1,4 @@
  -/* $Cambridge: exim/exim-src/src/pdkim/pdkim.h,v 1.1.2.2 2009/02/24 15:57:55 tom Exp $ */
  +/* $Cambridge: exim/exim-src/src/pdkim/pdkim.h,v 1.1.2.3 2009/02/26 16:07:36 tom Exp $ */
   /* pdkim.h */


#include "sha1.h"
@@ -86,7 +86,7 @@
/* Signature as it appears in a DKIM-Signature header */
typedef struct pdkim_signature {

  -  /* Bits stored in a DKIM signature header */
  +  /* Bits stored in a DKIM signature header ------ */
     int version;                    /* v=   */
     int algo;                       /* a=   */
     int canon_headers;              /* c=x/ */
  @@ -107,31 +107,22 @@
     char *headernames;              /* h=   */
     char *copiedheaders;            /* z=   */


  -  /* Public key used to verify this signature.
  -     (Verification only) */
  -  pdkim_pubkey pubkey;
  -
  -  /* Private RSA key used to create this signature */
  -  char *rsa_privkey;
  -
  -  /* Header field names to include in the signature,
  -     colon separated. When NULL, the recommended defaults
  -     from RFC 4871 are used. */
  -  char *sign_headers;


  -  /* Per-signature helper variables */
  +  /* Signing specific ---------------------------- */
  +  char *rsa_privkey;     /* Private RSA key */
  +  char *sign_headers;    /* To-be-signed header names */
  +
  +  /* Verification specific ----------------------- */
  +  pdkim_pubkey pubkey;   /* Public key used to verify this signature. */
  +  int verify_result;     /* Verification result */
  +  char *rawsig_no_b_val; /* Original signature header w/o b= tag value. */
  +  void *next;            /* Pointer to next signature in list. */
  +
  +  /* Per-signature helper variables -------------- */
     sha1_context sha1_body;
     sha2_context sha2_body;
     unsigned long signed_body_bytes;
     pdkim_stringlist *headers;
  -
  -  /* Verification specific */
  -  int verify_result;
  -
  -  /* Pointer to next signature in list.
  -     (Always NULL for signing) */
  -  void *next;
  -
   } pdkim_signature;



  @@ -185,11 +176,14 @@
   int   pdkim_feed_finish       (pdkim_ctx *, char **);


   pdkim_str
  -     *pdkim_create_header     (pdkim_ctx *, int);
  +     *pdkim_create_header     (pdkim_signature *, int);


   pdkim_ctx
        *pdkim_init_sign         (char *, char *, char *);


  +pdkim_ctx
  +     *pdkim_init_verify       (void);
  +
   int   pdkim_set_optional      (pdkim_ctx *,
                                  int,
                                  char *, char *,