/*
 * Process the handshake record.
 */
int do_svr_handshake(SSL *ssl, int handshake_type, uint8_t *buf, int hs_len)
{
    int ret = SSL_OK;
    ssl->hs_status = SSL_NOT_OK;            /* not connected */

    /* To get here the state must be valid */
    switch (handshake_type)
    {
        case HS_CLIENT_HELLO:
            if ((ret = process_client_hello(ssl)) == SSL_OK)
                ret = send_server_hello_sequence(ssl);
            break;

#ifdef CONFIG_SSL_CERT_VERIFICATION
        case HS_CERTIFICATE:/* the client sends its cert */
            ret = process_certificate(ssl, &ssl->x509_ctx);

            if (ret == SSL_OK)    /* verify the cert */
            { 
                int cert_res;
                int pathLenConstraint = 0;

                cert_res = x509_verify(ssl->ssl_ctx->ca_cert_ctx, 
                        ssl->x509_ctx, &pathLenConstraint);
                ret = (cert_res == 0) ? SSL_OK : SSL_X509_ERROR(cert_res);
            }
            break;

        case HS_CERT_VERIFY:    
            ret = process_cert_verify(ssl);
            add_packet(ssl, buf, hs_len);   /* needs to be done after */
            break;
#endif
        case HS_CLIENT_KEY_XCHG:
            ret = process_client_key_xchg(ssl);
            break;

        case HS_FINISHED:
            ret = process_finished(ssl, buf, hs_len);
            disposable_free(ssl);   /* free up some memory */
            break;
    }

    return ret;
}
Example #2
0
void x509_print(const X509_CTX *cert, CA_CERT_CTX *ca_cert_ctx)
{
    if (cert == NULL)
        return;

    printf("=== CERTIFICATE ISSUED TO ===\n");
    printf("Common Name (CN):\t\t");
    printf("%s\n", cert->cert_dn[X509_COMMON_NAME] ?
           cert->cert_dn[X509_COMMON_NAME] : not_part_of_cert);

    printf("Organization (O):\t\t");
    printf("%s\n", cert->cert_dn[X509_ORGANIZATION] ?
           cert->cert_dn[X509_ORGANIZATION] : not_part_of_cert);

    printf("Organizational Unit (OU):\t");
    printf("%s\n", cert->cert_dn[X509_ORGANIZATIONAL_UNIT] ?
           cert->cert_dn[X509_ORGANIZATIONAL_UNIT] : not_part_of_cert);

    printf("=== CERTIFICATE ISSUED BY ===\n");
    printf("Common Name (CN):\t\t");
    printf("%s\n", cert->ca_cert_dn[X509_COMMON_NAME] ?
           cert->ca_cert_dn[X509_COMMON_NAME] : not_part_of_cert);

    printf("Organization (O):\t\t");
    printf("%s\n", cert->ca_cert_dn[X509_ORGANIZATION] ?
           cert->ca_cert_dn[X509_ORGANIZATION] : not_part_of_cert);

    printf("Organizational Unit (OU):\t");
    printf("%s\n", cert->ca_cert_dn[X509_ORGANIZATIONAL_UNIT] ?
           cert->ca_cert_dn[X509_ORGANIZATIONAL_UNIT] : not_part_of_cert);

    printf("Not Before:\t\t\t%s", ctime(&cert->not_before));
    printf("Not After:\t\t\t%s", ctime(&cert->not_after));
    printf("RSA bitsize:\t\t\t%d\n", cert->rsa_ctx->num_octets*8);
    printf("Sig Type:\t\t\t");
    switch (cert->sig_type)
    {
    case SIG_TYPE_MD5:
        printf("MD5\n");
        break;
    case SIG_TYPE_SHA1:
        printf("SHA1\n");
        break;
    case SIG_TYPE_MD2:
        printf("MD2\n");
        break;
    default:
        printf("Unrecognized: %d\n", cert->sig_type);
        break;
    }

    if (ca_cert_ctx)
    {
        printf("Verify:\t\t\t\t%s\n",
               x509_display_error(x509_verify(ca_cert_ctx, cert)));
    }

#if 0
    print_blob("Signature", cert->signature, cert->sig_len);
    bi_print("Modulus", cert->rsa_ctx->m);
    bi_print("Pub Exp", cert->rsa_ctx->e);
#endif

    if (ca_cert_ctx)
    {
        x509_print(cert->next, ca_cert_ctx);
    }

    TTY_FLUSH();
}
Example #3
0
/**
 * Do some basic checks on the certificate chain.
 *
 * Certificate verification consists of a number of checks:
 * - The date of the certificate is after the start date.
 * - The date of the certificate is before the finish date.
 * - A root certificate exists in the certificate store.
 * - That the certificate(s) are not self-signed.
 * - The certificate chain is valid.
 * - The signature of the certificate is valid.
 */
