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