DKIM: rename internal signing api
[exim.git] / src / src / pdkim / signing.c
CommitLineData
2592e6c0
JH
1/*
2 * PDKIM - a RFC4871 (DKIM) implementation
3 *
4 * Copyright (C) 2016 Exim maintainers
5 *
6 * RSA signing/verification interface
9b2583c4
JH
7XXX rename interfaces to cover all signature methods.
8the method (algo) needs to be extracted from the supplied private-key
9and not only stashed as needed in the sign- or verify- context, but
10indicated to caller for protocol tag construction.
2592e6c0
JH
11 */
12
13#include "../exim.h"
14
15#ifndef DISABLE_DKIM /* entire file */
16
17#ifndef SUPPORT_TLS
18# error Need SUPPORT_TLS for DKIM
19#endif
20
21#include "crypt_ver.h"
9b2583c4 22#include "signing.h"
2592e6c0
JH
23
24
25/******************************************************************************/
26#ifdef RSA_GNUTLS
27
28void
9b2583c4 29exim_dkim_init(void)
2592e6c0
JH
30{
31}
32
33
34/* accumulate data (gnutls-only). String to be appended must be nul-terminated. */
35blob *
9b2583c4 36exim_dkim_data_append(blob * b, int * alloc, uschar * s)
2592e6c0
JH
37{
38int len = b->len;
39b->data = string_append(b->data, alloc, &len, 1, s);
40b->len = len;
41return b;
42}
43
44
45
46/* import private key from PEM string in memory.
47Return: NULL for success, or an error string */
48
49const uschar *
9b2583c4 50exim_dkim_signing_init(uschar * privkey_pem, es_ctx * sign_ctx)
2592e6c0
JH
51{
52gnutls_datum_t k;
53int rc;
54
55k.data = privkey_pem;
56k.size = strlen(privkey_pem);
57
58if ( (rc = gnutls_x509_privkey_init(&sign_ctx->rsa)) != GNUTLS_E_SUCCESS
9b2583c4 59 || (rc = gnutls_x509_privkey_import(sign_ctx->rsa, &k,
2592e6c0
JH
60 GNUTLS_X509_FMT_PEM)) != GNUTLS_E_SUCCESS
61 )
62 return gnutls_strerror(rc);
63
64return NULL;
65}
66
67
68
69/* allocate mem for signature (when signing) */
70/* sign data (gnutls_only)
71OR
72sign hash.
73
74Return: NULL for success, or an error string */
75
76const uschar *
9b2583c4 77exim_dkim_sign(es_ctx * sign_ctx, BOOL is_sha1, blob * data, blob * sig)
2592e6c0
JH
78{
79gnutls_datum_t k;
80size_t sigsize = 0;
81int rc;
82const uschar * ret = NULL;
83
84/* Allocate mem for signature */
85k.data = data->data;
86k.size = data->len;
87(void) gnutls_x509_privkey_sign_data(sign_ctx->rsa,
88 is_sha1 ? GNUTLS_DIG_SHA1 : GNUTLS_DIG_SHA256,
89 0, &k, NULL, &sigsize);
90
91sig->data = store_get(sigsize);
92sig->len = sigsize;
93
94/* Do signing */
9b2583c4
JH
95/*XXX will need extension for hash type; looks ok for non-RSA algos
96so long as the privkey_import stage got them. */
2592e6c0
JH
97if ((rc = gnutls_x509_privkey_sign_data(sign_ctx->rsa,
98 is_sha1 ? GNUTLS_DIG_SHA1 : GNUTLS_DIG_SHA256,
99 0, &k, sig->data, &sigsize)) != GNUTLS_E_SUCCESS
100 )
101 ret = gnutls_strerror(rc);
102
103gnutls_x509_privkey_deinit(sign_ctx->rsa);
104return ret;
105}
106
107
108
109/* import public key (from DER in memory)
110Return: NULL for success, or an error string */
111
112const uschar *
9b2583c4 113exim_dkim_verify_init(blob * pubkey_der, ev_ctx * verify_ctx)
2592e6c0
JH
114{
115gnutls_datum_t k;
116int rc;
117const uschar * ret = NULL;
118
119gnutls_pubkey_init(&verify_ctx->rsa);
120
121k.data = pubkey_der->data;
122k.size = pubkey_der->len;
123
124if ((rc = gnutls_pubkey_import(verify_ctx->rsa, &k, GNUTLS_X509_FMT_DER))
125 != GNUTLS_E_SUCCESS)
126 ret = gnutls_strerror(rc);
127return ret;
128}
129
130
131/* verify signature (of hash) (given pubkey & alleged sig)
132Return: NULL for success, or an error string */
133
134const uschar *
9b2583c4 135exim_dkim_verify(ev_ctx * verify_ctx, BOOL is_sha1, blob * data_hash, blob * sig)
2592e6c0
JH
136{
137gnutls_datum_t k, s;
138int rc;
139const uschar * ret = NULL;
140
141k.data = data_hash->data;
142k.size = data_hash->len;
143s.data = sig->data;
144s.size = sig->len;
145if ((rc = gnutls_pubkey_verify_hash2(verify_ctx->rsa,
9b2583c4 146/*XXX needs extension for SHA512 */
2592e6c0
JH
147 is_sha1 ? GNUTLS_SIGN_RSA_SHA1 : GNUTLS_SIGN_RSA_SHA256,
148 0, &k, &s)) < 0)
149 ret = gnutls_strerror(rc);
150
151gnutls_pubkey_deinit(verify_ctx->rsa);
152return ret;
153}
154
155
156
157
158#elif defined(RSA_GCRYPT)
159/******************************************************************************/
160
161
162/* Internal service routine:
163Read and move past an asn.1 header, checking class & tag,
164optionally returning the data-length */
165
166static int
167as_tag(blob * der, uschar req_cls, long req_tag, long * alen)
168{
169int rc;
170uschar tag_class;
171int taglen;
172long tag, len;
173
6e4aaa85 174/* debug_printf_indent("as_tag: %02x %02x %02x %02x\n",
2592e6c0
JH
175 der->data[0], der->data[1], der->data[2], der->data[3]); */
176
177if ((rc = asn1_get_tag_der(der->data++, der->len--, &tag_class, &taglen, &tag))
178 != ASN1_SUCCESS)
179 return rc;
180
181if (tag_class != req_cls || tag != req_tag) return ASN1_ELEMENT_NOT_FOUND;
182
183if ((len = asn1_get_length_der(der->data, der->len, &taglen)) < 0)
184 return ASN1_DER_ERROR;
185if (alen) *alen = len;
186
6e4aaa85 187/* debug_printf_indent("as_tag: tlen %d dlen %d\n", taglen, (int)len); */
2592e6c0
JH
188
189der->data += taglen;
190der->len -= taglen;
191return rc;
192}
193
194/* Internal service routine:
195Read and move over an asn.1 integer, setting an MPI to the value
196*/
197
198static uschar *
199as_mpi(blob * der, gcry_mpi_t * mpi)
200{
201long alen;
202int rc;
203gcry_error_t gerr;
204
205/* integer; move past the header */
206if ((rc = as_tag(der, 0, ASN1_TAG_INTEGER, &alen)) != ASN1_SUCCESS)
207 return US asn1_strerror(rc);
208
209/* read to an MPI */
210if ((gerr = gcry_mpi_scan(mpi, GCRYMPI_FMT_STD, der->data, alen, NULL)))
211 return US gcry_strerror(gerr);
212
213/* move over the data */
214der->data += alen; der->len -= alen;
215return NULL;
216}
217
218
219
220void
9b2583c4 221exim_dkim_init(void)
2592e6c0
JH
222{
223/* Version check should be the very first call because it
224makes sure that important subsystems are initialized. */
225if (!gcry_check_version (GCRYPT_VERSION))
226 {
227 fputs ("libgcrypt version mismatch\n", stderr);
228 exit (2);
229 }
230
231/* We don't want to see any warnings, e.g. because we have not yet
232parsed program options which might be used to suppress such
233warnings. */
234gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN);
235
236/* ... If required, other initialization goes here. Note that the
237process might still be running with increased privileges and that
238the secure memory has not been initialized. */
239
240/* Allocate a pool of 16k secure memory. This make the secure memory
241available and also drops privileges where needed. */
242gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0);
243
244/* It is now okay to let Libgcrypt complain when there was/is
245a problem with the secure memory. */
246gcry_control (GCRYCTL_RESUME_SECMEM_WARN);
247
248/* ... If required, other initialization goes here. */
249
250/* Tell Libgcrypt that initialization has completed. */
251gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
252
253return;
254}
255
256
257
258
259/* Accumulate data (gnutls-only).
260String to be appended must be nul-terminated. */
261
262blob *
9b2583c4 263exim_dkim_data_append(blob * b, int * alloc, uschar * s)
2592e6c0
JH
264{
265return b; /*dummy*/
266}
267
268
269
270/* import private key from PEM string in memory.
271Return: NULL for success, or an error string */
272
273const uschar *
9b2583c4 274exim_dkim_signing_init(uschar * privkey_pem, es_ctx * sign_ctx)
2592e6c0
JH
275{
276uschar * s1, * s2;
277blob der;
278long alen;
279int rc;
280
9b2583c4
JH
281/*XXX will need extension to _spot_ as well as handle a
282non-RSA key? I think... */
283
2592e6c0
JH
284/*
285 * RSAPrivateKey ::= SEQUENCE
286 * version Version,
287 * modulus INTEGER, -- n
288 * publicExponent INTEGER, -- e
289 * privateExponent INTEGER, -- d
290 * prime1 INTEGER, -- p
291 * prime2 INTEGER, -- q
292 * exponent1 INTEGER, -- d mod (p-1)
293 * exponent2 INTEGER, -- d mod (q-1)
294 * coefficient INTEGER, -- (inverse of q) mod p
295 * otherPrimeInfos OtherPrimeInfos OPTIONAL
296 */
297
298if ( !(s1 = Ustrstr(CS privkey_pem, "-----BEGIN RSA PRIVATE KEY-----"))
299 || !(s2 = Ustrstr(CS (s1+=31), "-----END RSA PRIVATE KEY-----" ))
300 )
301 return US"Bad PEM wrapper";
302
303*s2 = '\0';
304
305if ((der.len = b64decode(s1, &der.data)) < 0)
306 return US"Bad PEM-DER b64 decode";
307
308/* untangle asn.1 */
309
310/* sequence; just move past the header */
311if ((rc = as_tag(&der, ASN1_CLASS_STRUCTURED, ASN1_TAG_SEQUENCE, NULL))
312 != ASN1_SUCCESS) goto asn_err;
313
314/* integer version; move past the header, check is zero */
315if ((rc = as_tag(&der, 0, ASN1_TAG_INTEGER, &alen)) != ASN1_SUCCESS)
316 goto asn_err;
317if (alen != 1 || *der.data != 0)
318 return US"Bad version number";
319der.data++; der.len--;
320
321if ( (s1 = as_mpi(&der, &sign_ctx->n))
322 || (s1 = as_mpi(&der, &sign_ctx->e))
323 || (s1 = as_mpi(&der, &sign_ctx->d))
324 || (s1 = as_mpi(&der, &sign_ctx->p))
325 || (s1 = as_mpi(&der, &sign_ctx->q))
326 || (s1 = as_mpi(&der, &sign_ctx->dp))
327 || (s1 = as_mpi(&der, &sign_ctx->dq))
328 || (s1 = as_mpi(&der, &sign_ctx->qp))
329 )
330 return s1;
331
6e4aaa85 332DEBUG(D_acl) debug_printf_indent("rsa_signing_init:\n");
2592e6c0
JH
333 {
334 uschar * s;
335 gcry_mpi_aprint (GCRYMPI_FMT_HEX, &s, NULL, sign_ctx->n);
6e4aaa85 336 debug_printf_indent(" N : %s\n", s);
2592e6c0 337 gcry_mpi_aprint (GCRYMPI_FMT_HEX, &s, NULL, sign_ctx->e);
6e4aaa85 338 debug_printf_indent(" E : %s\n", s);
2592e6c0 339 gcry_mpi_aprint (GCRYMPI_FMT_HEX, &s, NULL, sign_ctx->d);
6e4aaa85 340 debug_printf_indent(" D : %s\n", s);
2592e6c0 341 gcry_mpi_aprint (GCRYMPI_FMT_HEX, &s, NULL, sign_ctx->p);
6e4aaa85 342 debug_printf_indent(" P : %s\n", s);
2592e6c0 343 gcry_mpi_aprint (GCRYMPI_FMT_HEX, &s, NULL, sign_ctx->q);
6e4aaa85 344 debug_printf_indent(" Q : %s\n", s);
2592e6c0 345 gcry_mpi_aprint (GCRYMPI_FMT_HEX, &s, NULL, sign_ctx->dp);
6e4aaa85 346 debug_printf_indent(" DP: %s\n", s);
2592e6c0 347 gcry_mpi_aprint (GCRYMPI_FMT_HEX, &s, NULL, sign_ctx->dq);
6e4aaa85 348 debug_printf_indent(" DQ: %s\n", s);
2592e6c0 349 gcry_mpi_aprint (GCRYMPI_FMT_HEX, &s, NULL, sign_ctx->qp);
6e4aaa85 350 debug_printf_indent(" QP: %s\n", s);
2592e6c0
JH
351 }
352return NULL;
353
354asn_err: return US asn1_strerror(rc);
355}
356
357
358
359/* allocate mem for signature (when signing) */
360/* sign data (gnutls_only)
361OR
362sign hash.
363
364Return: NULL for success, or an error string */
365
366const uschar *
9b2583c4 367exim_dkim_sign(es_ctx * sign_ctx, BOOL is_sha1, blob * data, blob * sig)
2592e6c0
JH
368{
369gcry_sexp_t s_hash = NULL, s_key = NULL, s_sig = NULL;
370gcry_mpi_t m_sig;
371uschar * errstr;
372gcry_error_t gerr;
373
9b2583c4
JH
374/*XXX will need extension for hash types (though, possibly, should
375be re-specced to not rehash but take an already-hashed value? Actually
376current impl looks WRONG - it _is_ given a has so should not be
377re-hashing. Has this been tested?
378
379Will need extension for non-RSA sugning algos. */
380
2592e6c0
JH
381#define SIGSPACE 128
382sig->data = store_get(SIGSPACE);
383
384if (gcry_mpi_cmp (sign_ctx->p, sign_ctx->q) > 0)
385 {
386 gcry_mpi_swap (sign_ctx->p, sign_ctx->q);
387 gcry_mpi_invm (sign_ctx->qp, sign_ctx->p, sign_ctx->q);
388 }
389
390if ( (gerr = gcry_sexp_build (&s_key, NULL,
391 "(private-key (rsa (n%m)(e%m)(d%m)(p%m)(q%m)(u%m)))",
392 sign_ctx->n, sign_ctx->e,
393 sign_ctx->d, sign_ctx->p,
394 sign_ctx->q, sign_ctx->qp))
395 || (gerr = gcry_sexp_build (&s_hash, NULL,
396 is_sha1
397 ? "(data(flags pkcs1)(hash sha1 %b))"
398 : "(data(flags pkcs1)(hash sha256 %b))",
399 (int) data->len, CS data->data))
400 || (gerr = gcry_pk_sign (&s_sig, s_hash, s_key))
401 )
402 return US gcry_strerror(gerr);
403
404/* gcry_sexp_dump(s_sig); */
405
406if ( !(s_sig = gcry_sexp_find_token(s_sig, "s", 0))
407 )
408 return US"no sig result";
409
410m_sig = gcry_sexp_nth_mpi(s_sig, 1, GCRYMPI_FMT_USG);
411
412DEBUG(D_acl)
413 {
414 uschar * s;
415 gcry_mpi_aprint (GCRYMPI_FMT_HEX, &s, NULL, m_sig);
6e4aaa85 416 debug_printf_indent(" SG: %s\n", s);
2592e6c0
JH
417 }
418
419gerr = gcry_mpi_print(GCRYMPI_FMT_USG, sig->data, SIGSPACE, &sig->len, m_sig);
420if (gerr)
421 {
6e4aaa85 422 debug_printf_indent("signature conversion from MPI to buffer failed\n");
2592e6c0
JH
423 return US gcry_strerror(gerr);
424 }
425#undef SIGSPACE
426
427return NULL;
428}
429
430
431/* import public key (from DER in memory)
432Return: NULL for success, or an error string */
433
434const uschar *
9b2583c4 435exim_dkim_verify_init(blob * pubkey_der, ev_ctx * verify_ctx)
2592e6c0
JH
436{
437/*
438in code sequence per b81207d2bfa92 rsa_parse_public_key() and asn1_get_mpi()
439*/
440uschar tag_class;
441int taglen;
442long alen;
443int rc;
444uschar * errstr;
445gcry_error_t gerr;
446uschar * stage = US"S1";
447
448/*
449sequence
450 sequence
451 OBJECT:rsaEncryption
452 NULL
453 BIT STRING:RSAPublicKey
454 sequence
455 INTEGER:Public modulus
456 INTEGER:Public exponent
457
458openssl rsa -in aux-fixed/dkim/dkim.private -pubout -outform DER | od -t x1 | head;
459openssl rsa -in aux-fixed/dkim/dkim.private -pubout | openssl asn1parse -dump;
460openssl rsa -in aux-fixed/dkim/dkim.private -pubout | openssl asn1parse -dump -offset 22;
461*/
462
463/* sequence; just move past the header */
464if ((rc = as_tag(pubkey_der, ASN1_CLASS_STRUCTURED, ASN1_TAG_SEQUENCE, NULL))
465 != ASN1_SUCCESS) goto asn_err;
466
467/* sequence; skip the entire thing */
468DEBUG(D_acl) stage = US"S2";
469if ((rc = as_tag(pubkey_der, ASN1_CLASS_STRUCTURED, ASN1_TAG_SEQUENCE, &alen))
470 != ASN1_SUCCESS) goto asn_err;
471pubkey_der->data += alen; pubkey_der->len -= alen;
472
473
474/* bitstring: limit range to size of bitstring;
475move over header + content wrapper */
476DEBUG(D_acl) stage = US"BS";
477if ((rc = as_tag(pubkey_der, 0, ASN1_TAG_BIT_STRING, &alen)) != ASN1_SUCCESS)
478 goto asn_err;
479pubkey_der->len = alen;
480pubkey_der->data++; pubkey_der->len--;
481
482/* sequence; just move past the header */
483DEBUG(D_acl) stage = US"S3";
484if ((rc = as_tag(pubkey_der, ASN1_CLASS_STRUCTURED, ASN1_TAG_SEQUENCE, NULL))
485 != ASN1_SUCCESS) goto asn_err;
486
487/* read two integers */
488DEBUG(D_acl) stage = US"MPI";
489if ( (errstr = as_mpi(pubkey_der, &verify_ctx->n))
490 || (errstr = as_mpi(pubkey_der, &verify_ctx->e))
491 )
492 return errstr;
493
6e4aaa85 494DEBUG(D_acl) debug_printf_indent("rsa_verify_init:\n");
2592e6c0
JH
495 {
496 uschar * s;
497 gcry_mpi_aprint (GCRYMPI_FMT_HEX, &s, NULL, verify_ctx->n);
6e4aaa85 498 debug_printf_indent(" N : %s\n", s);
2592e6c0 499 gcry_mpi_aprint (GCRYMPI_FMT_HEX, &s, NULL, verify_ctx->e);
6e4aaa85 500 debug_printf_indent(" E : %s\n", s);
2592e6c0
JH
501 }
502
503return NULL;
504
505asn_err:
506DEBUG(D_acl) return string_sprintf("%s: %s", stage, asn1_strerror(rc));
507 return US asn1_strerror(rc);
508}
509
510
511/* verify signature (of hash) (given pubkey & alleged sig)
512Return: NULL for success, or an error string */
513
514const uschar *
9b2583c4 515exim_dkim_verify(ev_ctx * verify_ctx, BOOL is_sha1, blob * data_hash, blob * sig)
2592e6c0
JH
516{
517/*
518cf. libgnutls 2.8.5 _wrap_gcry_pk_verify()
519*/
520gcry_mpi_t m_sig;
521gcry_sexp_t s_sig = NULL, s_hash = NULL, s_pkey = NULL;
522gcry_error_t gerr;
523uschar * stage;
524
525if ( (stage = US"pkey sexp build",
526 gerr = gcry_sexp_build (&s_pkey, NULL, "(public-key(rsa(n%m)(e%m)))",
527 verify_ctx->n, verify_ctx->e))
528 || (stage = US"data sexp build",
529 gerr = gcry_sexp_build (&s_hash, NULL,
9b2583c4 530/*XXX needs extension for SHA512 */
2592e6c0
JH
531 is_sha1
532 ? "(data(flags pkcs1)(hash sha1 %b))"
533 : "(data(flags pkcs1)(hash sha256 %b))",
534 (int) data_hash->len, CS data_hash->data))
535 || (stage = US"sig mpi scan",
536 gerr = gcry_mpi_scan(&m_sig, GCRYMPI_FMT_USG, sig->data, sig->len, NULL))
537 || (stage = US"sig sexp build",
538 gerr = gcry_sexp_build (&s_sig, NULL, "(sig-val(rsa(s%m)))", m_sig))
539 || (stage = US"verify",
540 gerr = gcry_pk_verify (s_sig, s_hash, s_pkey))
541 )
542 {
6e4aaa85 543 DEBUG(D_acl) debug_printf_indent("verify: error in stage '%s'\n", stage);
2592e6c0
JH
544 return US gcry_strerror(gerr);
545 }
546
547if (s_sig) gcry_sexp_release (s_sig);
548if (s_hash) gcry_sexp_release (s_hash);
549if (s_pkey) gcry_sexp_release (s_pkey);
550gcry_mpi_release (m_sig);
551gcry_mpi_release (verify_ctx->n);
552gcry_mpi_release (verify_ctx->e);
553
554return NULL;
555}
556
557
558
559
560#elif defined(RSA_OPENSSL)
561/******************************************************************************/
562
563void
9b2583c4 564exim_dkim_init(void)
2592e6c0
JH
565{
566}
567
568
569/* accumulate data (gnutls-only) */
570blob *
9b2583c4 571exim_dkim_data_append(blob * b, int * alloc, uschar * s)
2592e6c0
JH
572{
573return b; /*dummy*/
574}
575
576
577/* import private key from PEM string in memory.
578Return: NULL for success, or an error string */
579
580const uschar *
9b2583c4 581exim_dkim_signing_init(uschar * privkey_pem, es_ctx * sign_ctx)
2592e6c0
JH
582{
583uschar * p, * q;
584int len;
585
9b2583c4
JH
586/*XXX maybe use PEM_read_bio_PrivateKey() ???
587The sign_ctx would need to have an EVP_PKEY* */
588
2592e6c0
JH
589/* Convert PEM to DER */
590if ( !(p = Ustrstr(privkey_pem, "-----BEGIN RSA PRIVATE KEY-----"))
591 || !(q = Ustrstr(p+=31, "-----END RSA PRIVATE KEY-----"))
592 )
593 return US"Bad PEM wrapping";
594
595*q = '\0';
596if ((len = b64decode(p, &p)) < 0)
597 return US"b64decode failed";
598
599if (!(sign_ctx->rsa = d2i_RSAPrivateKey(NULL, CUSS &p, len)))
600 {
601 char ssl_errstring[256];
602 ERR_load_crypto_strings(); /*XXX move to a startup routine */
603 ERR_error_string(ERR_get_error(), ssl_errstring);
b78006ac 604 return string_copy(US ssl_errstring);
2592e6c0
JH
605 }
606
607return NULL;
608}
609
610
611
612/* allocate mem for signature (when signing) */
613/* sign data (gnutls_only)
614OR
615sign hash.
616
617Return: NULL for success, or an error string */
618
619const uschar *
9b2583c4 620exim_dkim_sign(es_ctx * sign_ctx, BOOL is_sha1, blob * data, blob * sig)
2592e6c0
JH
621{
622uint len;
623const uschar * ret = NULL;
624
9b2583c4
JH
625/*XXX will need extension for non-RSA signing algo. Maybe use
626https://www.openssl.org/docs/man1.0.2/crypto/EVP_PKEY_sign.html ??? */
627
2592e6c0
JH
628/* Allocate mem for signature */
629len = RSA_size(sign_ctx->rsa);
630sig->data = store_get(len);
631sig->len = len;
632
633/* Do signing */
634if (RSA_sign(is_sha1 ? NID_sha1 : NID_sha256,
635 CUS data->data, data->len,
636 US sig->data, &len, sign_ctx->rsa) != 1)
637 {
638 char ssl_errstring[256];
639 ERR_load_crypto_strings(); /*XXX move to a startup routine */
640 ERR_error_string(ERR_get_error(), ssl_errstring);
b78006ac 641 ret = string_copy(US ssl_errstring);
2592e6c0
JH
642 }
643
644RSA_free(sign_ctx->rsa);
645return ret;;
646}
647
648
649
650/* import public key (from DER in memory)
9b2583c4 651Return: NULL for success, or an error string */
2592e6c0
JH
652
653const uschar *
9b2583c4 654exim_dkim_verify_init(blob * pubkey_der, ev_ctx * verify_ctx)
2592e6c0
JH
655{
656const uschar * p = CUS pubkey_der->data;
657const uschar * ret = NULL;
658
9b2583c4
JH
659/*XXX d2i_X509_PUBKEY, X509_get_pubkey(), and an EVP_PKEY* in verify_ctx. */
660
2592e6c0
JH
661if (!(verify_ctx->rsa = d2i_RSA_PUBKEY(NULL, &p, (long) pubkey_der->len)))
662 {
663 char ssl_errstring[256];
664 ERR_load_crypto_strings(); /*XXX move to a startup routine */
665 ERR_error_string(ERR_get_error(), ssl_errstring);
b78006ac 666 ret = string_copy(CUS ssl_errstring);
2592e6c0
JH
667 }
668return ret;
669}
670
671
672
673
674/* verify signature (of hash) (given pubkey & alleged sig)
675Return: NULL for success, or an error string */
676
677const uschar *
9b2583c4 678exim_dkim_verify(ev_ctx * verify_ctx, BOOL is_sha1, blob * data_hash, blob * sig)
2592e6c0
JH
679{
680const uschar * ret = NULL;
681
9b2583c4
JH
682/*XXX needs extension for SHA512, Possibly EVP_PKEY_verify() is all we need??? */
683/* with EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING)
684- see example code at https://www.openssl.org/docs/man1.0.2/crypto/EVP_PKEY_verify.html
685and maybe also EVP_PKEY_CTX_set_signature_md(ctx, EVP_sha256()) though unclear
686if that only sets a pad/type field byte value, or sets up for an actual hash operation...
687Same on the signing side.
688https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_set_rsa_padding.html says it does what we want. */
689
2592e6c0
JH
690if (RSA_verify(is_sha1 ? NID_sha1 : NID_sha256,
691 CUS data_hash->data, data_hash->len,
692 US sig->data, (uint) sig->len, verify_ctx->rsa) != 1)
693 {
694 char ssl_errstring[256];
695 ERR_load_crypto_strings(); /*XXX move to a startup routine */
696 ERR_error_string(ERR_get_error(), ssl_errstring);
b78006ac 697 ret = string_copy(US ssl_errstring);
2592e6c0
JH
698 }
699return ret;
700}
701
702
703#endif
704/******************************************************************************/
705
706#endif /*DISABLE_DKIM*/
707/* End of File */