aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWerner Koch <[email protected]>2024-09-30 16:22:25 +0000
committerWerner Koch <[email protected]>2024-09-30 17:05:41 +0000
commitdcee2db36ba49a689625f8c4381000bb6e82ea76 (patch)
tree35f1adb92665f9b708daf3643c2622007869da2c
parentsm: Optmize clearing of the ephemeral flag. (diff)
downloadgnupg-dcee2db36ba49a689625f8c4381000bb6e82ea76.tar.gz
gnupg-dcee2db36ba49a689625f8c4381000bb6e82ea76.zip
gpgsm: Use a cache to speed up parent certificate lookup.
* sm/gpgsm.h (COMPAT_NO_CHAIN_CACHE): New. (struct cert_cache_item_s, cert_cache_item_t): New. (struct server_control_s): Add parent_cert_cache. * sm/gpgsm.c (compatibility_flags): Add "no-chain-cache". (parent_cache_stats): New. (gpgsm_exit): Print the stats with --debug=memstat. (gpgsm_deinit_default_ctrl): Release the cache. * sm/certchain.c (gpgsm_walk_cert_chain): Cache the certificates. (do_validate_chain): Ditto. -- This gives another boost of 30% (from 6.5 to 4.0 seconds in the test environment with ~1000 certs). do_validate_chain actually brings us the speedup becuase the gpgsm_walk_cert_chain is not used during a key listing. For the latter we actually cache all certificates because that was easier. GnuPG-bug-id: 7308 Adjusted for 2.2: - Add gpgsm_deinit_default_ctrl - Remove ctrl arg from keydb_new
-rw-r--r--sm/certchain.c87
-rw-r--r--sm/gpgsm.c27
-rw-r--r--sm/gpgsm.h18
-rw-r--r--sm/server.c1
4 files changed, 125 insertions, 8 deletions
diff --git a/sm/certchain.c b/sm/certchain.c
index 8c25bdec1..334af8d2d 100644
--- a/sm/certchain.c
+++ b/sm/certchain.c
@@ -1051,15 +1051,10 @@ gpgsm_walk_cert_chain (ctrl_t ctrl, ksba_cert_t start, ksba_cert_t *r_next)
gpg_error_t err = 0;
char *issuer = NULL;
char *subject = NULL;
- KEYDB_HANDLE kh = keydb_new ();
+ KEYDB_HANDLE kh = NULL;
+ cert_cache_item_t ci;
*r_next = NULL;
- if (!kh)
- {
- log_error (_("failed to allocate keyDB handle\n"));
- err = gpg_error (GPG_ERR_GENERAL);
- goto leave;
- }
issuer = ksba_cert_get_issuer (start, 0);
subject = ksba_cert_get_subject (start, 0);
@@ -1082,6 +1077,30 @@ gpgsm_walk_cert_chain (ctrl_t ctrl, ksba_cert_t start, ksba_cert_t *r_next)
goto leave;
}
+ if (!(opt.compat_flags & COMPAT_NO_CHAIN_CACHE))
+ {
+ unsigned char fpr[20];
+
+ gpgsm_get_fingerprint (start, GCRY_MD_SHA1, fpr, NULL);
+ for (ci = ctrl->parent_cert_cache; ci; ci = ci->next)
+ {
+ if (!memcmp (fpr, ci->fpr, 20) && ci->result)
+ {
+ /* Found in the cache. */
+ ksba_cert_ref ((*r_next = ci->result));
+ goto leave;
+ }
+ }
+ }
+
+ kh = keydb_new ();
+ if (!kh)
+ {
+ log_error (_("failed to allocate keyDB handle\n"));
+ err = gpg_error (GPG_ERR_GENERAL);
+ goto leave;
+ }
+
err = find_up (ctrl, kh, start, issuer, 0);
if (err)
{
@@ -1100,6 +1119,22 @@ gpgsm_walk_cert_chain (ctrl_t ctrl, ksba_cert_t start, ksba_cert_t *r_next)
log_error ("keydb_get_cert() failed: %s <%s>\n",
gpg_strerror (err), gpg_strsource (err));
err = gpg_error (GPG_ERR_GENERAL);
+ goto leave;
+ }
+
+ /* Cache it. */
+ if (!(opt.compat_flags & COMPAT_NO_CHAIN_CACHE))
+ {
+ ci = xtrycalloc (1, sizeof *ci);
+ if (!ci)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ gpgsm_get_fingerprint (start, GCRY_MD_SHA1, ci->fpr, NULL);
+ ksba_cert_ref ((ci->result = *r_next));
+ ci->next = ctrl->parent_cert_cache;
+ ctrl->parent_cert_cache = ci;
}
leave:
@@ -1816,6 +1851,24 @@ do_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t checktime_arg,
}
/* Find the next cert up the tree. */
+ if (!(opt.compat_flags & COMPAT_NO_CHAIN_CACHE))
+ {
+ cert_cache_item_t ci;
+ unsigned char fpr[20];
+
+ gpgsm_get_fingerprint (subject_cert, GCRY_MD_SHA1, fpr, NULL);
+ for (ci = ctrl->parent_cert_cache; ci; ci = ci->next)
+ {
+ if (!memcmp (fpr, ci->fpr, 20) && ci->result)
+ {
+ /* Found in the cache. */
+ ksba_cert_release (issuer_cert);
+ ksba_cert_ref ((issuer_cert = ci->result));
+ goto found_in_cache;
+ }
+ }
+ }
+
keydb_search_reset (kh);
rc = find_up (ctrl, kh, subject_cert, issuer, 0);
if (rc)
@@ -1846,6 +1899,26 @@ do_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t checktime_arg,
goto leave;
}
+ /* Cache it. The chain->next is here so that the leaf
+ * certificates are not cached. */
+ if (!(opt.compat_flags & COMPAT_NO_CHAIN_CACHE) && chain->next)
+ {
+ cert_cache_item_t ci;
+
+ ci = xtrycalloc (1, sizeof *ci);
+ if (!ci)
+ {
+ rc = gpg_error_from_syserror ();
+ goto leave;
+ }
+ gpgsm_get_fingerprint (subject_cert, GCRY_MD_SHA1, ci->fpr, NULL);
+ ksba_cert_ref ((ci->result = issuer_cert));
+ ci->next = ctrl->parent_cert_cache;
+ ctrl->parent_cert_cache = ci;
+ }
+
+ found_in_cache:
+
try_another_cert:
if (DBG_X509)
{
diff --git a/sm/gpgsm.c b/sm/gpgsm.c
index 25fdfe57b..91a63bd13 100644
--- a/sm/gpgsm.c
+++ b/sm/gpgsm.c
@@ -469,6 +469,7 @@ static struct debug_flags_s debug_flags [] =
static struct compatibility_flags_s compatibility_flags [] =
{
{ COMPAT_ALLOW_KA_TO_ENCR, "allow-ka-to-encr" },
+ { COMPAT_NO_CHAIN_CACHE, "no-chain-cache" },
{ 0, NULL }
};
@@ -499,6 +500,9 @@ static int default_include_certs = DEFAULT_INCLUDE_CERTS;
/* Whether the chain mode shall be used for validation. */
static int default_validation_model;
+/* Counter used to convey data from deinit_ctrl to gpgsm_exit. */
+static unsigned int parent_cache_stats;
+
/* The default cipher algo. */
#define DEFAULT_CIPHER_ALGO "AES256"
@@ -2111,6 +2115,7 @@ main ( int argc, char **argv)
}
/* cleanup */
+ gpgsm_deinit_default_ctrl (&ctrl);
free_strlist (opt.keyserver);
opt.keyserver = NULL;
gpgsm_release_certlist (recplist);
@@ -2135,6 +2140,7 @@ gpgsm_exit (int rc)
gcry_control (GCRYCTL_UPDATE_RANDOM_SEED_FILE);
if (opt.debug & DBG_MEMSTAT_VALUE)
{
+ log_info ("cert_chain_cache: cached=%u\n", parent_cache_stats);
gcry_control( GCRYCTL_DUMP_MEMORY_STATS );
gcry_control( GCRYCTL_DUMP_RANDOM_STATS );
}
@@ -2156,6 +2162,27 @@ gpgsm_init_default_ctrl (struct server_control_s *ctrl)
}
+/* This function is called to deinitialize a control object. The
+ * control object is is not released, though. */
+void
+gpgsm_deinit_default_ctrl (ctrl_t ctrl)
+{
+ unsigned int n;
+
+ n = 0;
+ while (ctrl->parent_cert_cache)
+ {
+ cert_cache_item_t next = ctrl->parent_cert_cache->next;
+ ksba_cert_release (ctrl->parent_cert_cache->result);
+ xfree (ctrl->parent_cert_cache);
+ ctrl->parent_cert_cache = next;
+ n++;
+ }
+ if (n > parent_cache_stats)
+ parent_cache_stats = n;
+}
+
+
int
gpgsm_parse_validation_model (const char *model)
{
diff --git a/sm/gpgsm.h b/sm/gpgsm.h
index 3946b5679..b05918f67 100644
--- a/sm/gpgsm.h
+++ b/sm/gpgsm.h
@@ -183,11 +183,23 @@ struct
* policies: 1.3.6.1.4.1.7924.1.1:N:
*/
#define COMPAT_ALLOW_KA_TO_ENCR 1
-
+/* Not actually a compatibiliy flag but useful to limit the
+ * required memory for a validated key listing. */
+#define COMPAT_NO_CHAIN_CACHE 2
/* Forward declaration for an object defined in server.c */
struct server_local_s;
+/* On object used to keep a track of already known certificates. */
+struct cert_cache_item_s
+{
+ struct cert_cache_item_s *next;
+ unsigned char fpr[20]; /* The certificate's fingerprint. */
+ ksba_cert_t result; /* The resulting certificate (ie. the issuer). */
+};
+typedef struct cert_cache_item_s *cert_cache_item_t;
+
+
/* Session control object. This object is passed down to most
functions. Note that the default values for it are set by
gpgsm_init_default_ctrl(). */
@@ -236,6 +248,9 @@ struct server_control_s
/* The current time. Used as a helper in certchain.c. */
ksba_isotime_t current_time;
+
+ /* The cache used to find the parent cert. */
+ cert_cache_item_t parent_cert_cache;
};
@@ -271,6 +286,7 @@ extern int gpgsm_errors_seen;
void gpgsm_exit (int rc);
void gpgsm_init_default_ctrl (struct server_control_s *ctrl);
+void gpgsm_deinit_default_ctrl (ctrl_t ctrl);
int gpgsm_parse_validation_model (const char *model);
/*-- server.c --*/
diff --git a/sm/server.c b/sm/server.c
index 31abe3727..5afb7f150 100644
--- a/sm/server.c
+++ b/sm/server.c
@@ -1422,6 +1422,7 @@ gpgsm_server (certlist_t default_recplist)
audit_release (ctrl.audit);
ctrl.audit = NULL;
+ gpgsm_deinit_default_ctrl (&ctrl);
assuan_release (ctx);
}