On Mon, May 08, 2023 at 02:01:52PM +0200, Theo Buehler wrote: > The diff below is based on a hint by beck and was discussed extensively > with beck, claudio and job during and after m2k23. It results in a quite > significant reduction of the runtime of an ordinary rpki-client run as > usually done from cron. > > One problem we're facing with the generally rather poor quality RFC 3779 > code in libcrypto is that its performance is abysmal. Flame graphs show > a lot of time spent in addr_contains(). While there is some room for > improvement in that function itself (the containment check for prefixes > could be optimized quite a bit), we can avoid a lot of the most expensive > checking of certificates with tons of resources close to the TA by using > the partial chains flag. > > More precisely, in the tree of already validated certs look for the first > one that has no inherited RFC 3779 resources and use that as the trust > anchor for our chains via the X509_V_FLAG_PARTIAL_CHAIN flag for the > verifier. This way we can be sure that a leaf's delegated resources are > properly covered and at the same time significantly shorten most paths > validated. There is no downside to doing this since we have already > validated the path from all certs in the auth tree to the root. > > In my testing, this avoids 30-50% of overhead and works equally well > with LibreSSL and OpenSSL 1.1. > > I currently hang any_inherits off struct auth. It may be worthwhile to > move that to struct cert and populate it in cert_parse_pre() in a later > step.
OK claudio@ > Index: cert.c > =================================================================== > RCS file: /cvs/src/usr.sbin/rpki-client/cert.c,v > retrieving revision 1.107 > diff -u -p -r1.107 cert.c > --- cert.c 15 Apr 2023 00:39:08 -0000 1.107 > +++ cert.c 8 May 2023 11:04:30 -0000 > @@ -1092,6 +1092,7 @@ auth_insert(struct auth_tree *auths, str > > na->parent = parent; > na->cert = cert; > + na->any_inherits = x509_any_inherits(cert->x509); > > if (RB_INSERT(auth_tree, auths, na) != NULL) > err(1, "auth tree corrupted"); > Index: extern.h > =================================================================== > RCS file: /cvs/src/usr.sbin/rpki-client/extern.h,v > retrieving revision 1.180 > diff -u -p -r1.180 extern.h > --- extern.h 27 Apr 2023 08:37:53 -0000 1.180 > +++ extern.h 8 May 2023 11:04:30 -0000 > @@ -454,6 +454,7 @@ struct auth { > RB_ENTRY(auth) entry; > struct cert *cert; /* owner information */ > struct auth *parent; /* pointer to parent or NULL for TA cert */ > + int any_inherits; > }; > /* > * Tree of auth sorted by ski > Index: validate.c > =================================================================== > RCS file: /cvs/src/usr.sbin/rpki-client/validate.c,v > retrieving revision 1.59 > diff -u -p -r1.59 validate.c > --- validate.c 27 Apr 2023 08:37:53 -0000 1.59 > +++ validate.c 8 May 2023 11:29:56 -0000 > @@ -332,25 +332,37 @@ valid_origin(const char *uri, const char > } > > /* > - * Walk the certificate tree to the root and build a certificate > - * chain from cert->x509. All certs in the tree are validated and > - * can be loaded as trusted stack into the validator. > + * Walk the tree of known valid CA certificates until we find a certificate > that > + * doesn't inherit. Build a chain of intermediates and use the non-inheriting > + * certificate as a trusted root by virtue of X509_V_FLAG_PARTIAL_CHAIN. The > + * RFC 3779 path validation needs a non-inheriting trust root to ensure that > + * all delegated resources are covered. > */ > static void > -build_chain(const struct auth *a, STACK_OF(X509) **chain) > +build_chain(const struct auth *a, STACK_OF(X509) **intermediates, > + STACK_OF(X509) **root) > { > - *chain = NULL; > + *intermediates = NULL; > + *root = NULL; > > if (a == NULL) > return; > > - if ((*chain = sk_X509_new_null()) == NULL) > + if ((*intermediates = sk_X509_new_null()) == NULL) > + err(1, "sk_X509_new_null"); > + if ((*root = sk_X509_new_null()) == NULL) > err(1, "sk_X509_new_null"); > for (; a != NULL; a = a->parent) { > assert(a->cert->x509 != NULL); > - if (!sk_X509_push(*chain, a->cert->x509)) > + if (!a->any_inherits) { > + if (!sk_X509_push(*root, a->cert->x509)) > + errx(1, "sk_X509_push"); > + break; > + } > + if (!sk_X509_push(*intermediates, a->cert->x509)) > errx(1, "sk_X509_push"); > } > + assert(sk_X509_num(*root) == 1); > } > > /* > @@ -381,13 +393,13 @@ valid_x509(char *file, X509_STORE_CTX *s > { > X509_VERIFY_PARAM *params; > ASN1_OBJECT *cp_oid; > - STACK_OF(X509) *chain; > + STACK_OF(X509) *intermediates, *root; > STACK_OF(X509_CRL) *crls = NULL; > unsigned long flags; > int error; > > *errstr = NULL; > - build_chain(a, &chain); > + build_chain(a, &intermediates, &root); > build_crls(crl, &crls); > > assert(store_ctx != NULL); > @@ -404,25 +416,33 @@ valid_x509(char *file, X509_STORE_CTX *s > X509_VERIFY_PARAM_set_time(params, evaluation_time); > > flags = X509_V_FLAG_CRL_CHECK; > + flags |= X509_V_FLAG_PARTIAL_CHAIN; > flags |= X509_V_FLAG_POLICY_CHECK; > flags |= X509_V_FLAG_EXPLICIT_POLICY; > flags |= X509_V_FLAG_INHIBIT_MAP; > X509_STORE_CTX_set_flags(store_ctx, flags); > X509_STORE_CTX_set_depth(store_ctx, MAX_CERT_DEPTH); > - X509_STORE_CTX_set0_trusted_stack(store_ctx, chain); > + /* > + * See the comment above build_chain() for details on what's happening > + * here. The nomenclature in this API is dubious and poorly documented. > + */ > + X509_STORE_CTX_set0_untrusted(store_ctx, intermediates); > + X509_STORE_CTX_set0_trusted_stack(store_ctx, root); > X509_STORE_CTX_set0_crls(store_ctx, crls); > > if (X509_verify_cert(store_ctx) <= 0) { > error = X509_STORE_CTX_get_error(store_ctx); > *errstr = X509_verify_cert_error_string(error); > X509_STORE_CTX_cleanup(store_ctx); > - sk_X509_free(chain); > + sk_X509_free(intermediates); > + sk_X509_free(root); > sk_X509_CRL_free(crls); > return 0; > } > > X509_STORE_CTX_cleanup(store_ctx); > - sk_X509_free(chain); > + sk_X509_free(intermediates); > + sk_X509_free(root); > sk_X509_CRL_free(crls); > return 1; > } > -- :wq Claudio