int x509_verify(const CA_CERT_CTX *ca_cert_ctx, const X509_CTX *cert)
{
    int ret = X509_OK, i = 0;
    bigint *cert_sig;
    X509_CTX *next_cert = NULL;
    BI_CTX *ctx = NULL;
    bigint *mod = NULL, *expn = NULL;
    int match_ca_cert = 0;
    struct timeval tv;
    uint8_t is_self_signed = 0;

    if (cert == NULL)
    {
        ret = X509_VFY_ERROR_NO_TRUSTED_CERT;
        goto end_verify;
    }

    /* a self-signed certificate that is not in the CA store - use this
       to check the signature */
    if (asn1_compare_dn(cert->ca_cert_dn, cert->cert_dn) == 0)
    {
        is_self_signed = 1;
        ctx = cert->rsa_ctx->bi_ctx;
        mod = cert->rsa_ctx->m;
        expn = cert->rsa_ctx->e;
    }

    gettimeofday(&tv, NULL);

    /* check the not before date */
    if (tv.tv_sec < cert->not_before)
    {
        ret = X509_VFY_ERROR_NOT_YET_VALID;
        goto end_verify;
    }

    /* check the not after date */
    if (tv.tv_sec > cert->not_after)
    {
        ret = X509_VFY_ERROR_EXPIRED;
        goto end_verify;
    }

    next_cert = cert->next;

    /* last cert in the chain - look for a trusted cert */
    if (next_cert == NULL)
    {
        if (ca_cert_ctx != NULL)
        {
            /* go thu the CA store */
            while (i < CONFIG_X509_MAX_CA_CERTS && ca_cert_ctx->cert[i])
            {
                if (asn1_compare_dn(cert->ca_cert_dn,
                                    ca_cert_ctx->cert[i]->cert_dn) == 0)
                {
                    /* use this CA certificate for signature verification */
                    match_ca_cert = 1;
                    ctx = ca_cert_ctx->cert[i]->rsa_ctx->bi_ctx;
                    mod = ca_cert_ctx->cert[i]->rsa_ctx->m;
                    expn = ca_cert_ctx->cert[i]->rsa_ctx->e;
                    break;
                }

                i++;
            }
        }

        /* couldn't find a trusted cert (& let self-signed errors
           be returned) */
        if (!match_ca_cert && !is_self_signed)
        {
            ret = X509_VFY_ERROR_NO_TRUSTED_CERT;
            goto end_verify;
        }
    }
    else if (asn1_compare_dn(cert->ca_cert_dn, next_cert->cert_dn) != 0)
    {
        /* check the chain */
        ret = X509_VFY_ERROR_INVALID_CHAIN;
        goto end_verify;
    }
    else /* use the next certificate in the chain for signature verify */
    {
        ctx = next_cert->rsa_ctx->bi_ctx;
        mod = next_cert->rsa_ctx->m;
        expn = next_cert->rsa_ctx->e;
    }

    /* cert is self signed */
    if (!match_ca_cert && is_self_signed)
    {
        ret = X509_VFY_ERROR_SELF_SIGNED;
        goto end_verify;
    }

    /* check the signature */
    cert_sig = sig_verify(ctx, cert->signature, cert->sig_len,
                          bi_clone(ctx, mod), bi_clone(ctx, expn));

    if (cert_sig && cert->digest)
    {
        if (bi_compare(cert_sig, cert->digest) != 0)
            ret = X509_VFY_ERROR_BAD_SIGNATURE;


        bi_free(ctx, cert_sig);
    }
    else
    {
        ret = X509_VFY_ERROR_BAD_SIGNATURE;
    }

    if (ret)
        goto end_verify;

    /* go down the certificate chain using recursion. */
    if (next_cert != NULL)
    {
        ret = x509_verify(ca_cert_ctx, next_cert);
    }

end_verify:
    return ret;
}
Example #4
0
/**
 * Do some basic checks on the certificate chain.
 *
 * Certificate verification consists of a number of checks:
 * - The date of the certificate is after the start date.
 * - The date of the certificate is before the finish date.
 * - A root certificate exists in the certificate store.
 * - That the certificate(s) are not self-signed.
 * - The certificate chain is valid.
 * - The signature of the certificate is valid.
 */
