1
0
mirror of git://git.gnupg.org/gnupg.git synced 2024-12-22 10:19:57 +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
This commit is contained in:
Werner Koch 2024-09-30 18:22:25 +02:00
parent cb6c506e4e
commit ce0580a599
No known key found for this signature in database
GPG Key ID: AF99952165A3D8C5
3 changed files with 114 additions and 8 deletions

View File

@ -1056,15 +1056,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 (ctrl); 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);
@ -1087,6 +1082,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 (ctrl);
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)
{ {
@ -1105,6 +1124,22 @@ gpgsm_walk_cert_chain (ctrl_t ctrl, ksba_cert_t start, ksba_cert_t *r_next)
log_error ("keydb_get_cert failed in %s: %s <%s>\n", log_error ("keydb_get_cert failed in %s: %s <%s>\n",
__func__, gpg_strerror (err), gpg_strsource (err)); __func__, 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:
@ -1852,6 +1887,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)
@ -1883,6 +1936,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

@ -500,6 +500,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 }
}; };
@ -536,6 +537,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"
@ -2354,6 +2358,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 );
} }
@ -2381,9 +2386,22 @@ gpgsm_init_default_ctrl (struct server_control_s *ctrl)
void void
gpgsm_deinit_default_ctrl (ctrl_t ctrl) gpgsm_deinit_default_ctrl (ctrl_t ctrl)
{ {
unsigned int n;
gpgsm_keydb_deinit_session_data (ctrl); gpgsm_keydb_deinit_session_data (ctrl);
xfree (ctrl->revocation_reason); xfree (ctrl->revocation_reason);
ctrl->revocation_reason = NULL; ctrl->revocation_reason = NULL;
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;
} }

View File

@ -220,7 +220,9 @@ 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;
@ -230,6 +232,16 @@ struct keydb_local_s;
typedef struct keydb_local_s *keydb_local_t; typedef struct keydb_local_s *keydb_local_t;
/* 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(). */
@ -284,6 +296,9 @@ struct server_control_s
/* The revocation info. Used as a helper inc ertchain.c */ /* The revocation info. Used as a helper inc ertchain.c */
gnupg_isotime_t revoked_at; gnupg_isotime_t revoked_at;
char *revocation_reason; char *revocation_reason;
/* The cache used to find the parent cert. */
cert_cache_item_t parent_cert_cache;
}; };