DKIM: rename internal signing api
authorJeremy Harris <jgh146exb@wizmail.org>
Fri, 19 May 2017 12:32:53 +0000 (13:32 +0100)
committerJeremy Harris <jgh146exb@wizmail.org>
Tue, 12 Sep 2017 16:08:45 +0000 (17:08 +0100)
src/scripts/MakeLinks
src/src/dkim.c
src/src/hash.c
src/src/pdkim/Makefile
src/src/pdkim/pdkim.c
src/src/pdkim/signing.c [moved from src/src/pdkim/rsa.c with 84% similarity]
src/src/pdkim/signing.h [moved from src/src/pdkim/rsa.h with 74% similarity]

index 44e3a4e..22e5a4b 100755 (executable)
@@ -85,7 +85,7 @@ cd ..
 mkdir pdkim
 cd pdkim
 for f in README Makefile crypt_ver.h pdkim.c \
-  pdkim.h hash.c hash.h rsa.c rsa.h blob.h
+  pdkim.h hash.c hash.h signing.c signing.h blob.h
 do
   ln -s ../../src/pdkim/$f $f
 done
index 2857e63..f7b9ee0 100644 (file)
@@ -608,6 +608,11 @@ while ((dkim_signing_domain = string_nextinlist(&dkim_domain, &sep, NULL, 0)))
     dkim_private_key_expanded = big_buffer;
     }
 
