def verify_crl(cert, path, validation_context, use_deltas=True, cert_description=None, end_entity_name_override=None):
    """
    Verifies a certificate against a list of CRLs, checking to make sure the
    certificate has not been revoked. Uses the algorithm from
    https://tools.ietf.org/html/rfc5280#section-6.3 as a basis, but the
    implementation differs to allow CRLs from unrecorded locations.

    :param cert:
        An asn1cyrpto.x509.Certificate object to check for in the CRLs

    :param path:
        A certvalidator.path.ValidationPath object of the cert's validation path

    :param certificate_lists:
        A list of asn1crypto.crl.CertificateList objects

    :param validation_context:
        A certvalidator.context.ValidationContext object to use for caching
        validation information

    :param use_deltas:
        A boolean indicating if delta CRLs should be used

    :param cert_description:
        A unicode string containing a description of the certificate to be used
        in exception messages

    :param end_entity_name_override:
        None or a unicode string of the name to use for the end-entity
        certificate when including in exception messages

    :raises:
        certvalidator.errors.CRLNoMatchesError - when none of the CRLs match the certificate
        certvalidator.errors.CRLValidationError - when any error occurs trying to verify the CertificateList
        certvalidator.errors.RevokedError - when the CRL indicates the certificate has been revoked
    """

    if not isinstance(cert, x509.Certificate):
        raise TypeError(pretty_message(
            '''
            cert must be an instance of asn1crypto.x509.Certificate, not %s
            ''',
            type_name(cert)
        ))

    if not isinstance(path, ValidationPath):
        raise TypeError(pretty_message(
            '''
            path must be an instance of certvalidator.path.ValidationPath,
            not %s
            ''',
            type_name(path)
        ))

    if not isinstance(validation_context, ValidationContext):
        raise TypeError(pretty_message(
            '''
            validation_context must be an instance of
            certvalidator.context.ValidationContext, not %s
            ''',
            type_name(validation_context)
        ))

    if cert_description is None:
        cert_description = 'the certificate'

    if not isinstance(cert_description, str_cls):
        raise TypeError(pretty_message(
            '''
            cert_description must be a unicode string, not %s
            ''',
            type_name(cert_description)
        ))

    moment = validation_context.moment
    certificate_registry = validation_context.certificate_registry

    certificate_lists = validation_context.retrieve_crls(cert)

    cert_issuer = path.find_issuer(cert)

    complete_lists_by_issuer = {}
    delta_lists_by_issuer = {}
    for certificate_list in certificate_lists:
        issuer_hashable = certificate_list.issuer.hashable
        if certificate_list.delta_crl_indicator_value is None:
            if issuer_hashable not in complete_lists_by_issuer:
                complete_lists_by_issuer[issuer_hashable] = []
            complete_lists_by_issuer[issuer_hashable].append(certificate_list)
        else:
            if issuer_hashable not in delta_lists_by_issuer:
                delta_lists_by_issuer[issuer_hashable] = []
            delta_lists_by_issuer[issuer_hashable].append(certificate_list)

    # In the main loop, only complete CRLs are processed, so delta CRLs are
    # weeded out of the todo list
    crls_to_process = []
    for issuer_crls in complete_lists_by_issuer.values():
        crls_to_process.extend(issuer_crls)
    total_crls = len(crls_to_process)

    # Build a lookup table for the Distribution point objects associated with
    # an issuer name hashable
    distribution_point_map = {}
    sources = [cert.crl_distribution_points]
    if use_deltas:
        sources.extend(cert.delta_crl_distribution_points)
    for dp_list in sources:
        for distribution_point in dp_list:
            if isinstance(distribution_point['crl_issuer'], x509.GeneralNames):
                dp_name_hashes = []
                for general_name in distribution_point['crl_issuer']:
                    if general_name.name == 'directory_name':
                        dp_name_hashes.append(general_name.chosen.hashable)
            else:
                dp_name_hashes = [cert.issuer.hashable]
            for dp_name_hash in dp_name_hashes:
                if dp_name_hash not in distribution_point_map:
                    distribution_point_map[dp_name_hash] = []
                distribution_point_map[dp_name_hash].append(distribution_point)

    valid_reasons = set([
        'key_compromise',
        'ca_compromise',
        'affiliation_changed',
        'superseded',
        'cessation_of_operation',
        'certificate_hold',
        'privilege_withdrawn',
        'aa_compromise',
    ])

    known_extensions = set([
        'issuer_alt_name',
        'crl_number',
        'delta_crl_indicator',
        'issuing_distribution_point',
        'authority_key_identifier',
        'freshest_crl',
        'authority_information_access',
    ])

    checked_reasons = set()

    failures = []
    issuer_failures = 0

    while len(crls_to_process) > 0:
        certificate_list = crls_to_process.pop(0)
        crl_idp = certificate_list.issuing_distribution_point_value
        delta_certificate_list = None
        delta_crl_idp = None

        interim_reasons = set()

        crl_issuer = None
        crl_issuer_name = None
        is_indirect = False

        if crl_idp and crl_idp['indirect_crl'].native:
            is_indirect = True
            crl_idp_name = crl_idp['distribution_point']
            if crl_idp_name:
                if crl_idp_name.name == 'full_name':
                    crl_issuer_name = crl_idp_name.chosen[0].chosen
                else:
                    crl_issuer_name = cert_issuer.subject.copy().chosen.append(
                        crl_idp_name.chosen
                    )
            elif certificate_list.authority_key_identifier:
                tmp_crl_issuer = certificate_registry.retrieve_by_key_identifier(
                    certificate_list.authority_key_identifier
                )
                crl_issuer_name = tmp_crl_issuer.subject
            else:
                failures.append((
                    'CRL is marked as an indirect CRL, but provides no '
                    'mechanism for locating the CRL issuer certificate',
                    certificate_list
                ))
                continue
        else:
            crl_issuer_name = certificate_list.issuer

        if not crl_issuer:
            crl_issuer = validation_context.check_crl_issuer(certificate_list)

        if not crl_issuer:
            candidate_crl_issuers = certificate_registry.retrieve_by_name(crl_issuer_name, cert_issuer)
            candidates_skipped = 0
            signatures_failed = 0
            unauthorized_certs = 0

            if not candidate_crl_issuers and crl_issuer_name != certificate_list.issuer:
                candidate_crl_issuers = certificate_registry.retrieve_by_name(certificate_list.issuer, cert_issuer)

            for candidate_crl_issuer in candidate_crl_issuers:
                direct_issuer = candidate_crl_issuer.subject == cert_issuer.subject

                # In some cases an indirect CRL issuer is a certificate issued
                # by the certificate issuer. However, we need to ensure that
                # the candidate CRL issuer is not the certificate being checked,
                # otherwise we may be checking an incorrect CRL and produce
                # incorrect results.
                indirect_issuer = candidate_crl_issuer.issuer == cert_issuer.subject
                indirect_issuer = indirect_issuer and candidate_crl_issuer.sha256 != cert.sha256

                if not direct_issuer and not indirect_issuer and not is_indirect:
                    candidates_skipped += 1
                    continue

                # Step f
                candidate_crl_issuer_path = None

                if validation_context:
                    candidate_crl_issuer_path = validation_context.check_validation(candidate_crl_issuer)

                if candidate_crl_issuer_path is None:
                    candidate_crl_issuer_path = path.copy().truncate_to_issuer(candidate_crl_issuer)
                    candidate_crl_issuer_path.append(candidate_crl_issuer)
                    try:
                        # Pre-emptively mark a path as validated to prevent recursion
                        if validation_context:
                            validation_context.record_validation(candidate_crl_issuer, candidate_crl_issuer_path)

                        temp_override = end_entity_name_override
                        if temp_override is None and candidate_crl_issuer.sha256 != cert_issuer.sha256:
                            temp_override = cert_description + ' CRL issuer'
                        _validate_path(
                            validation_context,
                            candidate_crl_issuer_path,
                            end_entity_name_override=temp_override
                        )

                    except (PathValidationError) as e:
                        # If the validation did not work out, clear it
                        if validation_context:
                            validation_context.clear_validation(candidate_crl_issuer)

                        # We let a revoked error fall through since step k will catch
                        # it with a correct error message
                        if isinstance(e, RevokedError):
                            raise
                        raise CRLValidationError('CRL issuer certificate path could not be validated')

                key_usage_value = candidate_crl_issuer.key_usage_value
                if key_usage_value and 'crl_sign' not in key_usage_value.native:
                    unauthorized_certs += 1
                    continue

                try:
                    # Step g
                    _verify_signature(certificate_list, candidate_crl_issuer)

                    crl_issuer = candidate_crl_issuer
                    break

                except (CRLValidationError):
                    signatures_failed += 1
                    continue

            if crl_issuer is None:
                if candidates_skipped == len(candidate_crl_issuers):
                    issuer_failures += 1
                else:
                    if signatures_failed == len(candidate_crl_issuers):
                        failures.append((
                            'CRL signature could not be verified',
                            certificate_list
                        ))
                    elif unauthorized_certs == len(candidate_crl_issuers):
                        failures.append((
                            'The CRL issuer is not authorized to sign CRLs',
                            certificate_list
                        ))
                    else:
                        failures.append((
                            'Unable to locate CRL issuer certificate',
                            certificate_list
                        ))
                continue
            else:
                validation_context.record_crl_issuer(certificate_list, crl_issuer)

        # Step b 1
        has_dp_crl_issuer = False
        dp_match = False

        dps = cert.crl_distribution_points_value
        if dps:
            crl_issuer_general_name = x509.GeneralName(
                name='directory_name',
                value=crl_issuer.subject
            )
            for dp in dps:
                if dp['crl_issuer']:
                    has_dp_crl_issuer = True
                    if crl_issuer_general_name in dp['crl_issuer']:
                        dp_match = True

        same_issuer = crl_issuer.subject == cert_issuer.subject
        indirect_match = has_dp_crl_issuer and dp_match and is_indirect
        missing_idp = has_dp_crl_issuer and (not dp_match or not is_indirect)
        indirect_crl_issuer = crl_issuer.issuer == cert_issuer.subject

        if (not same_issuer and not indirect_match and not indirect_crl_issuer) or missing_idp:
            issuer_failures += 1
            continue

        # Check to make sure the CRL is valid for the moment specified
        if moment < certificate_list['tbs_cert_list']['this_update'].native:
            failures.append((
                'CRL is from after the validation time',
                certificate_list
            ))
            continue
        if moment > certificate_list['tbs_cert_list']['next_update'].native:
            failures.append((
                'CRL should have been regenerated by the validation time',
                certificate_list
            ))
            continue

        # Step b 2

        if crl_idp is not None:
            # Step b 2 i
            has_idp_name = False
            has_dp_name = False
            idp_dp_match = False

            idp_general_names = []
            idp_dp_name = crl_idp['distribution_point']
            if idp_dp_name:
                has_idp_name = True
                if idp_dp_name.name == 'full_name':
                    for general_name in idp_dp_name.chosen:
                        idp_general_names.append(general_name)
                else:
                    inner_extended_issuer_name = crl_issuer.subject.copy()
                    inner_extended_issuer_name.chosen.append(idp_dp_name.chosen.untag())
                    idp_general_names.append(x509.GeneralName(
                        name='directory_name',
                        value=inner_extended_issuer_name
                    ))

            dps = cert.crl_distribution_points_value
            if dps:
                for dp in dps:
                    if idp_dp_match:
                        break
                    dp_name = dp['distribution_point']
                    if dp_name:
                        has_dp_name = True
                        if dp_name.name == 'full_name':
                            for general_name in dp_name.chosen:
                                if general_name in idp_general_names:
                                    idp_dp_match = True
                                    break
                        else:
                            inner_extended_issuer_name = crl_issuer.subject.copy()
                            inner_extended_issuer_name.chosen.append(dp_name.chosen.untag())
                            dp_extended_issuer_name = x509.GeneralName(
                                name='directory_name',
                                value=inner_extended_issuer_name
                            )

                            if dp_extended_issuer_name in idp_general_names:
                                idp_dp_match = True

                    elif dp['crl_issuer']:
                        has_dp_name = True
                        for dp_crl_issuer_name in dp['crl_issuer']:
                            if dp_crl_issuer_name in idp_general_names:
                                idp_dp_match = True
                                break
            else:
                # If there is no DP, we consider the CRL issuer name to be it
                has_dp_name = True
                general_name = x509.GeneralName(
                    name='directory_name',
                    value=crl_issuer_name
                )
                if general_name in idp_general_names:
                    idp_dp_match = True

            idp_dp_match_failed = has_idp_name and has_dp_name and not idp_dp_match

            if idp_dp_match_failed:
                failures.append((
                    pretty_message(
                        '''
                        The CRL issuing distribution point extension does not
                        share any names with the certificate CRL distribution
                        point extension
                        '''
                    ),
                    certificate_list
                ))
                issuer_failures += 1
                continue

            # Step b 2 ii
            if crl_idp['only_contains_user_certs'].native:
                if cert.basic_constraints_value and cert.basic_constraints_value['ca'].native:
                    failures.append((
                        pretty_message(
                            '''
                            CRL only contains end-entity certificates and
                            certificate is a CA certificate
                            '''
                        ),
                        certificate_list
                    ))
                    continue

            # Step b 2 iii
            if crl_idp['only_contains_ca_certs'].native:
                if not cert.basic_constraints_value or cert.basic_constraints_value['ca'].native is False:
                    failures.append((
                        pretty_message(
                            '''
                            CRL only contains CA certificates and certificate
                            is an end-entity certificate
                            '''
                        ),
                        certificate_list
                    ))
                    continue

            # Step b 2 iv
            if crl_idp['only_contains_attribute_certs'].native:
                failures.append((
                    'CRL only contains attribute certificates',
                    certificate_list
                ))
                continue

        # Step c
        if use_deltas and certificate_list.freshest_crl_value and len(certificate_list.freshest_crl_value) > 0:
            for candidate_delta_cl in delta_lists_by_issuer.get(crl_issuer_name.hashable, []):

                # Step c 1
                if candidate_delta_cl.issuer != crl_issuer_name:
                    continue

                # Step c 2
                delta_crl_idp = candidate_delta_cl.issuing_distribution_point_value
                if (crl_idp is None and delta_crl_idp is not None) or (crl_idp is not None and delta_crl_idp is None):
                    continue

                if crl_idp and crl_idp.native != delta_crl_idp.native:
                    continue

                # Step c 3
                if certificate_list.authority_key_identifier != candidate_delta_cl.authority_key_identifier:
                    continue

                delta_certificate_list = candidate_delta_cl
                break

        # Step d
        idp_reasons = None

        if crl_idp and crl_idp['only_some_reasons'].native is not None:
            idp_reasons = crl_idp['only_some_reasons'].native

        reason_keys = None
        if idp_reasons:
            reason_keys = idp_reasons

        if reason_keys is None:
            interim_reasons = valid_reasons.copy()
        else:
            interim_reasons = reason_keys

        # Step e
        # We don't skip a CRL if it only contains reasons already checked since
        # a certificate issuer can self-issue a new cert that is used for CRLs

        if certificate_list.critical_extensions - known_extensions:
            failures.append((
                'One or more unrecognized critical extensions are present in '
                'the CRL',
                certificate_list
            ))
            continue

        if use_deltas and delta_certificate_list and delta_certificate_list.critical_extensions - known_extensions:
            failures.append((
                'One or more unrecognized critical extensions are present in '
                'the delta CRL',
                delta_certificate_list
            ))
            continue

        # Step h
        if use_deltas and delta_certificate_list:
            try:
                _verify_signature(delta_certificate_list, crl_issuer)
            except (CRLValidationError):
                failures.append((
                    'Delta CRL signature could not be verified',
                    certificate_list,
                    delta_certificate_list
                ))
                continue

            if moment < delta_certificate_list['tbs_cert_list']['this_update'].native:
                failures.append((
                    'Delta CRL is from after the validation time',
                    certificate_list,
                    delta_certificate_list
                ))
                continue
            if moment > delta_certificate_list['tbs_cert_list']['next_update'].native:
                failures.append((
                    'Delta CRL is from before the validation time',
                    certificate_list,
                    delta_certificate_list
                ))
                continue

        # Step i
        revoked_reason = None
        revoked_date = None

        if use_deltas and delta_certificate_list:
            try:
                revoked_date, revoked_reason = _find_cert_in_list(cert, cert_issuer, delta_certificate_list, crl_issuer)
            except (NotImplementedError):
                failures.append((
                    'One or more critical extensions are present in the CRL '
                    'entry for the certificate',
                    delta_certificate_list
                ))
                continue

        # Step j
        if revoked_reason is None:
            try:
                revoked_date, revoked_reason = _find_cert_in_list(cert, cert_issuer, certificate_list, crl_issuer)
            except (NotImplementedError):
                failures.append((
                    'One or more critical extensions are present in the CRL '
                    'entry for the certificate',
                    certificate_list
                ))
                continue

        # Step k
        if revoked_reason and revoked_reason.native == 'remove_from_crl':
            revoked_reason = None
            revoked_date = None

        if revoked_reason:
            reason = revoked_reason.human_friendly
            date = revoked_date.native.strftime('%Y-%m-%d')
            time = revoked_date.native.strftime('%H:%M:%S')
            raise RevokedError(pretty_message(
                '''
                CRL indicates %s was revoked at %s on %s, due to %s
                ''',
                cert_description,
                time,
                date,
                reason
            ))

        # Step l
        checked_reasons |= interim_reasons

    # CRLs should not include this value, but at least one of the examples
    # from the NIST test suite does
    checked_reasons -= set(['unused'])

    if checked_reasons != valid_reasons:
        if total_crls == issuer_failures:
            raise CRLNoMatchesError(pretty_message(
                '''
                No CRLs were issued by the issuer of %s, or any indirect CRL
                issuer
                ''',
                cert_description
            ))

        if not failures:
            failures.append((
                'The available CRLs do not cover all revocation reasons',
            ))

        raise CRLValidationIndeterminateError(
            pretty_message(
                '''
                Unable to determine if %s is revoked due to insufficient
                information from known CRLs
                ''',
                cert_description
            ),
            failures
        )
Verifies a certificate against a list of CRLs, checking to make sure the
    certificate has not been revoked. Uses the algorithm from
    https://tools.ietf.org/html/rfc5280#section-6.3 as a basis, but the
    implementation differs to allow CRLs from unrecorded locations.

    :param cert:
        An asn1cyrpto.x509.Certificate object to check for in the CRLs

    :param path:
        A certvalidator.path.ValidationPath object of the cert's validation path

    :param certificate_lists:
        A list of asn1crypto.crl.CertificateList objects

    :param validation_context:
        A certvalidator.context.ValidationContext object to use for caching
        validation information

    :param use_deltas:
        A boolean indicating if delta CRLs should be used

    :param cert_description:
        A unicode string containing a description of the certificate to be used
        in exception messages

    :param end_entity_name_override:
        None or a unicode string of the name to use for the end-entity
        certificate when including in exception messages

    :raises:
        certvalidator.errors.CRLNoMatchesError - when none of the CRLs match the certificate
        certvalidator.errors.CRLValidationError - when any error occurs trying to verify the CertificateList
        certvalidator.errors.RevokedError - when the CRL indicates the certificate has been revoked