int x509_verify(X509_CTX* ca_certs /* GBG: changed */, const X509_CTX *cert, const SSL_DateTime* now) 
{
    int ret = X509_OK;
    bigint *cert_sig;
    X509_CTX *next_cert = NULL;
    BI_CTX *ctx = NULL;
    bigint *mod = NULL, *expn = NULL;
    int match_ca_cert = 0;
    uint8_t is_self_signed = 0;
    SSL_DateTime now_dt;

    if (cert == NULL)
    {
        ret = X509_VFY_ERROR_NO_TRUSTED_CERT;       
        goto end_verify;
    }

    /* a self-signed certificate that is not in the CA store - use this 
       to check the signature */
    if (asn1_compare_dn(cert->ca_cert_dn, cert->cert_dn) == 0)
    {
        is_self_signed = 1;
        ctx = cert->rsa_ctx->bi_ctx;
        mod = cert->rsa_ctx->m;
        expn = cert->rsa_ctx->e;
    }

    /* if the time was not passed in, get it now */
    if (now == NULL) {
        SSL_DateTime_Now(&now_dt);
        now = &now_dt;
    }

    /* check the not before date */
    if (SSL_DateTime_Before(now, &cert->not_before))
    {
        ret = X509_VFY_ERROR_NOT_YET_VALID;
        goto end_verify;
    }

    /* check the not after date */
    if (SSL_DateTime_Before(&cert->not_after, now))
    {
        ret = X509_VFY_ERROR_EXPIRED;
        goto end_verify;
    }

    next_cert = cert->next;

    /* last cert in the chain - look for a trusted cert */
    if (next_cert == NULL)
    {
        /* GBG: modified */
        X509_CTX* ca_cert = ca_certs;
        while (ca_cert) {
            if (asn1_compare_dn(cert->ca_cert_dn, ca_cert->cert_dn) == 0) {
                /* use this CA certificate for signature verification */
                match_ca_cert = 1;
                ctx  = ca_cert->rsa_ctx->bi_ctx;
                mod  = ca_cert->rsa_ctx->m;
                expn = ca_cert->rsa_ctx->e;
                break;
            }
            ca_cert = ca_cert->next;
        }

       /* couldn't find a trusted cert (& let self-signed errors be returned) */
        if (!match_ca_cert && !is_self_signed)
        {
            ret = X509_VFY_ERROR_NO_TRUSTED_CERT;       
            goto end_verify;
        }
    }
    else if (asn1_compare_dn(cert->ca_cert_dn, next_cert->cert_dn) != 0)
    {
        /* check the chain */
        ret = X509_VFY_ERROR_INVALID_CHAIN;
        goto end_verify;
    }
    else /* use the next certificate in the chain for signature verify */
    {
        ctx = next_cert->rsa_ctx->bi_ctx;
        mod = next_cert->rsa_ctx->m;
        expn = next_cert->rsa_ctx->e;
    }

    /* cert is self signed */
    if (!match_ca_cert && is_self_signed)
    {
        ret = X509_VFY_ERROR_SELF_SIGNED;
        goto end_verify;
    }

    /* check the signature */
    cert_sig = sig_verify(ctx, cert->signature, cert->sig_len, 
                        bi_clone(ctx, mod), bi_clone(ctx, expn));

    if (cert_sig && cert->digest)
    {
        if (bi_compare(cert_sig, cert->digest) != 0)
            ret = X509_VFY_ERROR_BAD_SIGNATURE;


        bi_free(ctx, cert_sig);
    }
    else
    {
        ret = X509_VFY_ERROR_BAD_SIGNATURE;
    }

    if (ret)
        goto end_verify;

    /* go down the certificate chain using recursion. */
    if (next_cert != NULL)
    {
        ret = x509_verify(ca_certs, next_cert, now);
    }

end_verify:
    return ret;
}
Example #5
0
void x509_print(const X509_CTX *cert, CA_CERT_CTX *ca_cert_ctx) 
{
    if (cert == NULL)
        return;

    printf("=== CERTIFICATE ISSUED TO ===\n");
    printf("Common Name (CN):\t\t");
    printf("%s\n", cert->cert_dn[X509_COMMON_NAME] ?
                    cert->cert_dn[X509_COMMON_NAME] : not_part_of_cert);

    printf("Organization (O):\t\t");
    printf("%s\n", cert->cert_dn[X509_ORGANIZATION] ?
        cert->cert_dn[X509_ORGANIZATION] : not_part_of_cert);

    if (cert->cert_dn[X509_ORGANIZATIONAL_UNIT]) 
    {
        printf("Organizational Unit (OU):\t");
        printf("%s\n", cert->cert_dn[X509_ORGANIZATIONAL_UNIT]);
    }

    if (cert->cert_dn[X509_LOCATION]) 
    {
        printf("Location (L):\t\t\t");
        printf("%s\n", cert->cert_dn[X509_LOCATION]);
    }

    if (cert->cert_dn[X509_COUNTRY]) 
    {
        printf("Country (C):\t\t\t");
        printf("%s\n", cert->cert_dn[X509_COUNTRY]);
    }

    if (cert->cert_dn[X509_STATE]) 
    {
        printf("State (ST):\t\t\t");
        printf("%s\n", cert->cert_dn[X509_STATE]);
    }

    if (cert->basic_constraint_present)
    {
        printf("Basic Constraints:\t\t%sCA:%s, pathlen:%d\n",
                cert->basic_constraint_is_critical ? 
                    "critical, " : "",
                cert->basic_constraint_cA? "TRUE" : "FALSE",
                cert->basic_constraint_pathLenConstraint);
    }

    if (cert->key_usage_present)
    {
        printf("Key Usage:\t\t\t%s", cert->key_usage_is_critical ? 
                    "critical, " : "");
        bool has_started = false;

        if (IS_SET_KEY_USAGE_FLAG(cert, KEY_USAGE_DIGITAL_SIGNATURE))
        {
            printf("Digital Signature");
            has_started = true;
        }

        if (IS_SET_KEY_USAGE_FLAG(cert, KEY_USAGE_NON_REPUDIATION))
        {
            if (has_started)
                printf(", ");

            printf("Non Repudiation");
            has_started = true;
        }

        if (IS_SET_KEY_USAGE_FLAG(cert, KEY_USAGE_KEY_ENCIPHERMENT))
        {
            if (has_started)
                printf(", ");

            printf("Key Encipherment");
            has_started = true;
        }
        
        if (IS_SET_KEY_USAGE_FLAG(cert, KEY_USAGE_DATA_ENCIPHERMENT))
        {
            if (has_started)
                printf(", ");

            printf("Data Encipherment");
            has_started = true;
        }

        if (IS_SET_KEY_USAGE_FLAG(cert, KEY_USAGE_KEY_AGREEMENT))
        {
            if (has_started)
                printf(", ");

            printf("Key Agreement");
            has_started = true;
        }

        if (IS_SET_KEY_USAGE_FLAG(cert, KEY_USAGE_KEY_CERT_SIGN))
        {
            if (has_started)
                printf(", ");

            printf("Key Cert Sign");
            has_started = true;
        }

        if (IS_SET_KEY_USAGE_FLAG(cert, KEY_USAGE_CRL_SIGN))
        {
            if (has_started)
                printf(", ");

            printf("CRL Sign");
            has_started = true;
        }
       
        if (IS_SET_KEY_USAGE_FLAG(cert, KEY_USAGE_ENCIPHER_ONLY))
        {
            if (has_started)
                printf(", ");

            printf("Encipher Only");
            has_started = true;
        }

        if (IS_SET_KEY_USAGE_FLAG(cert, KEY_USAGE_DECIPHER_ONLY))
        {
            if (has_started)
                printf(", ");

            printf("Decipher Only");
            has_started = true;
        }

        printf("\n");
    }

    if (cert->subject_alt_name_present)
    {
        printf("Subject Alt Name:\t\t%s", cert->subject_alt_name_is_critical 
                ?  "critical, " : "");
        if (cert->subject_alt_dnsnames)
        {
            int i = 0;

            while (cert->subject_alt_dnsnames[i])
                printf("%s ", cert->subject_alt_dnsnames[i++]);
        }
        printf("\n");

    }

    printf("=== CERTIFICATE ISSUED BY ===\n");
    printf("Common Name (CN):\t\t");
    printf("%s\n", cert->ca_cert_dn[X509_COMMON_NAME] ?
                    cert->ca_cert_dn[X509_COMMON_NAME] : not_part_of_cert);

    printf("Organization (O):\t\t");
    printf("%s\n", cert->ca_cert_dn[X509_ORGANIZATION] ?
        cert->ca_cert_dn[X509_ORGANIZATION] : not_part_of_cert);

    if (cert->ca_cert_dn[X509_ORGANIZATIONAL_UNIT]) 
    {
        printf("Organizational Unit (OU):\t");
        printf("%s\n", cert->ca_cert_dn[X509_ORGANIZATIONAL_UNIT]);
    }

    if (cert->ca_cert_dn[X509_LOCATION]) 
    {
        printf("Location (L):\t\t\t");
        printf("%s\n", cert->ca_cert_dn[X509_LOCATION]);
    }

    if (cert->ca_cert_dn[X509_COUNTRY]) 
    {
        printf("Country (C):\t\t\t");
        printf("%s\n", cert->ca_cert_dn[X509_COUNTRY]);
    }

    if (cert->ca_cert_dn[X509_STATE]) 
    {
        printf("State (ST):\t\t\t");
        printf("%s\n", cert->ca_cert_dn[X509_STATE]);
    }

    printf("Not Before:\t\t\t%s", ctime(&cert->not_before));
    printf("Not After:\t\t\t%s", ctime(&cert->not_after));
    printf("RSA bitsize:\t\t\t%d\n", cert->rsa_ctx->num_octets*8);
    printf("Sig Type:\t\t\t");
    switch (cert->sig_type)
    {
        case SIG_TYPE_MD5:
            printf("MD5\n");
            break;
        case SIG_TYPE_SHA1:
            printf("SHA1\n");
            break;
        case SIG_TYPE_SHA256:
            printf("SHA256\n");
            break;
        case SIG_TYPE_SHA384:
            printf("SHA384\n");
            break;
        case SIG_TYPE_SHA512:
            printf("SHA512\n");
            break;
        default:
            printf("Unrecognized: %d\n", cert->sig_type);
            break;
    }

    if (ca_cert_ctx)
    {
        int pathLenConstraint = 0;
        char buff[64];
        printf("Verify:\t\t\t\t%s\n",
                x509_display_error(x509_verify(ca_cert_ctx, cert,
                        &pathLenConstraint), buff));
    }

#if 0
    print_blob("Signature", cert->signature, cert->sig_len);
    bi_print("Modulus", cert->rsa_ctx->m);
    bi_print("Pub Exp", cert->rsa_ctx->e);
#endif

    if (ca_cert_ctx)
    {
        x509_print(cert->next, ca_cert_ctx);
    }

    TTY_FLUSH();
}
Example #6
0
/**
 * Do some basic checks on the certificate chain.
 *
 * Certificate verification consists of a number of checks:
 * - The date of the certificate is after the start date.
 * - The date of the certificate is before the finish date.
 * - A root certificate exists in the certificate store.
 * - That the certificate(s) are not self-signed.
 * - The certificate chain is valid.
 * - The signature of the certificate is valid.
 * - Basic constraints 
 */
