# Buggy code
def create_crl(
    path=None,
    text=False,
    signing_private_key=None,
    signing_private_key_passphrase=None,
    signing_cert=None,
    revoked=None,
    include_expired=False,
    days_valid=100,
    digest="",
):
    r"""
    Create a CRL

    :depends:   - PyOpenSSL Python module

    path:
        Path to write the CRL to.

    text:
        If ``True``, return the PEM text without writing to a file.
        Default ``False``.

    signing_private_key:
        A path or string of the private key in PEM format that will be used
        to sign the CRL. This is required.

    signing_private_key_passphrase:
        Passphrase to decrypt the private key.

    signing_cert:
        A certificate matching the private key that will be used to sign
        the CRL. This is required.

    revoked:
        A list of dicts containing all the certificates to revoke. Each dict
        represents one certificate. A dict must contain either the key
        ``serial_number`` with the value of the serial number to revoke, or
        ``certificate`` with either the PEM encoded text of the certificate,
        or a path to the certificate to revoke.

        The dict can optionally contain the ``revocation_date`` key. If this
        key is omitted the revocation date will be set to now. If should be a
        string in the format "%Y-%m-%d %H:%M:%S".

        The dict can also optionally contain the ``not_after`` key. This is
        redundant if the ``certificate`` key is included. If the
        ``Certificate`` key is not included, this can be used for the logic
        behind the ``include_expired`` parameter. If should be a string in
        the format "%Y-%m-%d %H:%M:%S".

        The dict can also optionally contain the ``reason`` key. This is the
        reason code for the revocation. Available choices are ``unspecified``,
        ``keyCompromise``, ``CACompromise``, ``affiliationChanged``,
        ``superseded``, ``cessationOfOperation`` and ``certificateHold``.

    include_expired:
        Include expired certificates in the CRL. Default is ``False``.

    days_valid:
        The number of days that the CRL should be valid. This sets the Next
        Update field in the CRL.

    digest:
        The digest to use for signing the CRL.
        This has no effect on versions of pyOpenSSL less than 0.14

    .. note

        At this time the pyOpenSSL library does not allow choosing a signing
        algorithm for CRLs See https://github.com/pyca/pyopenssl/issues/159

    CLI Example:

    .. code-block:: bash

        salt '*' x509.create_crl path=/etc/pki/mykey.key \
                signing_private_key=/etc/pki/ca.key \
                signing_cert=/etc/pki/ca.crt \
                revoked="{'compromized-web-key': {'certificate': '/etc/pki/certs/www1.crt', 'revocation_date': '2015-03-01 00:00:00'}}"
    """
    # pyOpenSSL is required for dealing with CSLs. Importing inside these
    # functions because Client operations like creating CRLs shouldn't require
    # pyOpenSSL Note due to current limitations in pyOpenSSL it is impossible
    # to specify a digest For signing the CRL. This will hopefully be fixed
    # soon: https://github.com/pyca/pyopenssl/pull/161
    if not HAS_OPENSSL:
        raise salt.exceptions.SaltInvocationError(
            "Could not load OpenSSL module, OpenSSL unavailable"
        )
    crl = OpenSSL.crypto.CRL()

    if revoked is None:
        revoked = []

    for rev_item in revoked:
        if "certificate" in rev_item:
            rev_cert = read_certificate(rev_item["certificate"])
            rev_item["serial_number"] = rev_cert["Serial Number"]
            rev_item["not_after"] = rev_cert["Not After"]

        serial_number = rev_item["serial_number"].replace(":", "")
        # OpenSSL bindings requires this to be a non-unicode string
        serial_number = salt.utils.stringutils.to_bytes(serial_number)

        if "not_after" in rev_item and not include_expired:
            not_after = datetime.datetime.strptime(
                rev_item["not_after"], "%Y-%m-%d %H:%M:%S"
            )
            if datetime.datetime.now() > not_after:
                continue

        if "revocation_date" not in rev_item:
            rev_item["revocation_date"] = datetime.datetime.now().strftime(
                "%Y-%m-%d %H:%M:%S"
            )

        rev_date = datetime.datetime.strptime(
            rev_item["revocation_date"], "%Y-%m-%d %H:%M:%S"
        )
        rev_date = rev_date.strftime("%Y%m%d%H%M%SZ")
        rev_date = salt.utils.stringutils.to_bytes(rev_date)

        rev = OpenSSL.crypto.Revoked()
        rev.set_serial(salt.utils.stringutils.to_bytes(serial_number))
        rev.set_rev_date(salt.utils.stringutils.to_bytes(rev_date))

        if "reason" in rev_item:
            # Same here for OpenSSL bindings and non-unicode strings
            reason = salt.utils.stringutils.to_str(rev_item["reason"])
            rev.set_reason(reason)

        crl.add_revoked(rev)

    signing_cert = _text_or_file(signing_cert)
    cert = OpenSSL.crypto.load_certificate(
        OpenSSL.crypto.FILETYPE_PEM, get_pem_entry(signing_cert, pem_type="CERTIFICATE")
    )
    signing_private_key = _get_private_key_obj(
        signing_private_key, passphrase=signing_private_key_passphrase
    ).as_pem(cipher=None)
    key = OpenSSL.crypto.load_privatekey(
        OpenSSL.crypto.FILETYPE_PEM, get_pem_entry(signing_private_key)
    )

    export_kwargs = {
        "cert": cert,
        "key": key,
        "type": OpenSSL.crypto.FILETYPE_PEM,
        "days": days_valid,
    }
    if digest:
        export_kwargs["digest"] = salt.utils.stringutils.to_bytes(digest)
    else:
        log.warning("No digest specified. The default md5 digest will be used.")

    try:
        crltext = crl.export(**export_kwargs)
    except (TypeError, ValueError):
        log.warning(
            "Error signing crl with specified digest. Are you using pyopenssl 0.15 or newer? The default md5 digest will be used."
        )
        export_kwargs.pop("digest", None)
        crltext = crl.export(**export_kwargs)

    if text:
        return crltext

    return write_pem(text=crltext, path=path, pem_type="X509 CRL")