+/*XXX so we currently nail signing to RSA + SHA256.  Need to extract algo
+from privkey, and provide means for selecting hash-method.
+Check for disallowed combos.
+Will need new dkim_ transport option for hash. */
+
   if (!(ctx = pdkim_init_sign(CS dkim_signing_domain,
                        CS dkim_signing_selector,
                        CS dkim_private_key_expanded,
index f49add2..e239516 100644 (file)
@@ -33,6 +33,7 @@ sha1;
 BOOL
 exim_sha_init(hctx * h, hashmethod m)
 {
+/*XXX extend for sha512 */
 switch (h->method = m)
   {
   case HASH_SHA1:   h->hashlen = 20; SHA1_Init  (&h->u.sha1); break;
@@ -77,6 +78,7 @@ switch (h->method)
 BOOL
 exim_sha_init(hctx * h, hashmethod m)
 {
+/*XXX extend for sha512 */
 switch (h->method = m)
   {
   case HASH_SHA1:     h->hashlen = 20; gnutls_hash_init(&h->sha, GNUTLS_DIG_SHA1); break;
@@ -112,6 +114,7 @@ gnutls_hash_output(h->sha, b->data);
 BOOL
 exim_sha_init(hctx * h, hashmethod m)
 {
+/*XXX extend for sha512 */
 switch (h->method = m)
   {
   case HASH_SHA1:   h->hashlen = 20; gcry_md_open(&h->sha, GCRY_MD_SHA1, 0); break;
@@ -145,6 +148,7 @@ memcpy(b->data, gcry_md_read(h->sha, 0), h->hashlen);
 BOOL
 exim_sha_init(hctx * h, hashmethod m)
 {
+/*XXX extend for sha512 */
 switch (h->method = m)
   {
   case HASH_SHA1:   h->hashlen = 20; sha1_starts(&h->u.sha1);    break;
index c298568..10631ce 100644 (file)
@@ -1,6 +1,6 @@
 # Make file for building the pdkim library.
 
-OBJ = pdkim.o rsa.o
+OBJ = pdkim.o signing.o
 
 pdkim.a:         $(OBJ)
                 @$(RM_COMMAND) -f pdkim.a
@@ -13,6 +13,6 @@ pdkim.a:         $(OBJ)
                 $(FE)$(CC) -c $(CFLAGS) $(INCLUDE) -I. $*.c
 
 pdkim.o: $(HDRS) crypt_ver.h pdkim.h pdkim.c
-rsa.o:   $(HDRS) crypt_ver.h rsa.h rsa.c
+signing.o: $(HDRS) crypt_ver.h signing.h signing.c
 
 # End
index 2ce0ff6..441f96c 100644 (file)
@@ -42,7 +42,7 @@
 #endif
 
 #include "pdkim.h"
-#include "rsa.h"
+#include "signing.h"
 
 #define PDKIM_SIGNATURE_VERSION     "1"
 #define PDKIM_PUB_RECORD_VERSION    US "DKIM1"
@@ -83,11 +83,13 @@ const uschar * pdkim_canons[] = {
   US"relaxed",
   NULL
 };
+/*XXX currently unused */
 const uschar * pdkim_hashes[] = {
   US"sha256",
   US"sha1",
   NULL
 };
+/*XXX currently unused */
 const uschar * pdkim_keytypes[] = {
   US"rsa",
   NULL
@@ -505,6 +507,7 @@ for (p = raw_hdr; ; p++)
              Ustrcmp(cur_val, PDKIM_SIGNATURE_VERSION) == 0 ? 1 : -1;
            break;
          case 'a':
+/*XXX this searches a list of combined (algo + hash-method)s */
            for (i = 0; pdkim_algos[i]; i++)
              if (Ustrcmp(cur_val, pdkim_algos[i]) == 0)
                {
@@ -583,6 +586,7 @@ DEBUG(D_acl)
          "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
   }
 
+/*XXX hash method: extend for sha512 */
 if (!exim_sha_init(&sig->body_hash_ctx,
               sig->algo == PDKIM_ALGO_RSA_SHA1 ? HASH_SHA1 : HASH_SHA256))
   {
@@ -1185,6 +1189,7 @@ col = hdr_len;
 
 /* Required and static bits */
 hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"a=",
+/*XXX this is a combo of algo and hash-method */
                    pdkim_algos[sig->algo]);
 hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"q=",
                    pdkim_querymethods[sig->querymethod]);
@@ -1332,7 +1337,7 @@ DEBUG(D_acl) debug_printf(
       "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
 
 /* Import public key */
-if ((*errstr = exim_rsa_verify_init(&p->key, vctx)))
+if ((*errstr = exim_dkim_verify_init(&p->key, vctx)))
   {
   DEBUG(D_acl) debug_printf("verify_init: %s\n", *errstr);
   sig->verify_status =      PDKIM_VERIFY_INVALID;
@@ -1370,6 +1375,7 @@ pdkim_finish_bodyhash(ctx);
 
 while (sig)
   {
+/*XXX bool probably not enough */
   BOOL is_sha1 = sig->algo == PDKIM_ALGO_RSA_SHA1;
   hctx hhash_ctx;
   uschar * sig_hdr = US"";
@@ -1422,7 +1428,7 @@ while (sig)
        exim_sha_update(&hhash_ctx, CUS rh, Ustrlen(rh));
 
        /* Remember headers block for signing (when the library cannot do incremental)  */
-       (void) exim_rsa_data_append(&hdata, &hdata_alloc, rh);
+       (void) exim_dkim_data_append(&hdata, &hdata_alloc, rh);
 
        DEBUG(D_acl) pdkim_quoteprint(rh, Ustrlen(rh));
        }
@@ -1522,8 +1528,9 @@ while (sig)
     }
 
   /* Remember headers block for signing (when the library cannot do incremental)  */
+/*XXX is this assuing algo == RSA? */
   if (ctx->flags & PDKIM_MODE_SIGN)
-    (void) exim_rsa_data_append(&hdata, &hdata_alloc, US sig_hdr);
+    (void) exim_dkim_data_append(&hdata, &hdata_alloc, US sig_hdr);
 
   /* SIGNING ---------------------------------------------------------------- */
   if (ctx->flags & PDKIM_MODE_SIGN)
@@ -1531,7 +1538,8 @@ while (sig)
     es_ctx sctx;
 
     /* Import private key */
-    if ((*err = exim_rsa_signing_init(US sig->rsa_privkey, &sctx)))
+/*XXX extend for non-RSA algos */
+    if ((*err = exim_dkim_signing_init(US sig->rsa_privkey, &sctx)))
       {
       DEBUG(D_acl) debug_printf("signing_init: %s\n", *err);
       return PDKIM_ERR_RSA_PRIVKEY;
@@ -1545,7 +1553,11 @@ while (sig)
     hdata = hhash;
 #endif
 
-    if ((*err = exim_rsa_sign(&sctx, is_sha1, &hdata, &sig->sighash)))
+/*XXX extend for non-RSA algos */
+/*XXX oddly the dkim rfc does _not_ say what variant (sha1 or sha256) of
+RSA signing should be done.  We use the same variant as the hash-method. */
+
+    if ((*err = exim_dkim_sign(&sctx, is_sha1, &hdata, &sig->sighash)))
       {
       DEBUG(D_acl) debug_printf("signing: %s\n", *err);
       return PDKIM_ERR_RSA_SIGNING;
@@ -1619,7 +1631,8 @@ while (sig)
       }
 
     /* Check the signature */
-    if ((*err = exim_rsa_verify(&vctx, is_sha1, &hhash, &sig->sighash)))
+/*XXX needs extension for non-RSA */
+    if ((*err = exim_dkim_verify(&vctx, is_sha1, &hhash, &sig->sighash)))
       {
       DEBUG(D_acl) debug_printf("headers verify: %s\n", *err);
       sig->verify_status =      PDKIM_VERIFY_FAIL;
@@ -1677,6 +1690,9 @@ return ctx;
 
 /* -------------------------------------------------------------------------- */
 
+/*XXX ? needs extension to cover non-RSA algo?  Currently the "algo" is actually
+the combo of algo and hash-method */
+
 DLLEXPORT pdkim_ctx *
 pdkim_init_sign(char * domain, char * selector, char * rsa_privkey, int algo,
   BOOL dot_stuffed, int(*dns_txt_callback)(char *, char *),
@@ -1707,6 +1723,7 @@ sig->selector = string_copy(US selector);
 sig->rsa_privkey = string_copy(US rsa_privkey);
 sig->algo = algo;
 
+/*XXX extend for sha512 */
 if (!exim_sha_init(&sig->body_hash_ctx,
               algo == PDKIM_ALGO_RSA_SHA1 ? HASH_SHA1 : HASH_SHA256))
   {
@@ -1761,7 +1778,7 @@ return PDKIM_OK;
 void
 pdkim_init(void)
 {
-exim_rsa_init();
+exim_dkim_init();
 }
 
 
similarity index 84%
rename from src/src/pdkim/rsa.c
rename to src/src/pdkim/signing.c
index 950c617..bcd64fd 100644 (file)
@@ -4,6 +4,10 @@
  *  Copyright (C) 2016  Exim maintainers
  *
  *  RSA signing/verification interface
+XXX rename interfaces to cover all signature methods.
+the method (algo) needs to be extracted from the supplied private-key
+and not only stashed as needed in the sign- or verify- context, but
+indicated to caller for protocol tag construction.
  */
 
 #include "../exim.h"
 #endif
 
 #include "crypt_ver.h"
-#include "rsa.h"
+#include "signing.h"
 
 
 /******************************************************************************/
 #ifdef RSA_GNUTLS
 
 void
-exim_rsa_init(void)
+exim_dkim_init(void)
 {
 }
 
 
 /* accumulate data (gnutls-only).  String to be appended must be nul-terminated. */
 blob *
-exim_rsa_data_append(blob * b, int * alloc, uschar * s)
+exim_dkim_data_append(blob * b, int * alloc, uschar * s)
 {
 int len = b->len;
 b->data = string_append(b->data, alloc, &len, 1, s);
@@ -43,7 +47,7 @@ return b;
 Return: NULL for success, or an error string */
 
 const uschar *
-exim_rsa_signing_init(uschar * privkey_pem, es_ctx * sign_ctx)
+exim_dkim_signing_init(uschar * privkey_pem, es_ctx * sign_ctx)
 {
 gnutls_datum_t k;
 int rc;
@@ -52,13 +56,7 @@ k.data = privkey_pem;
 k.size = strlen(privkey_pem);
 
 if (  (rc = gnutls_x509_privkey_init(&sign_ctx->rsa)) != GNUTLS_E_SUCCESS
-   /*|| (rc = gnutls_x509_privkey_import(sign_ctx->rsa, &k,
-         GNUTLS_X509_FMT_PEM)) != GNUTLS_E_SUCCESS */
-   )
-  return gnutls_strerror(rc);
-
-if (  /* (rc = gnutls_x509_privkey_init(&sign_ctx->rsa)) != GNUTLS_E_SUCCESS
-   ||*/ (rc = gnutls_x509_privkey_import(sign_ctx->rsa, &k,
+   || (rc = gnutls_x509_privkey_import(sign_ctx->rsa, &k,
          GNUTLS_X509_FMT_PEM)) != GNUTLS_E_SUCCESS
    )
   return gnutls_strerror(rc);
@@ -76,7 +74,7 @@ sign hash.
 Return: NULL for success, or an error string */
 
 const uschar *
-exim_rsa_sign(es_ctx * sign_ctx, BOOL is_sha1, blob * data, blob * sig)
+exim_dkim_sign(es_ctx * sign_ctx, BOOL is_sha1, blob * data, blob * sig)
 {
 gnutls_datum_t k;
 size_t sigsize = 0;
@@ -94,6 +92,8 @@ sig->data = store_get(sigsize);
 sig->len = sigsize;
 
 /* Do signing */
+/*XXX will need extension for hash type; looks ok for non-RSA algos
+so long as the privkey_import stage got them. */
 if ((rc = gnutls_x509_privkey_sign_data(sign_ctx->rsa,
            is_sha1 ? GNUTLS_DIG_SHA1 : GNUTLS_DIG_SHA256,
            0, &k, sig->data, &sigsize)) != GNUTLS_E_SUCCESS
@@ -110,7 +110,7 @@ return ret;
 Return: NULL for success, or an error string */
 
 const uschar *
-exim_rsa_verify_init(blob * pubkey_der, ev_ctx * verify_ctx)
+exim_dkim_verify_init(blob * pubkey_der, ev_ctx * verify_ctx)
 {
 gnutls_datum_t k;
 int rc;
@@ -132,7 +132,7 @@ return ret;
 Return: NULL for success, or an error string */
 
 const uschar *
-exim_rsa_verify(ev_ctx * verify_ctx, BOOL is_sha1, blob * data_hash, blob * sig)
+exim_dkim_verify(ev_ctx * verify_ctx, BOOL is_sha1, blob * data_hash, blob * sig)
 {
 gnutls_datum_t k, s;
 int rc;
@@ -143,6 +143,7 @@ k.size = data_hash->len;
 s.data = sig->data;
 s.size = sig->len;
 if ((rc = gnutls_pubkey_verify_hash2(verify_ctx->rsa,
+/*XXX needs extension for SHA512 */
            is_sha1 ? GNUTLS_SIGN_RSA_SHA1 : GNUTLS_SIGN_RSA_SHA256,
            0, &k, &s)) < 0)
   ret = gnutls_strerror(rc);
@@ -217,7 +218,7 @@ return NULL;
 
 
 void
-exim_rsa_init(void)
+exim_dkim_init(void)
 {
 /* Version check should be the very first call because it
 makes sure that important subsystems are initialized. */
@@ -259,7 +260,7 @@ return;
 String to be appended must be nul-terminated. */
 
 blob *
-exim_rsa_data_append(blob * b, int * alloc, uschar * s)
+exim_dkim_data_append(blob * b, int * alloc, uschar * s)
 {
 return b;      /*dummy*/
 }
@@ -270,13 +271,16 @@ return b; /*dummy*/
 Return: NULL for success, or an error string */
 
 const uschar *
-exim_rsa_signing_init(uschar * privkey_pem, es_ctx * sign_ctx)
+exim_dkim_signing_init(uschar * privkey_pem, es_ctx * sign_ctx)
 {
 uschar * s1, * s2;
 blob der;
 long alen;
 int rc;
 
+/*XXX will need extension to _spot_ as well as handle a
+non-RSA key?  I think... */
+
 /*
  *  RSAPrivateKey ::= SEQUENCE
  *      version           Version,
@@ -360,13 +364,20 @@ sign hash.
 Return: NULL for success, or an error string */
 
 const uschar *
-exim_rsa_sign(es_ctx * sign_ctx, BOOL is_sha1, blob * data, blob * sig)
+exim_dkim_sign(es_ctx * sign_ctx, BOOL is_sha1, blob * data, blob * sig)
 {
 gcry_sexp_t s_hash = NULL, s_key = NULL, s_sig = NULL;
 gcry_mpi_t m_sig;
 uschar * errstr;
 gcry_error_t gerr;
 
+/*XXX will need extension for hash types (though, possibly, should
+be re-specced to not rehash but take an already-hashed value? Actually
+current impl looks WRONG - it _is_ given a has so should not be
+re-hashing.  Has this been tested?
+
+Will need extension for non-RSA sugning algos. */
+
 #define SIGSPACE 128
 sig->data = store_get(SIGSPACE);
 
@@ -421,7 +432,7 @@ return NULL;
 Return: NULL for success, or an error string */
 
 const uschar *
-exim_rsa_verify_init(blob * pubkey_der, ev_ctx * verify_ctx)
+exim_dkim_verify_init(blob * pubkey_der, ev_ctx * verify_ctx)
 {
 /*
 in code sequence per b81207d2bfa92 rsa_parse_public_key() and asn1_get_mpi()
@@ -501,7 +512,7 @@ DEBUG(D_acl) return string_sprintf("%s: %s", stage, asn1_strerror(rc));
 Return: NULL for success, or an error string */
 
 const uschar *
-exim_rsa_verify(ev_ctx * verify_ctx, BOOL is_sha1, blob * data_hash, blob * sig)
+exim_dkim_verify(ev_ctx * verify_ctx, BOOL is_sha1, blob * data_hash, blob * sig)
 {
 /*
 cf. libgnutls 2.8.5 _wrap_gcry_pk_verify()
@@ -516,6 +527,7 @@ if (  (stage = US"pkey sexp build",
                        verify_ctx->n, verify_ctx->e))
    || (stage = US"data sexp build",
        gerr = gcry_sexp_build (&s_hash, NULL,
+/*XXX needs extension for SHA512 */
                is_sha1
                ? "(data(flags pkcs1)(hash sha1 %b))"
                : "(data(flags pkcs1)(hash sha256 %b))",
@@ -549,14 +561,14 @@ return NULL;
 /******************************************************************************/
 
 void
-exim_rsa_init(void)
+exim_dkim_init(void)
 {
 }
 
 
 /* accumulate data (gnutls-only) */
 blob *
-exim_rsa_data_append(blob * b, int * alloc, uschar * s)
+exim_dkim_data_append(blob * b, int * alloc, uschar * s)
 {
 return b;      /*dummy*/
 }
@@ -566,11 +578,14 @@ return b; /*dummy*/
 Return: NULL for success, or an error string */
 
 const uschar *
-exim_rsa_signing_init(uschar * privkey_pem, es_ctx * sign_ctx)
+exim_dkim_signing_init(uschar * privkey_pem, es_ctx * sign_ctx)
 {
 uschar * p, * q;
 int len;
 
+/*XXX maybe use PEM_read_bio_PrivateKey() ??? 
+The sign_ctx would need to have an EVP_PKEY* */
+
 /* Convert PEM to DER */
 if (  !(p = Ustrstr(privkey_pem, "-----BEGIN RSA PRIVATE KEY-----"))
    || !(q = Ustrstr(p+=31,       "-----END RSA PRIVATE KEY-----"))
@@ -602,11 +617,14 @@ sign hash.
 Return: NULL for success, or an error string */
 
 const uschar *
-exim_rsa_sign(es_ctx * sign_ctx, BOOL is_sha1, blob * data, blob * sig)
+exim_dkim_sign(es_ctx * sign_ctx, BOOL is_sha1, blob * data, blob * sig)
 {
 uint len;
 const uschar * ret = NULL;
 
+/*XXX will need extension for non-RSA signing algo.  Maybe use
+https://www.openssl.org/docs/man1.0.2/crypto/EVP_PKEY_sign.html  ???  */
+
 /* Allocate mem for signature */
 len = RSA_size(sign_ctx->rsa);
 sig->data = store_get(len);
@@ -630,14 +648,16 @@ return ret;;
 
 
 /* import public key (from DER in memory)
-Return: nULL for success, or an error string */
+Return: NULL for success, or an error string */
 
 const uschar *
-exim_rsa_verify_init(blob * pubkey_der, ev_ctx * verify_ctx)
+exim_dkim_verify_init(blob * pubkey_der, ev_ctx * verify_ctx)
 {
 const uschar * p = CUS pubkey_der->data;
 const uschar * ret = NULL;
 
+/*XXX d2i_X509_PUBKEY, X509_get_pubkey(), and an EVP_PKEY* in verify_ctx. */
+
 if (!(verify_ctx->rsa = d2i_RSA_PUBKEY(NULL, &p, (long) pubkey_der->len)))
   {
   char ssl_errstring[256];
@@ -655,10 +675,18 @@ return ret;
 Return: NULL for success, or an error string */
 
 const uschar *
-exim_rsa_verify(ev_ctx * verify_ctx, BOOL is_sha1, blob * data_hash, blob * sig)
+exim_dkim_verify(ev_ctx * verify_ctx, BOOL is_sha1, blob * data_hash, blob * sig)
 {
 const uschar * ret = NULL;
 
+/*XXX needs extension for SHA512, Possibly EVP_PKEY_verify() is all we need???  */
+/* with EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) 
+- see example code at https://www.openssl.org/docs/man1.0.2/crypto/EVP_PKEY_verify.html
+and maybe also EVP_PKEY_CTX_set_signature_md(ctx, EVP_sha256()) though unclear
+if that only sets a pad/type field byte value, or sets up for an actual hash operation...
+Same on the signing side.
+https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_set_rsa_padding.html says it does what we want. */
+
 if (RSA_verify(is_sha1 ? NID_sha1 : NID_sha256,
       CUS data_hash->data, data_hash->len,
       US sig->data, (uint) sig->len, verify_ctx->rsa) != 1)
similarity index 74%
rename from src/src/pdkim/rsa.h
rename to src/src/pdkim/signing.h
index 6018eba..4e85808 100644 (file)
@@ -69,13 +69,13 @@ typedef struct {
 #endif
 
 
-extern void exim_rsa_init(void);
-extern blob * exim_rsa_data_append(blob *, int *, uschar *);
+extern void exim_dkim_init(void);
+extern blob * exim_dkim_data_append(blob *, int *, uschar *);
 
-extern const uschar * exim_rsa_signing_init(uschar *, es_ctx *);
-extern const uschar * exim_rsa_sign(es_ctx *, BOOL, blob *, blob *);
-extern const uschar * exim_rsa_verify_init(blob *, ev_ctx *);
-extern const uschar * exim_rsa_verify(ev_ctx *, BOOL, blob *, blob *);
+extern const uschar * exim_dkim_signing_init(uschar *, es_ctx *);
+extern const uschar * exim_dkim_sign(es_ctx *, BOOL, blob *, blob *);
+extern const uschar * exim_dkim_verify_init(blob *, ev_ctx *);
+extern const uschar * exim_dkim_verify(ev_ctx *, BOOL, blob *, blob *);
 
 #endif /*DISABLE_DKIM*/
 /* End of File */