2 * PDKIM - a RFC4871 (DKIM) implementation
4 * Copyright (C) 2016 Exim maintainers
6 * signing/verification interface
11 #ifndef DISABLE_DKIM /* entire file */
14 # error Need SUPPORT_TLS for DKIM
17 #include "crypt_ver.h"
21 /******************************************************************************/
30 /* accumulate data (gnutls-only). String to be appended must be nul-terminated. */
32 exim_dkim_data_append(blob
* b
, int * alloc
, uschar
* s
)
35 b
->data
= string_append(b
->data
, alloc
, &len
, 1, s
);
42 /* import private key from PEM string in memory.
43 Return: NULL for success, or an error string */
46 exim_dkim_signing_init(uschar
* privkey_pem
, es_ctx
* sign_ctx
)
52 k
.size
= strlen(privkey_pem
);
54 if ( (rc
= gnutls_x509_privkey_init(&sign_ctx
->key
)) != GNUTLS_E_SUCCESS
55 || (rc
= gnutls_x509_privkey_import(sign_ctx
->key
, &k
,
56 GNUTLS_X509_FMT_PEM
)) != GNUTLS_E_SUCCESS
58 return gnutls_strerror(rc
);
65 /* allocate mem for signature (when signing) */
66 /* sign data (gnutls_only)
70 Return: NULL for success, or an error string */
73 exim_dkim_sign(es_ctx
* sign_ctx
, hashmethod hash
, blob
* data
, blob
* sig
)
75 gnutls_digest_algorithm_t dig
;
79 const uschar
* ret
= NULL
;
83 case HASH_SHA1
: dig
= GNUTLS_DIG_SHA1
; break;
84 case HASH_SHA2_256
: dig
= GNUTLS_DIG_SHA256
; break;
85 case HASH_SHA2_512
: dig
= GNUTLS_DIG_SHA512
; break;
86 default: return US
"nonhandled hash type";
89 /* Allocate mem for signature */
92 (void) gnutls_x509_privkey_sign_data(sign_ctx
->key
, dig
,
93 0, &k
, NULL
, &sigsize
);
95 sig
->data
= store_get(sigsize
);
99 if ((rc
= gnutls_x509_privkey_sign_data(sign_ctx
->key
, dig
,
100 0, &k
, sig
->data
, &sigsize
)) != GNUTLS_E_SUCCESS
102 ret
= gnutls_strerror(rc
);
104 gnutls_x509_privkey_deinit(sign_ctx
->key
);
110 /* import public key (from DER in memory)
111 Return: NULL for success, or an error string */
114 exim_dkim_verify_init(blob
* pubkey_der
, ev_ctx
* verify_ctx
)
118 const uschar
* ret
= NULL
;
120 gnutls_pubkey_init(&verify_ctx
->key
);
122 k
.data
= pubkey_der
->data
;
123 k
.size
= pubkey_der
->len
;
125 if ((rc
= gnutls_pubkey_import(verify_ctx
->key
, &k
, GNUTLS_X509_FMT_DER
))
127 ret
= gnutls_strerror(rc
);
132 /* verify signature (of hash) (given pubkey & alleged sig)
133 Return: NULL for success, or an error string */
136 exim_dkim_verify(ev_ctx
* verify_ctx
, hashmethod hash
, blob
* data_hash
, blob
* sig
)
138 gnutls_sign_algorithm_t algo
;
141 const uschar
* ret
= NULL
;
143 /*XXX needs extension for non-rsa */
146 case HASH_SHA1
: algo
= GNUTLS_SIGN_RSA_SHA1
; break;
147 case HASH_SHA2_256
: algo
= GNUTLS_SIGN_RSA_SHA256
; break;
148 case HASH_SHA2_512
: algo
= GNUTLS_SIGN_RSA_SHA512
; break;
149 default: return US
"nonhandled hash type";
152 k
.data
= data_hash
->data
;
153 k
.size
= data_hash
->len
;
156 if ((rc
= gnutls_pubkey_verify_hash2(verify_ctx
->key
, algo
, 0, &k
, &s
)) < 0)
157 ret
= gnutls_strerror(rc
);
159 gnutls_pubkey_deinit(verify_ctx
->key
);
166 #elif defined(SIGN_GCRYPT)
167 /******************************************************************************/
168 /* This variant is used under pre-3.0.0 GnuTLS. Only rsa-sha1 and rsa-sha256 */
171 /* Internal service routine:
172 Read and move past an asn.1 header, checking class & tag,
173 optionally returning the data-length */
176 as_tag(blob
* der
, uschar req_cls
, long req_tag
, long * alen
)
183 /* debug_printf_indent("as_tag: %02x %02x %02x %02x\n",
184 der->data[0], der->data[1], der->data[2], der->data[3]); */
186 if ((rc
= asn1_get_tag_der(der
->data
++, der
->len
--, &tag_class
, &taglen
, &tag
))
190 if (tag_class
!= req_cls
|| tag
!= req_tag
) return ASN1_ELEMENT_NOT_FOUND
;
192 if ((len
= asn1_get_length_der(der
->data
, der
->len
, &taglen
)) < 0)
193 return ASN1_DER_ERROR
;
194 if (alen
) *alen
= len
;
196 /* debug_printf_indent("as_tag: tlen %d dlen %d\n", taglen, (int)len); */
203 /* Internal service routine:
204 Read and move over an asn.1 integer, setting an MPI to the value
208 as_mpi(blob
* der
, gcry_mpi_t
* mpi
)
214 /* integer; move past the header */
215 if ((rc
= as_tag(der
, 0, ASN1_TAG_INTEGER
, &alen
)) != ASN1_SUCCESS
)
216 return US
asn1_strerror(rc
);
219 if ((gerr
= gcry_mpi_scan(mpi
, GCRYMPI_FMT_STD
, der
->data
, alen
, NULL
)))
220 return US
gcry_strerror(gerr
);
222 /* move over the data */
223 der
->data
+= alen
; der
->len
-= alen
;
232 /* Version check should be the very first call because it
233 makes sure that important subsystems are initialized. */
234 if (!gcry_check_version (GCRYPT_VERSION
))
236 fputs ("libgcrypt version mismatch\n", stderr
);
240 /* We don't want to see any warnings, e.g. because we have not yet
241 parsed program options which might be used to suppress such
243 gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN
);
245 /* ... If required, other initialization goes here. Note that the
246 process might still be running with increased privileges and that
247 the secure memory has not been initialized. */
249 /* Allocate a pool of 16k secure memory. This make the secure memory
250 available and also drops privileges where needed. */
251 gcry_control (GCRYCTL_INIT_SECMEM
, 16384, 0);
253 /* It is now okay to let Libgcrypt complain when there was/is
254 a problem with the secure memory. */
255 gcry_control (GCRYCTL_RESUME_SECMEM_WARN
);
257 /* ... If required, other initialization goes here. */
259 /* Tell Libgcrypt that initialization has completed. */
260 gcry_control (GCRYCTL_INITIALIZATION_FINISHED
, 0);
268 /* Accumulate data (gnutls-only).
269 String to be appended must be nul-terminated. */
272 exim_dkim_data_append(blob
* b
, int * alloc
, uschar
* s
)
279 /* import private key from PEM string in memory.
280 Return: NULL for success, or an error string */
283 exim_dkim_signing_init(uschar
* privkey_pem
, es_ctx
* sign_ctx
)
290 /*XXX will need extension to _spot_ as well as handle a
291 non-RSA key? I think... */
294 * RSAPrivateKey ::= SEQUENCE
296 * modulus INTEGER, -- n
297 * publicExponent INTEGER, -- e
298 * privateExponent INTEGER, -- d
299 * prime1 INTEGER, -- p
300 * prime2 INTEGER, -- q
301 * exponent1 INTEGER, -- d mod (p-1)
302 * exponent2 INTEGER, -- d mod (q-1)
303 * coefficient INTEGER, -- (inverse of q) mod p
304 * otherPrimeInfos OtherPrimeInfos OPTIONAL
307 if ( !(s1
= Ustrstr(CS privkey_pem
, "-----BEGIN RSA PRIVATE KEY-----"))
308 || !(s2
= Ustrstr(CS (s1
+=31), "-----END RSA PRIVATE KEY-----" ))
310 return US
"Bad PEM wrapper";
314 if ((der
.len
= b64decode(s1
, &der
.data
)) < 0)
315 return US
"Bad PEM-DER b64 decode";
319 /* sequence; just move past the header */
320 if ((rc
= as_tag(&der
, ASN1_CLASS_STRUCTURED
, ASN1_TAG_SEQUENCE
, NULL
))
321 != ASN1_SUCCESS
) goto asn_err
;
323 /* integer version; move past the header, check is zero */
324 if ((rc
= as_tag(&der
, 0, ASN1_TAG_INTEGER
, &alen
)) != ASN1_SUCCESS
)
326 if (alen
!= 1 || *der
.data
!= 0)
327 return US
"Bad version number";
328 der
.data
++; der
.len
--;
330 if ( (s1
= as_mpi(&der
, &sign_ctx
->n
))
331 || (s1
= as_mpi(&der
, &sign_ctx
->e
))
332 || (s1
= as_mpi(&der
, &sign_ctx
->d
))
333 || (s1
= as_mpi(&der
, &sign_ctx
->p
))
334 || (s1
= as_mpi(&der
, &sign_ctx
->q
))
335 || (s1
= as_mpi(&der
, &sign_ctx
->dp
))
336 || (s1
= as_mpi(&der
, &sign_ctx
->dq
))
337 || (s1
= as_mpi(&der
, &sign_ctx
->qp
))
341 DEBUG(D_acl
) debug_printf_indent("rsa_signing_init:\n");
344 gcry_mpi_aprint (GCRYMPI_FMT_HEX
, &s
, NULL
, sign_ctx
->n
);
345 debug_printf_indent(" N : %s\n", s
);
346 gcry_mpi_aprint (GCRYMPI_FMT_HEX
, &s
, NULL
, sign_ctx
->e
);
347 debug_printf_indent(" E : %s\n", s
);
348 gcry_mpi_aprint (GCRYMPI_FMT_HEX
, &s
, NULL
, sign_ctx
->d
);
349 debug_printf_indent(" D : %s\n", s
);
350 gcry_mpi_aprint (GCRYMPI_FMT_HEX
, &s
, NULL
, sign_ctx
->p
);
351 debug_printf_indent(" P : %s\n", s
);
352 gcry_mpi_aprint (GCRYMPI_FMT_HEX
, &s
, NULL
, sign_ctx
->q
);
353 debug_printf_indent(" Q : %s\n", s
);
354 gcry_mpi_aprint (GCRYMPI_FMT_HEX
, &s
, NULL
, sign_ctx
->dp
);
355 debug_printf_indent(" DP: %s\n", s
);
356 gcry_mpi_aprint (GCRYMPI_FMT_HEX
, &s
, NULL
, sign_ctx
->dq
);
357 debug_printf_indent(" DQ: %s\n", s
);
358 gcry_mpi_aprint (GCRYMPI_FMT_HEX
, &s
, NULL
, sign_ctx
->qp
);
359 debug_printf_indent(" QP: %s\n", s
);
363 asn_err
: return US
asn1_strerror(rc
);
368 /* allocate mem for signature (when signing) */
369 /* sign data (gnutls_only)
373 Return: NULL for success, or an error string */
376 exim_dkim_sign(es_ctx
* sign_ctx
, hashmethod hash
, blob
* data
, blob
* sig
)
379 gcry_sexp_t s_hash
= NULL
, s_key
= NULL
, s_sig
= NULL
;
384 /*XXX will need extension for hash types (though, possibly, should
385 be re-specced to not rehash but take an already-hashed value? Actually
386 current impl looks WRONG - it _is_ given a hash so should not be
387 re-hashing. Has this been tested?
389 Will need extension for non-RSA sugning algos. */
393 case HASH_SHA1
: is_sha1
= TRUE
; break;
394 case HASH_SHA2_256
: is_sha1
= FALSE
; break;
395 default: return US
"nonhandled hash type";
399 sig
->data
= store_get(SIGSPACE
);
401 if (gcry_mpi_cmp (sign_ctx
->p
, sign_ctx
->q
) > 0)
403 gcry_mpi_swap (sign_ctx
->p
, sign_ctx
->q
);
404 gcry_mpi_invm (sign_ctx
->qp
, sign_ctx
->p
, sign_ctx
->q
);
407 if ( (gerr
= gcry_sexp_build (&s_key
, NULL
,
408 "(private-key (rsa (n%m)(e%m)(d%m)(p%m)(q%m)(u%m)))",
409 sign_ctx
->n
, sign_ctx
->e
,
410 sign_ctx
->d
, sign_ctx
->p
,
411 sign_ctx
->q
, sign_ctx
->qp
))
412 || (gerr
= gcry_sexp_build (&s_hash
, NULL
,
414 ? "(data(flags pkcs1)(hash sha1 %b))"
415 : "(data(flags pkcs1)(hash sha256 %b))",
416 (int) data
->len
, CS data
->data
))
417 || (gerr
= gcry_pk_sign (&s_sig
, s_hash
, s_key
))
419 return US
gcry_strerror(gerr
);
421 /* gcry_sexp_dump(s_sig); */
423 if ( !(s_sig
= gcry_sexp_find_token(s_sig
, "s", 0))
425 return US
"no sig result";
427 m_sig
= gcry_sexp_nth_mpi(s_sig
, 1, GCRYMPI_FMT_USG
);
432 gcry_mpi_aprint (GCRYMPI_FMT_HEX
, &s
, NULL
, m_sig
);
433 debug_printf_indent(" SG: %s\n", s
);
436 gerr
= gcry_mpi_print(GCRYMPI_FMT_USG
, sig
->data
, SIGSPACE
, &sig
->len
, m_sig
);
439 debug_printf_indent("signature conversion from MPI to buffer failed\n");
440 return US
gcry_strerror(gerr
);
448 /* import public key (from DER in memory)
449 Return: NULL for success, or an error string */
452 exim_dkim_verify_init(blob
* pubkey_der
, ev_ctx
* verify_ctx
)
455 in code sequence per b81207d2bfa92 rsa_parse_public_key() and asn1_get_mpi()
463 uschar
* stage
= US
"S1";
470 BIT STRING:RSAPublicKey
472 INTEGER:Public modulus
473 INTEGER:Public exponent
475 openssl rsa -in aux-fixed/dkim/dkim.private -pubout -outform DER | od -t x1 | head;
476 openssl rsa -in aux-fixed/dkim/dkim.private -pubout | openssl asn1parse -dump;
477 openssl rsa -in aux-fixed/dkim/dkim.private -pubout | openssl asn1parse -dump -offset 22;
480 /* sequence; just move past the header */
481 if ((rc
= as_tag(pubkey_der
, ASN1_CLASS_STRUCTURED
, ASN1_TAG_SEQUENCE
, NULL
))
482 != ASN1_SUCCESS
) goto asn_err
;
484 /* sequence; skip the entire thing */
485 DEBUG(D_acl
) stage
= US
"S2";
486 if ((rc
= as_tag(pubkey_der
, ASN1_CLASS_STRUCTURED
, ASN1_TAG_SEQUENCE
, &alen
))
487 != ASN1_SUCCESS
) goto asn_err
;
488 pubkey_der
->data
+= alen
; pubkey_der
->len
-= alen
;
491 /* bitstring: limit range to size of bitstring;
492 move over header + content wrapper */
493 DEBUG(D_acl
) stage
= US
"BS";
494 if ((rc
= as_tag(pubkey_der
, 0, ASN1_TAG_BIT_STRING
, &alen
)) != ASN1_SUCCESS
)
496 pubkey_der
->len
= alen
;
497 pubkey_der
->data
++; pubkey_der
->len
--;
499 /* sequence; just move past the header */
500 DEBUG(D_acl
) stage
= US
"S3";
501 if ((rc
= as_tag(pubkey_der
, ASN1_CLASS_STRUCTURED
, ASN1_TAG_SEQUENCE
, NULL
))
502 != ASN1_SUCCESS
) goto asn_err
;
504 /* read two integers */
505 DEBUG(D_acl
) stage
= US
"MPI";
506 if ( (errstr
= as_mpi(pubkey_der
, &verify_ctx
->n
))
507 || (errstr
= as_mpi(pubkey_der
, &verify_ctx
->e
))
511 DEBUG(D_acl
) debug_printf_indent("rsa_verify_init:\n");
514 gcry_mpi_aprint (GCRYMPI_FMT_HEX
, &s
, NULL
, verify_ctx
->n
);
515 debug_printf_indent(" N : %s\n", s
);
516 gcry_mpi_aprint (GCRYMPI_FMT_HEX
, &s
, NULL
, verify_ctx
->e
);
517 debug_printf_indent(" E : %s\n", s
);
523 DEBUG(D_acl
) return string_sprintf("%s: %s", stage
, asn1_strerror(rc
));
524 return US
asn1_strerror(rc
);
528 /* verify signature (of hash) (given pubkey & alleged sig)
529 Return: NULL for success, or an error string */
532 exim_dkim_verify(ev_ctx
* verify_ctx
, hashmethod hash
, blob
* data_hash
, blob
* sig
)
535 cf. libgnutls 2.8.5 _wrap_gcry_pk_verify()
538 gcry_sexp_t s_sig
= NULL
, s_hash
= NULL
, s_pkey
= NULL
;
544 case HASH_SHA1
: is_sha1
= TRUE
; break;
545 case HASH_SHA2_256
: is_sha1
= FALSE
; break;
546 default: return US
"nonhandled hash type";
549 if ( (stage
= US
"pkey sexp build",
550 gerr
= gcry_sexp_build (&s_pkey
, NULL
, "(public-key(rsa(n%m)(e%m)))",
551 verify_ctx
->n
, verify_ctx
->e
))
552 || (stage
= US
"data sexp build",
553 gerr
= gcry_sexp_build (&s_hash
, NULL
,
554 /*XXX needs extension for SHA512 */
556 ? "(data(flags pkcs1)(hash sha1 %b))"
557 : "(data(flags pkcs1)(hash sha256 %b))",
558 (int) data_hash
->len
, CS data_hash
->data
))
559 || (stage
= US
"sig mpi scan",
560 gerr
= gcry_mpi_scan(&m_sig
, GCRYMPI_FMT_USG
, sig
->data
, sig
->len
, NULL
))
561 || (stage
= US
"sig sexp build",
562 gerr
= gcry_sexp_build (&s_sig
, NULL
, "(sig-val(rsa(s%m)))", m_sig
))
563 || (stage
= US
"verify",
564 gerr
= gcry_pk_verify (s_sig
, s_hash
, s_pkey
))
567 DEBUG(D_acl
) debug_printf_indent("verify: error in stage '%s'\n", stage
);
568 return US
gcry_strerror(gerr
);
571 if (s_sig
) gcry_sexp_release (s_sig
);
572 if (s_hash
) gcry_sexp_release (s_hash
);
573 if (s_pkey
) gcry_sexp_release (s_pkey
);
574 gcry_mpi_release (m_sig
);
575 gcry_mpi_release (verify_ctx
->n
);
576 gcry_mpi_release (verify_ctx
->e
);
584 #elif defined(SIGN_OPENSSL)
585 /******************************************************************************/
590 ERR_load_crypto_strings();
594 /* accumulate data (gnutls-only) */
596 exim_dkim_data_append(blob
* b
, int * alloc
, uschar
* s
)
602 /* import private key from PEM string in memory.
603 Return: NULL for success, or an error string */
606 exim_dkim_signing_init(uschar
* privkey_pem
, es_ctx
* sign_ctx
)
608 BIO
* bp
= BIO_new_mem_buf(privkey_pem
, -1);
610 if (!(sign_ctx
->key
= PEM_read_bio_PrivateKey(bp
, NULL
, NULL
, NULL
)))
611 return ERR_error_string(ERR_get_error(), NULL
);
617 /* allocate mem for signature (when signing) */
618 /* sign data (gnutls_only)
622 Return: NULL for success with the signaature in the sig blob, or an error string */
625 exim_dkim_sign(es_ctx
* sign_ctx
, hashmethod hash
, blob
* data
, blob
* sig
)
633 case HASH_SHA1
: md
= EVP_sha1(); break;
634 case HASH_SHA2_256
: md
= EVP_sha256(); break;
635 case HASH_SHA2_512
: md
= EVP_sha512(); break;
636 default: return US
"nonhandled hash type";
639 if ( (ctx
= EVP_PKEY_CTX_new(sign_ctx
->key
, NULL
))
640 && EVP_PKEY_sign_init(ctx
) > 0
641 && EVP_PKEY_CTX_set_rsa_padding(ctx
, RSA_PKCS1_PADDING
) > 0
642 && EVP_PKEY_CTX_set_signature_md(ctx
, md
) > 0
643 && EVP_PKEY_sign(ctx
, NULL
, &siglen
, data
->data
, data
->len
) > 0
646 /* Allocate mem for signature */
647 sig
->data
= store_get(siglen
);
649 if (EVP_PKEY_sign(ctx
, sig
->data
, &siglen
, data
->data
, data
->len
) > 0)
651 EVP_PKEY_CTX_free(ctx
);
657 if (ctx
) EVP_PKEY_CTX_free(ctx
);
658 return ERR_error_string(ERR_get_error(), NULL
);
663 /* import public key (from DER in memory)
664 Return: NULL for success, or an error string */
667 exim_dkim_verify_init(blob
* pubkey_der
, ev_ctx
* verify_ctx
)
669 const uschar
* s
= pubkey_der
->data
;
671 /*XXX hmm, we never free this */
673 if ((verify_ctx
->key
= d2i_PUBKEY(NULL
, &s
, pubkey_der
->len
)))
675 return ERR_error_string(ERR_get_error(), NULL
);
681 /* verify signature (of hash) (given pubkey & alleged sig)
682 Return: NULL for success, or an error string */
685 exim_dkim_verify(ev_ctx
* verify_ctx
, hashmethod hash
, blob
* data_hash
, blob
* sig
)
692 case HASH_SHA1
: md
= EVP_sha1(); break;
693 case HASH_SHA2_256
: md
= EVP_sha256(); break;
694 case HASH_SHA2_512
: md
= EVP_sha512(); break;
695 default: return US
"nonhandled hash type";
698 if ( (ctx
= EVP_PKEY_CTX_new(verify_ctx
->key
, NULL
))
699 && EVP_PKEY_verify_init(ctx
) > 0
700 && EVP_PKEY_CTX_set_rsa_padding(ctx
, RSA_PKCS1_PADDING
) > 0
701 && EVP_PKEY_CTX_set_signature_md(ctx
, md
) > 0
702 && EVP_PKEY_verify(ctx
, sig
->data
, sig
->len
,
703 data_hash
->data
, data_hash
->len
) == 1
705 { EVP_PKEY_CTX_free(ctx
); return NULL
; }
707 if (ctx
) EVP_PKEY_CTX_free(ctx
);
708 return ERR_error_string(ERR_get_error(), NULL
);
714 /******************************************************************************/
716 #endif /*DISABLE_DKIM*/