# Traceback (TypeError)
An exception occurred in this state: Traceback (most recent call last):
File "/usr/lib/python3/dist-packages/salt/modules/x509.py", line 1014, in create_crl
crltext = crl.export(**export_kwargs)
File "/usr/lib/python3/dist-packages/OpenSSL/crypto.py", line 2230, in export
raise TypeError("digest must be provided")
TypeError: digest must be provided

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "/usr/lib/python3/dist-packages/salt/state.py", line 1933, in call
**cdata['kwargs'])
File "/usr/lib/python3/dist-packages/salt/loader.py", line 1951, in wrapper
return f(*args, **kwargs)
File "/usr/lib/python3/dist-packages/salt/states/x509.py", line 675, in crl_managed
signing_cert=signing_cert, revoked=revoked, days_valid=days_valid, digest=digest, include_expired=include_expired)
File "/usr/lib/python3/dist-packages/salt/modules/x509.py", line 1019, in create_crl
crltext = crl.export(**export_kwargs)
File "/usr/lib/python3/dist-packages/OpenSSL/crypto.py", line 2230, in export
raise TypeError("digest must be provided")
TypeError: digest must be provided

# Fixed code
def create_crl(
    path=None,
    text=False,
    signing_private_key=None,
    signing_private_key_passphrase=None,
    signing_cert=None,
    revoked=None,
    include_expired=False,
    days_valid=100,
    digest="",
):
    r"""
    Create a CRL

    :depends:   - PyOpenSSL Python module

    path:
        Path to write the CRL to.

    text:
        If ``True``, return the PEM text without writing to a file.
        Default ``False``.

    signing_private_key:
        A path or string of the private key in PEM format that will be used
        to sign the CRL. This is required.

    signing_private_key_passphrase:
        Passphrase to decrypt the private key.

    signing_cert:
        A certificate matching the private key that will be used to sign
        the CRL. This is required.

    revoked:
        A list of dicts containing all the certificates to revoke. Each dict
        represents one certificate. A dict must contain either the key
        ``serial_number`` with the value of the serial number to revoke, or
        ``certificate`` with either the PEM encoded text of the certificate,
        or a path to the certificate to revoke.

        The dict can optionally contain the ``revocation_date`` key. If this
        key is omitted the revocation date will be set to now. If should be a
        string in the format "%Y-%m-%d %H:%M:%S".

        The dict can also optionally contain the ``not_after`` key. This is
        redundant if the ``certificate`` key is included. If the
        ``Certificate`` key is not included, this can be used for the logic
        behind the ``include_expired`` parameter. If should be a string in
        the format "%Y-%m-%d %H:%M:%S".

        The dict can also optionally contain the ``reason`` key. This is the
        reason code for the revocation. Available choices are ``unspecified``,
        ``keyCompromise``, ``CACompromise``, ``affiliationChanged``,
        ``superseded``, ``cessationOfOperation`` and ``certificateHold``.

    include_expired:
        Include expired certificates in the CRL. Default is ``False``.

    days_valid:
        The number of days that the CRL should be valid. This sets the Next
        Update field in the CRL.

    digest:
        The digest to use for signing the CRL.
        This has no effect on versions of pyOpenSSL less than 0.14

    .. note

        At this time the pyOpenSSL library does not allow choosing a signing
        algorithm for CRLs See https://github.com/pyca/pyopenssl/issues/159

    CLI Example:

    .. code-block:: bash

        salt '*' x509.create_crl path=/etc/pki/mykey.key \
                signing_private_key=/etc/pki/ca.key \
                signing_cert=/etc/pki/ca.crt \
                revoked="{'compromized-web-key': {'certificate': '/etc/pki/certs/www1.crt', 'revocation_date': '2015-03-01 00:00:00'}}"
    """
    # pyOpenSSL is required for dealing with CSLs. Importing inside these
    # functions because Client operations like creating CRLs shouldn't require
    # pyOpenSSL Note due to current limitations in pyOpenSSL it is impossible
    # to specify a digest For signing the CRL. This will hopefully be fixed
    # soon: https://github.com/pyca/pyopenssl/pull/161
    if not HAS_OPENSSL:
        raise salt.exceptions.SaltInvocationError(
            "Could not load OpenSSL module, OpenSSL unavailable"
        )
    crl = OpenSSL.crypto.CRL()

    if revoked is None:
        revoked = []

    for rev_item in revoked:
        if "certificate" in rev_item:
            rev_cert = read_certificate(rev_item["certificate"])
            rev_item["serial_number"] = rev_cert["Serial Number"]
            rev_item["not_after"] = rev_cert["Not After"]

        serial_number = rev_item["serial_number"].replace(":", "")
        # OpenSSL bindings requires this to be a non-unicode string
        serial_number = salt.utils.stringutils.to_bytes(serial_number)

        if "not_after" in rev_item and not include_expired:
            not_after = datetime.datetime.strptime(
                rev_item["not_after"], "%Y-%m-%d %H:%M:%S"
            )
            if datetime.datetime.now() > not_after:
                continue

        if "revocation_date" not in rev_item:
            rev_item["revocation_date"] = datetime.datetime.now().strftime(
                "%Y-%m-%d %H:%M:%S"
            )

        rev_date = datetime.datetime.strptime(
            rev_item["revocation_date"], "%Y-%m-%d %H:%M:%S"
        )
        rev_date = rev_date.strftime("%Y%m%d%H%M%SZ")
        rev_date = salt.utils.stringutils.to_bytes(rev_date)

        rev = OpenSSL.crypto.Revoked()
        rev.set_serial(salt.utils.stringutils.to_bytes(serial_number))
        rev.set_rev_date(salt.utils.stringutils.to_bytes(rev_date))

        if "reason" in rev_item:
            # Same here for OpenSSL bindings and non-unicode strings
            reason = salt.utils.stringutils.to_bytes(rev_item["reason"])
            rev.set_reason(reason)

        crl.add_revoked(rev)

    signing_cert = _text_or_file(signing_cert)
    cert = OpenSSL.crypto.load_certificate(
        OpenSSL.crypto.FILETYPE_PEM, get_pem_entry(signing_cert, pem_type="CERTIFICATE")
    )
    signing_private_key = _get_private_key_obj(
        signing_private_key, passphrase=signing_private_key_passphrase
    ).as_pem(cipher=None)
    key = OpenSSL.crypto.load_privatekey(
        OpenSSL.crypto.FILETYPE_PEM, get_pem_entry(signing_private_key)
    )

    export_kwargs = {
        "cert": cert,
        "key": key,
        "type": OpenSSL.crypto.FILETYPE_PEM,
        "days": days_valid,
    }
    if digest:
        export_kwargs["digest"] = salt.utils.stringutils.to_bytes(digest)
    else:
        log.warning("No digest specified. The default md5 digest will be used.")

    try:
        crltext = crl.export(**export_kwargs)
    except (TypeError, ValueError):
        log.warning(
            "Error signing crl with specified digest. Are you using pyopenssl 0.15 or newer? The default md5 digest will be used."
        )
        export_kwargs.pop("digest", None)
        crltext = crl.export(**export_kwargs)

    if text:
        return crltext

    return write_pem(text=crltext, path=path, pem_type="X509 CRL")