From: Jeremy Harris Date: Fri, 19 May 2017 12:32:53 +0000 (+0100) Subject: DKIM: rename internal signing api X-Git-Tag: exim-4_90_RC1~77 X-Git-Url: https://vcs.fsf.org/?p=exim.git;a=commitdiff_plain;h=9b2583c440ab9104070054dfa02e8611799f777b DKIM: rename internal signing api --- diff --git a/src/scripts/MakeLinks b/src/scripts/MakeLinks index 44e3a4ebc..22e5a4bd7 100755 --- a/src/scripts/MakeLinks +++ b/src/scripts/MakeLinks @@ -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 diff --git a/src/src/dkim.c b/src/src/dkim.c index 2857e6398..f7b9ee0d1 100644 --- a/src/src/dkim.c +++ b/src/src/dkim.c @@ -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, diff --git a/src/src/hash.c b/src/src/hash.c index f49add29c..e239516e1 100644 --- a/src/src/hash.c +++ b/src/src/hash.c @@ -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; diff --git a/src/src/pdkim/Makefile b/src/src/pdkim/Makefile index c298568ea..10631ceaa 100644 --- a/src/src/pdkim/Makefile +++ b/src/src/pdkim/Makefile @@ -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 diff --git a/src/src/pdkim/pdkim.c b/src/src/pdkim/pdkim.c index 2ce0ff6c9..441f96cb5 100644 --- a/src/src/pdkim/pdkim.c +++ b/src/src/pdkim/pdkim.c @@ -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(); } diff --git a/src/src/pdkim/rsa.c b/src/src/pdkim/signing.c similarity index 84% rename from src/src/pdkim/rsa.c rename to src/src/pdkim/signing.c index 950c617c7..bcd64fdc4 100644 --- a/src/src/pdkim/rsa.c +++ b/src/src/pdkim/signing.c @@ -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" @@ -15,21 +19,21 @@ #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) diff --git a/src/src/pdkim/rsa.h b/src/src/pdkim/signing.h similarity index 74% rename from src/src/pdkim/rsa.h rename to src/src/pdkim/signing.h index 6018eba64..4e8580859 100644 --- a/src/src/pdkim/rsa.h +++ b/src/src/pdkim/signing.h @@ -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 */