#endif
#include "pdkim.h"
-#include "rsa.h"
+#include "signing.h"
#define PDKIM_SIGNATURE_VERSION "1"
#define PDKIM_PUB_RECORD_VERSION US "DKIM1"
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
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)
{
"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))
{
/* 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]);
"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;
while (sig)
{
+/*XXX bool probably not enough */
BOOL is_sha1 = sig->algo == PDKIM_ALGO_RSA_SHA1;
hctx hhash_ctx;
uschar * sig_hdr = US"";
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));
}
}
/* 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)
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;
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;
}
/* 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;
/* -------------------------------------------------------------------------- */
+/*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 *),
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))
{
void
pdkim_init(void)
{
-exim_rsa_init();
+exim_dkim_init();
}
* 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);
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;
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);
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;
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
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;
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;
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);
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. */
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*/
}
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,
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);
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()
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()
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))",
/******************************************************************************/
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*/
}
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-----"))
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);
/* 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];
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)