diff options
Diffstat (limited to 'dirmngr/ocsp.c')
-rw-r--r-- | dirmngr/ocsp.c | 153 |
1 files changed, 104 insertions, 49 deletions
diff --git a/dirmngr/ocsp.c b/dirmngr/ocsp.c index 3483ab9..5462449 100644 --- a/dirmngr/ocsp.c +++ b/dirmngr/ocsp.c @@ -224,7 +224,7 @@ do_ocsp_request (ctrl_t ctrl, ksba_ocsp_t ocsp, case 301: case 302: { - const char *s = http_get_header (http, "Location"); + const char *s = http_get_header (http, "Location", 0); log_info (_("URL '%s' redirected to '%s' (%u)\n"), url, s?s:"[none]", http_get_status_code (http)); @@ -406,32 +406,115 @@ validate_responder_cert (ctrl_t ctrl, ksba_cert_t cert, } -/* Helper for check_signature. */ -static int +/* Helper for check_signature. MD is the finalized hash context. */ +static gpg_error_t check_signature_core (ctrl_t ctrl, ksba_cert_t cert, gcry_sexp_t s_sig, - gcry_sexp_t s_hash, fingerprint_list_t signer_fpr_list) + gcry_md_hd_t md, fingerprint_list_t signer_fpr_list) { gpg_error_t err; - ksba_sexp_t pubkey; gcry_sexp_t s_pkey = NULL; + gcry_sexp_t s_hash = NULL; + const char *s; + int mdalgo, mdlen; + + /* Get the public key as a gcrypt s-expression. */ + { + ksba_sexp_t pk = ksba_cert_get_public_key (cert); + if (!pk) + err = gpg_error (GPG_ERR_INV_OBJ); + else + { + err = canon_sexp_to_gcry (pk, &s_pkey); + xfree (pk); + } + if (err) + goto leave; + } + + mdalgo = gcry_md_get_algo (md); + mdlen = gcry_md_get_algo_dlen (mdalgo); + + if (pk_algo_from_sexp (s_pkey) == GCRY_PK_ECC) + { + unsigned int qbits0, qbits; + + qbits0 = gcry_pk_get_nbits (s_pkey); + qbits = qbits0 == 521? 512 : qbits0; + + if ((qbits%8)) + { + log_error ("ECDSA requires the hash length to be a" + " multiple of 8 bits\n"); + err = gpg_error (GPG_ERR_INTERNAL); + goto leave; + } + + /* Don't allow any Q smaller than 160 bits. */ + if (qbits < 160) + { + log_error (_("%s key uses an unsafe (%u bit) hash\n"), + "ECDSA", qbits0); + err = gpg_error (GPG_ERR_INTERNAL); + goto leave; + } + + /* Check if we're too short. */ + if (mdlen < qbits/8) + { + log_error (_("a %u bit hash is not valid for a %u bit %s key\n"), + (unsigned int)mdlen*8, + qbits0, + "ECDSA"); + if (mdlen < 20) + { + err = gpg_error (GPG_ERR_INTERNAL); + goto leave; + } + } + + /* Truncate. */ + if (mdlen > qbits/8) + mdlen = qbits/8; + + err = gcry_sexp_build (&s_hash, NULL, "(data(flags raw)(value %b))", + (int)mdlen, gcry_md_read (md, mdalgo)); + } + else if (mdalgo && (s = gcry_md_algo_name (mdalgo)) && strlen (s) < 16) + { + /* Assume RSA */ + char hashalgostr[16+1]; + int i; - pubkey = ksba_cert_get_public_key (cert); - if (!pubkey) - err = gpg_error (GPG_ERR_INV_OBJ); + for (i=0; s[i]; i++) + hashalgostr[i] = ascii_tolower (s[i]); + hashalgostr[i] = 0; + err = gcry_sexp_build (&s_hash, NULL, "(data(flags pkcs1)(hash %s %b))", + hashalgostr, + (int)mdlen, + gcry_md_read (md, mdalgo)); + } else - err = canon_sexp_to_gcry (pubkey, &s_pkey); - xfree (pubkey); - if (!err) - err = gcry_pk_verify (s_sig, s_hash, s_pkey); - if (!err) - err = validate_responder_cert (ctrl, cert, signer_fpr_list); - if (!err) + err = gpg_error (GPG_ERR_DIGEST_ALGO); + if (err) + { + log_error (_("creating S-expression failed: %s\n"), gcry_strerror (err)); + goto leave; + } + + if (DBG_CRYPTO) { - gcry_sexp_release (s_pkey); - return 0; /* Successfully verified the signature. */ + gcry_log_debugsxp ("sig ", s_sig); + gcry_log_debugsxp ("hash", s_hash); } - /* We simply ignore all errors. */ + err = gcry_pk_verify (s_sig, s_hash, s_pkey); + if (err) + goto leave; + + err = validate_responder_cert (ctrl, cert, signer_fpr_list); + + leave: + gcry_sexp_release (s_hash); gcry_sexp_release (s_pkey); return err; } @@ -449,35 +532,11 @@ check_signature (ctrl_t ctrl, fingerprint_list_t signer_fpr_list) { gpg_error_t err; - int algo, cert_idx; - gcry_sexp_t s_hash; + int cert_idx; ksba_cert_t cert; - const char *s; /* Create a suitable S-expression with the hash value of our response. */ gcry_md_final (md); - algo = gcry_md_get_algo (md); - s = gcry_md_algo_name (algo); - if (algo && s && strlen (s) < 16) - { - char hashalgostr[16+1]; - int i; - - for (i=0; s[i]; i++) - hashalgostr[i] = ascii_tolower (s[i]); - hashalgostr[i] = 0; - err = gcry_sexp_build (&s_hash, NULL, "(data(flags pkcs1)(hash %s %b))", - hashalgostr, - (int)gcry_md_get_algo_dlen (algo), - gcry_md_read (md, algo)); - } - else - err = gpg_error (GPG_ERR_DIGEST_ALGO); - if (err) - { - log_error (_("creating S-expression failed: %s\n"), gcry_strerror (err)); - return err; - } /* Get rid of old OCSP specific certificate references. */ release_ctrl_ocsp_certs (ctrl); @@ -492,13 +551,12 @@ check_signature (ctrl_t ctrl, cert = get_cert_local (ctrl, signer_fpr_list->hexfpr); if (cert) { - err = check_signature_core (ctrl, cert, s_sig, s_hash, + err = check_signature_core (ctrl, cert, s_sig, md, signer_fpr_list); ksba_cert_release (cert); cert = NULL; if (!err) { - gcry_sexp_release (s_hash); return 0; /* Successfully verified the signature. */ } } @@ -558,14 +616,12 @@ check_signature (ctrl_t ctrl, if (cert) { - err = check_signature_core (ctrl, cert, s_sig, s_hash, - signer_fpr_list); + err = check_signature_core (ctrl, cert, s_sig, md, signer_fpr_list); ksba_cert_release (cert); if (!err) { ksba_free (name); ksba_free (keyid); - gcry_sexp_release (s_hash); return 0; /* Successfully verified the signature. */ } log_error ("responder certificate "); @@ -583,7 +639,6 @@ check_signature (ctrl_t ctrl, ksba_free (keyid); } - gcry_sexp_release (s_hash); log_error (_("no suitable certificate found to verify the OCSP response\n")); return gpg_error (GPG_ERR_NO_PUBKEY); } |