Try to convert an ``ssl_options`` dictionary to an `~ssl.SSLContext` object. The ``ssl_options`` dictionary contains keywords to be passed to `ssl.wrap_socket`. In Python 2.7.9+, `ssl.SSLContext` objects can be used instead. This function converts the dict form to its `~ssl.SS
(
ssl_options: Union[Dict[str, Any], ssl.SSLContext],
server_side: Optional[bool] = None,
)
| 587 | |
| 588 | |
| 589 | def ssl_options_to_context( |
| 590 | ssl_options: Union[Dict[str, Any], ssl.SSLContext], |
| 591 | server_side: Optional[bool] = None, |
| 592 | ) -> ssl.SSLContext: |
| 593 | """Try to convert an ``ssl_options`` dictionary to an |
| 594 | `~ssl.SSLContext` object. |
| 595 | |
| 596 | The ``ssl_options`` dictionary contains keywords to be passed to |
| 597 | `ssl.wrap_socket`. In Python 2.7.9+, `ssl.SSLContext` objects can |
| 598 | be used instead. This function converts the dict form to its |
| 599 | `~ssl.SSLContext` equivalent, and may be used when a component which |
| 600 | accepts both forms needs to upgrade to the `~ssl.SSLContext` version |
| 601 | to use features like SNI or NPN. |
| 602 | |
| 603 | .. versionchanged:: 6.2 |
| 604 | |
| 605 | Added server_side argument. Omitting this argument will |
| 606 | result in a DeprecationWarning on Python 3.10. |
| 607 | |
| 608 | """ |
| 609 | if isinstance(ssl_options, ssl.SSLContext): |
| 610 | return ssl_options |
| 611 | assert isinstance(ssl_options, dict) |
| 612 | assert all(k in _SSL_CONTEXT_KEYWORDS for k in ssl_options), ssl_options |
| 613 | # TODO: Now that we have the server_side argument, can we switch to |
| 614 | # create_default_context or would that change behavior? |
| 615 | default_version = ssl.PROTOCOL_TLS |
| 616 | if server_side: |
| 617 | default_version = ssl.PROTOCOL_TLS_SERVER |
| 618 | elif server_side is not None: |
| 619 | default_version = ssl.PROTOCOL_TLS_CLIENT |
| 620 | context = ssl.SSLContext(ssl_options.get("ssl_version", default_version)) |
| 621 | if "certfile" in ssl_options: |
| 622 | context.load_cert_chain( |
| 623 | ssl_options["certfile"], ssl_options.get("keyfile", None) |
| 624 | ) |
| 625 | if "cert_reqs" in ssl_options: |
| 626 | if ssl_options["cert_reqs"] == ssl.CERT_NONE: |
| 627 | # This may have been set automatically by PROTOCOL_TLS_CLIENT but is |
| 628 | # incompatible with CERT_NONE so we must manually clear it. |
| 629 | context.check_hostname = False |
| 630 | context.verify_mode = ssl_options["cert_reqs"] |
| 631 | if "ca_certs" in ssl_options: |
| 632 | context.load_verify_locations(ssl_options["ca_certs"]) |
| 633 | if "ciphers" in ssl_options: |
| 634 | context.set_ciphers(ssl_options["ciphers"]) |
| 635 | if hasattr(ssl, "OP_NO_COMPRESSION"): |
| 636 | # Disable TLS compression to avoid CRIME and related attacks. |
| 637 | # This constant depends on openssl version 1.0. |
| 638 | # TODO: Do we need to do this ourselves or can we trust |
| 639 | # the defaults? |
| 640 | context.options |= ssl.OP_NO_COMPRESSION |
| 641 | return context |
| 642 | |
| 643 | |
| 644 | def ssl_wrap_socket( |