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.

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;
 }

Reply via email to