Add zero-cost abstraction layer for CRT verification chain

When verifying an X.509 certificate, the current verification logic
maintains an instance of the internal mbedtls_x509_crt_verify_chain
structure representing the state of the verification process. This
instance references the list of certificates that comprise the chain
built so far together with their verification flags. This information
must be stored during verification because it's being passed to the
verification callback at the end of verification - if the user has
specified those.

If the user hasn't specified a verification callback, it is not
necessary to maintain the list of CRTs, and it is also not necessary
to maintain verification flags for each CRT individually, as they're
merged at the end of the verification process.

To allow a readable simplification of the code in case no verification
callbacks are used, this commit introduces a zero-cost abstraction layer
for the functionality that's required from the verification chain structure:
- init/reset
- add a new CRT to the chain
- get pointer to current CRT flags
- add flags to EE certificate
- get current chain length
- trigger callbacks and get final (merged) flags
This gives flexibility for re-implementing the verification chain
structure, e.g. in the case where no verification callbacks are
provided, and there's hence no need to store CRTs and flags
individually. This will be done in a later commit.
diff --git a/include/mbedtls/x509_crt.h b/include/mbedtls/x509_crt.h
index f0801df..e90f6a0 100644
--- a/include/mbedtls/x509_crt.h
+++ b/include/mbedtls/x509_crt.h
@@ -249,6 +249,9 @@
     /* for find_parent_in() */
     mbedtls_x509_crt *parent; /* non-null iff parent_in in progress */
 
+    /* current child CRT */
+    mbedtls_x509_crt *cur_crt;
+
 #if defined(MBEDTLS_HAVE_TIME_DATE)
     mbedtls_x509_crt *fallback_parent;
     int fallback_signature_is_good;
diff --git a/library/x509_crt.c b/library/x509_crt.c
index 730126b..a04e33c 100644
--- a/library/x509_crt.c
+++ b/library/x509_crt.c
@@ -670,23 +670,6 @@
 #endif /* !MBEDTLS_X509_REMOVE_HOSTNAME_VERIFICATION */
 
 /*
- * Reset (init or clear) a verify_chain
- */
-static void x509_crt_verify_chain_reset(
-    mbedtls_x509_crt_verify_chain *ver_chain )
-{
-    size_t i;
-
-    for( i = 0; i < MBEDTLS_X509_MAX_VERIFY_CHAIN_SIZE; i++ )
-    {
-        ver_chain->items[i].crt = NULL;
-        ver_chain->items[i].flags = (uint32_t) -1;
-    }
-
-    ver_chain->len = 0;
-}
-
-/*
  *  Version  ::=  INTEGER  {  v1(0), v2(1), v3(2)  }
  */
 static int x509_get_version( unsigned char **p,
@@ -3203,6 +3186,82 @@
 }
 
 /*
+ * Reset (init or clear) a verify_chain
+ */
+static void x509_crt_verify_chain_reset(
+    mbedtls_x509_crt_verify_chain *ver_chain )
+{
+    size_t i;
+
+    for( i = 0; i < MBEDTLS_X509_MAX_VERIFY_CHAIN_SIZE; i++ )
+    {
+        ver_chain->items[i].crt = NULL;
+        ver_chain->items[i].flags = (uint32_t) -1;
+    }
+
+    ver_chain->len = 0;
+}
+
+/*
+ * Merge the flags for all certs in the chain, after calling callback
+ */
+static int x509_crt_verify_chain_get_flags(
+           const mbedtls_x509_crt_verify_chain *ver_chain,
+           uint32_t *flags,
+           int (*f_vrfy)(void *, mbedtls_x509_crt *, int, uint32_t *),
+           void *p_vrfy )
+{
+    int ret;
+    unsigned i;
+    uint32_t cur_flags;
+    const mbedtls_x509_crt_verify_chain_item *cur;
+
+    for( i = ver_chain->len; i != 0; --i )
+    {
+        cur = &ver_chain->items[i-1];
+        cur_flags = cur->flags;
+
+        if( NULL != f_vrfy )
+            if( ( ret = f_vrfy( p_vrfy, cur->crt, (int) i-1, &cur_flags ) ) != 0 )
+                return( ret );
+
+        *flags |= cur_flags;
+    }
+
+    return( 0 );
+}
+
+static void x509_crt_verify_chain_add_ee_flags(
+    mbedtls_x509_crt_verify_chain *chain,
+    uint32_t ee_flags )
+{
+    chain->items[0].flags |= ee_flags;
+}
+
+static void x509_crt_verify_chain_add_crt(
+    mbedtls_x509_crt_verify_chain *chain,
+    mbedtls_x509_crt *crt )
+{
+    mbedtls_x509_crt_verify_chain_item *cur;
+    cur = &chain->items[chain->len];
+    cur->crt = crt;
+    cur->flags = 0;
+    chain->len++;
+}
+
+static uint32_t* x509_crt_verify_chain_get_cur_flags(
+    mbedtls_x509_crt_verify_chain *chain )
+{
+    return( &chain->items[chain->len - 1].flags );
+}
+
+static unsigned x509_crt_verify_chain_len(
+    mbedtls_x509_crt_verify_chain const *chain )
+{
+    return( chain->len );
+}
+
+/*
  * Build and verify a certificate chain
  *
  * Given a peer-provided list of certificates EE, C1, ..., Cn and
@@ -3254,7 +3313,6 @@
      * catch potential issues with jumping ahead when restarting */
     int ret;
     uint32_t *flags;