int x509_verify(const CA_CERT_CTX *ca_cert_ctx, const X509_CTX *cert, 
        int *pathLenConstraint) 
{
    int ret = X509_OK, i = 0;
    bigint *cert_sig;
    X509_CTX *next_cert = NULL;
    BI_CTX *ctx = NULL;
    bigint *mod = NULL, *expn = NULL;
    int match_ca_cert = 0;
    struct timeval tv;
    uint8_t is_self_signed = 0;

    if (cert == NULL)
    {
        ret = X509_VFY_ERROR_NO_TRUSTED_CERT;       
        goto end_verify;
    }

    /* a self-signed certificate that is not in the CA store - use this 
       to check the signature */
    if (asn1_compare_dn(cert->ca_cert_dn, cert->cert_dn) == 0)
    {
        is_self_signed = 1;
        ctx = cert->rsa_ctx->bi_ctx;
        mod = cert->rsa_ctx->m;
        expn = cert->rsa_ctx->e;
    }

    gettimeofday(&tv, NULL);

    /* check the not before date */
    if (tv.tv_sec < cert->not_before)
    {
        ret = X509_VFY_ERROR_NOT_YET_VALID;
        goto end_verify;
    }

    /* check the not after date */
    if (tv.tv_sec > cert->not_after)
    {
        ret = X509_VFY_ERROR_EXPIRED;
        goto end_verify;
    }

    if (cert->basic_constraint_present)
    {
        /* If the cA boolean is not asserted,
           then the keyCertSign bit in the key usage extension MUST NOT be
           asserted. */
        if (!cert->basic_constraint_cA &&
                IS_SET_KEY_USAGE_FLAG(cert, KEY_USAGE_KEY_CERT_SIGN))
        {
            ret = X509_VFY_ERROR_BASIC_CONSTRAINT;
            goto end_verify;
        }

        /* The pathLenConstraint field is meaningful only if the cA boolean is
           asserted and the key usage extension, if present, asserts the
           keyCertSign bit.  In this case, it gives the maximum number of 
           non-self-issued intermediate certificates that may follow this 
           certificate in a valid certification path. */
        if (cert->basic_constraint_cA &&
            (!cert->key_usage_present || 
                IS_SET_KEY_USAGE_FLAG(cert, KEY_USAGE_KEY_CERT_SIGN)) &&
            (cert->basic_constraint_pathLenConstraint+1) < *pathLenConstraint)
        {
            ret = X509_VFY_ERROR_BASIC_CONSTRAINT;
            goto end_verify;
        }
    }

    next_cert = cert->next;

    /* last cert in the chain - look for a trusted cert */
    if (next_cert == NULL)
    {
       if (ca_cert_ctx != NULL) 
       {
            /* go thru the CA store */
            while (i < CONFIG_X509_MAX_CA_CERTS && ca_cert_ctx->cert[i])
            {
                /* the extension is present but the cA boolean is not 
                   asserted, then the certified public key MUST NOT be used 
                   to verify certificate signatures. */
                if (cert->basic_constraint_present && 
                        !ca_cert_ctx->cert[i]->basic_constraint_cA)
                    continue;
                        
                if (asn1_compare_dn(cert->ca_cert_dn,
                                            ca_cert_ctx->cert[i]->cert_dn) == 0)
                {
                    /* use this CA certificate for signature verification */
                    match_ca_cert = true;
                    ctx = ca_cert_ctx->cert[i]->rsa_ctx->bi_ctx;
                    mod = ca_cert_ctx->cert[i]->rsa_ctx->m;
                    expn = ca_cert_ctx->cert[i]->rsa_ctx->e;


                    break;
                }

                i++;
            }
        }

        /* couldn't find a trusted cert (& let self-signed errors 
           be returned) */
        if (!match_ca_cert && !is_self_signed)
        {
            ret = X509_VFY_ERROR_NO_TRUSTED_CERT;       
            goto end_verify;
        }
    }
    else if (asn1_compare_dn(cert->ca_cert_dn, next_cert->cert_dn) != 0)
    {
        /* check the chain */
        ret = X509_VFY_ERROR_INVALID_CHAIN;
        goto end_verify;
    }
    else /* use the next certificate in the chain for signature verify */
    {
        ctx = next_cert->rsa_ctx->bi_ctx;
        mod = next_cert->rsa_ctx->m;
        expn = next_cert->rsa_ctx->e;
    }

    /* cert is self signed */
    if (!match_ca_cert && is_self_signed)
    {
        ret = X509_VFY_ERROR_SELF_SIGNED;
        goto end_verify;
    }

    /* check the signature */
    cert_sig = sig_verify(ctx, cert->signature, cert->sig_len, 
                        bi_clone(ctx, mod), bi_clone(ctx, expn));

    if (cert_sig && cert->digest)
    {
        if (bi_compare(cert_sig, cert->digest) != 0)
            ret = X509_VFY_ERROR_BAD_SIGNATURE;


        bi_free(ctx, cert_sig);
    }
    else
    {
        ret = X509_VFY_ERROR_BAD_SIGNATURE;
    }

    bi_clear_cache(ctx);

    if (ret)
        goto end_verify;

    /* go down the certificate chain using recursion. */
    if (next_cert != NULL)
    {
        (*pathLenConstraint)++; /* don't include last certificate */
        ret = x509_verify(ca_cert_ctx, next_cert, pathLenConstraint);
    }

end_verify:
    return ret;
}