1
0
mirror of git://git.gnupg.org/gnupg.git synced 2024-10-29 19:48:43 +01:00

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
This commit is contained in:
Werner Koch 2024-09-30 18:22:25 +02:00
parent 9543b3567b
commit dcee2db36b
No known key found for this signature in database
GPG Key ID: AF99952165A3D8C5
4 changed files with 125 additions and 8 deletions

View File

@ -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; gpg_error_t err = 0;
char *issuer = NULL; char *issuer = NULL;
char *subject = NULL; char *subject = NULL;
KEYDB_HANDLE kh = keydb_new (); KEYDB_HANDLE kh = NULL;
cert_cache_item_t ci;
*r_next = NULL; *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); issuer = ksba_cert_get_issuer (start, 0);
subject = ksba_cert_get_subject (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; 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); err = find_up (ctrl, kh, start, issuer, 0);
if (err) 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", log_error ("keydb_get_cert() failed: %s <%s>\n",
gpg_strerror (err), gpg_strsource (err)); gpg_strerror (err), gpg_strsource (err));
err = gpg_error (GPG_ERR_GENERAL); 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: 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. */ /* 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); keydb_search_reset (kh);
rc = find_up (ctrl, kh, subject_cert, issuer, 0); rc = find_up (ctrl, kh, subject_cert, issuer, 0);
if (rc) if (rc)
@ -1846,6 +1899,26 @@ do_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t checktime_arg,
goto leave; 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: try_another_cert:
if (DBG_X509) if (DBG_X509)
{ {

View File

@ -469,6 +469,7 @@ static struct debug_flags_s debug_flags [] =
static struct compatibility_flags_s compatibility_flags [] = static struct compatibility_flags_s compatibility_flags [] =
{ {
{ COMPAT_ALLOW_KA_TO_ENCR, "allow-ka-to-encr" }, { COMPAT_ALLOW_KA_TO_ENCR, "allow-ka-to-encr" },
{ COMPAT_NO_CHAIN_CACHE, "no-chain-cache" },
{ 0, NULL } { 0, NULL }
}; };
@ -499,6 +500,9 @@ static int default_include_certs = DEFAULT_INCLUDE_CERTS;
/* Whether the chain mode shall be used for validation. */ /* Whether the chain mode shall be used for validation. */
static int default_validation_model; 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. */ /* The default cipher algo. */
#define DEFAULT_CIPHER_ALGO "AES256" #define DEFAULT_CIPHER_ALGO "AES256"
@ -2111,6 +2115,7 @@ main ( int argc, char **argv)
} }
/* cleanup */ /* cleanup */
gpgsm_deinit_default_ctrl (&ctrl);
free_strlist (opt.keyserver); free_strlist (opt.keyserver);
opt.keyserver = NULL; opt.keyserver = NULL;
gpgsm_release_certlist (recplist); gpgsm_release_certlist (recplist);
@ -2135,6 +2140,7 @@ gpgsm_exit (int rc)
gcry_control (GCRYCTL_UPDATE_RANDOM_SEED_FILE); gcry_control (GCRYCTL_UPDATE_RANDOM_SEED_FILE);
if (opt.debug & DBG_MEMSTAT_VALUE) 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_MEMORY_STATS );
gcry_control( GCRYCTL_DUMP_RANDOM_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 int
gpgsm_parse_validation_model (const char *model) gpgsm_parse_validation_model (const char *model)
{ {

View File

@ -183,11 +183,23 @@ struct
* policies: 1.3.6.1.4.1.7924.1.1:N: * policies: 1.3.6.1.4.1.7924.1.1:N:
*/ */
#define COMPAT_ALLOW_KA_TO_ENCR 1 #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 */ /* Forward declaration for an object defined in server.c */
struct server_local_s; 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 /* Session control object. This object is passed down to most
functions. Note that the default values for it are set by functions. Note that the default values for it are set by
gpgsm_init_default_ctrl(). */ gpgsm_init_default_ctrl(). */
@ -236,6 +248,9 @@ struct server_control_s
/* The current time. Used as a helper in certchain.c. */ /* The current time. Used as a helper in certchain.c. */
ksba_isotime_t current_time; 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_exit (int rc);
void gpgsm_init_default_ctrl (struct server_control_s *ctrl); 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); int gpgsm_parse_validation_model (const char *model);
/*-- server.c --*/ /*-- server.c --*/

View File

@ -1422,6 +1422,7 @@ gpgsm_server (certlist_t default_recplist)
audit_release (ctrl.audit); audit_release (ctrl.audit);
ctrl.audit = NULL; ctrl.audit = NULL;
gpgsm_deinit_default_ctrl (&ctrl);
assuan_release (ctx); assuan_release (ctx);
} }