-    mbedtls_x509_crt_verify_chain_item *cur;
     mbedtls_x509_crt *child_crt;
     mbedtls_x509_crt *parent_crt;
     int parent_is_trusted;
@@ -3269,10 +3327,7 @@
         /* restore saved state */
         *ver_chain = rs_ctx->ver_chain; /* struct copy */
         self_cnt = rs_ctx->self_cnt;
-
-        /* restore derived state */
-        cur = &ver_chain->items[ver_chain->len - 1];
-        child_crt = cur->crt;
+        child_crt = rs_ctx->cur_crt;
 
         child_is_trusted = 0;
         goto find_parent;
@@ -3291,16 +3346,13 @@
         int self_issued;
 
         /* Add certificate to the verification chain */
-        cur = &ver_chain->items[ver_chain->len];
-        cur->crt = child_crt;
-        cur->flags = 0;
-        ver_chain->len++;
+        x509_crt_verify_chain_add_crt( ver_chain, child_crt );
 
 #if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE)
 find_parent:
 #endif
 
-        flags = &cur->flags;
+        flags = x509_crt_verify_chain_get_cur_flags( ver_chain );
 
         {
             mbedtls_x509_crt_sig_info child_sig;
@@ -3342,7 +3394,7 @@
                     *flags |= MBEDTLS_X509_BADCERT_BAD_PK;
 
                 /* Special case: EE certs that are locally trusted */
-                if( ver_chain->len == 1 && self_issued &&
+                if( x509_crt_verify_chain_len( ver_chain ) == 1 && self_issued &&
                     x509_crt_check_ee_locally_trusted( child, trust_ca ) == 0 )
                 {
                     mbedtls_x509_crt_frame_release( child_crt );
@@ -3364,7 +3416,8 @@
             ret = x509_crt_find_parent( &child_sig, child_crt->next,
                                         trust_ca, &parent_crt,
                                         &parent_is_trusted, &signature_is_good,
-                                        ver_chain->len - 1, self_cnt, rs_ctx );
+                                        x509_crt_verify_chain_len( ver_chain ) - 1,
+                                        self_cnt, rs_ctx );
 
             x509_crt_free_sig_info( &child_sig );
         }
@@ -3376,6 +3429,7 @@
             rs_ctx->in_progress = x509_crt_rs_find_parent;
             rs_ctx->self_cnt = self_cnt;
             rs_ctx->ver_chain = *ver_chain; /* struct copy */
+            rs_ctx->cur_crt = child_crt;
             return( ret );
         }
 #else
@@ -3392,13 +3446,14 @@
         /* Count intermediate self-issued (not necessarily self-signed) certs.
          * These can occur with some strategies for key rollover, see [SIRO],
          * and should be excluded from max_pathlen checks. */
-        if( ver_chain->len != 1 && self_issued )
+        if( x509_crt_verify_chain_len( ver_chain ) != 1 && self_issued )
             self_cnt++;
 
         /* path_cnt is 0 for the first intermediate CA,
          * and if parent is trusted it's not an intermediate CA */
         if( ! parent_is_trusted &&
-            ver_chain->len > MBEDTLS_X509_MAX_INTERMEDIATE_CA )
+            x509_crt_verify_chain_len( ver_chain ) >
+            MBEDTLS_X509_MAX_INTERMEDIATE_CA )
         {
             /* return immediately to avoid overflow the chain array */
             return( MBEDTLS_ERR_X509_FATAL_ERROR );
@@ -3553,35 +3608,6 @@
 #endif /* !MBEDTLS_X509_REMOVE_HOSTNAME_VERIFICATION */
 
 /*
- * Merge the flags for all certs in the chain, after calling callback
- */
-static int x509_crt_merge_flags_with_cb(
-           uint32_t *flags,
-           const mbedtls_x509_crt_verify_chain *ver_chain,
-           int (*f_vrfy)(void *, mbedtls_x509_crt *, int, uint32_t *),
-           void *p_vrfy )
-{
-    int ret;
-    unsigned i;
-    uint32_t cur_flags;
-    const mbedtls_x509_crt_verify_chain_item *cur;
-
-    for( i = ver_chain->len; i != 0; --i )
-    {
-        cur = &ver_chain->items[i-1];
-        cur_flags = cur->flags;
-
-        if( NULL != f_vrfy )
-            if( ( ret = f_vrfy( p_vrfy, cur->crt, (int) i-1, &cur_flags ) ) != 0 )
-                return( ret );
-
-        *flags |= cur_flags;
-    }
-
-    return( 0 );
-}
-
-/*
  * Verify the certificate validity (default profile, not restartable)
  */
 int mbedtls_x509_crt_verify( mbedtls_x509_crt *crt,
@@ -3714,13 +3740,13 @@
         goto exit;
 
     /* Merge end-entity flags */
-    ver_chain.items[0].flags |= ee_flags;
+    x509_crt_verify_chain_add_ee_flags( &ver_chain, ee_flags );
 
     /* Build final flags, calling callback on the way if any */
 #if !defined(MBEDTLS_X509_REMOVE_VERIFY_CALLBACK)
-    ret = x509_crt_merge_flags_with_cb( flags, &ver_chain, f_vrfy, p_vrfy );
+    ret = x509_crt_verify_chain_get_flags( &ver_chain, flags, f_vrfy, p_vrfy );
 #else
-    ret = x509_crt_merge_flags_with_cb( flags, &ver_chain, NULL, NULL );
+    ret = x509_crt_verify_chain_get_flags( &ver_chain, flags, NULL, NULL );
 #endif /* MBEDTLS_X509_REMOVE_VERIFY_CALLBACK */
 
 exit: