-
Notifications
You must be signed in to change notification settings - Fork 41
openssl3 support #126
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
jmcrawford45
wants to merge
3
commits into
nabla-c0d3:release
Choose a base branch
from
jmcrawford45:feature/openssl3-support
base: release
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
openssl3 support #126
Changes from 2 commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,2 +1,40 @@ | ||
| __author__ = "Alban Diquet" | ||
| __version__ = "5.3.0" | ||
|
|
||
|
|
||
| def _detect_available_openssl_versions(): | ||
| """Detect which OpenSSL versions are available.""" | ||
| available_versions = [] | ||
|
|
||
| # Try legacy OpenSSL | ||
| try: | ||
| import nassl._nassl_legacy # noqa: F401 | ||
| available_versions.append("legacy") | ||
| except ImportError: | ||
| pass | ||
|
|
||
| # Try modern OpenSSL (1.1.1) | ||
| try: | ||
| import nassl._nassl # noqa: F401 | ||
| available_versions.append("modern") | ||
| except ImportError: | ||
| pass | ||
|
|
||
| # Try OpenSSL 3 | ||
| try: | ||
| import nassl._nassl3 # noqa: F401 | ||
| available_versions.append("openssl3") | ||
| except ImportError: | ||
| pass | ||
|
|
||
| return available_versions | ||
|
|
||
|
|
||
| def get_openssl_versions(): | ||
| """Get a list of available OpenSSL versions.""" | ||
| return _detect_available_openssl_versions() | ||
|
|
||
|
|
||
| def has_openssl3_support(): | ||
| """Check if OpenSSL 3 support is available.""" | ||
| return "openssl3" in _detect_available_openssl_versions() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| from typing import Any | ||
|
|
||
| SSL_CTX: Any | ||
| SSL: Any | ||
| BIO: Any | ||
| X509: Any | ||
| X509_STORE_CTX: Any | ||
| OCSP_RESPONSE: Any | ||
| OpenSSLError: Any | ||
| SslError: Any | ||
| WantReadError: Any | ||
| WantX509LookupError: Any | ||
| SSL_SESSION: Any |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,177 @@ | ||
| """SSL client implementation using OpenSSL 3.x.""" | ||
|
|
||
| import socket | ||
| from pathlib import Path | ||
| from typing import List, Any, Tuple, Optional | ||
|
|
||
| try: | ||
| from nassl import _nassl3 as nassl3_module | ||
| from nassl._nassl3 import WantReadError, OpenSSLError, WantX509LookupError | ||
| _OPENSSL3_AVAILABLE = True | ||
| except ImportError: | ||
| _OPENSSL3_AVAILABLE = False | ||
| nassl3_module = None # type: ignore | ||
|
|
||
| # Define enums locally to avoid circular imports - using same values as ssl_client.py | ||
| class OpenSslVersionEnum: | ||
| SSLV23 = 0 | ||
| SSLV2 = 1 | ||
| SSLV3 = 2 | ||
| TLSV1 = 3 | ||
| TLSV1_1 = 4 | ||
| TLSV1_2 = 5 | ||
| TLSV1_3 = 6 | ||
|
|
||
| class OpenSslVerifyEnum: | ||
| NONE = 0 | ||
| PEER = 1 | ||
|
|
||
| from nassl.ephemeral_key_info import EphemeralKeyInfo | ||
|
|
||
|
|
||
| class OpenSSL3UnavailableError(Exception): | ||
| """Raised when OpenSSL 3 functionality is requested but not available.""" | ||
| pass | ||
|
|
||
|
|
||
| class OpenSSL3SslClient: | ||
| """SSL client implementation using OpenSSL 3.x. | ||
|
|
||
| This class provides an interface to OpenSSL 3.x functionality through | ||
| the _nassl3 C extension. It offers similar functionality to the regular | ||
| SslClient but uses the newer OpenSSL 3 API. | ||
| """ | ||
|
|
||
| def __init__( | ||
| self, | ||
| ssl_version: OpenSslVersionEnum = OpenSslVersionEnum.TLSV1_2, | ||
| ssl_verify: OpenSslVerifyEnum = OpenSslVerifyEnum.NONE, | ||
| ssl_verify_locations: Optional[Path] = None, | ||
| client_certchain_file: Optional[Path] = None, | ||
| client_key_file: Optional[Path] = None, | ||
| client_key_type: int = 1, # PEM format | ||
| client_key_password: str = "", | ||
| ignore_client_authentication_requests: bool = False, | ||
| ) -> None: | ||
| """Initialize the OpenSSL 3 SSL client. | ||
|
|
||
| Args: | ||
| ssl_version: The SSL/TLS version to use | ||
| ssl_verify: SSL verification mode | ||
| ssl_verify_locations: Path to CA certificates for verification | ||
| client_certchain_file: Path to client certificate chain file | ||
| client_key_file: Path to client private key file | ||
| client_key_type: Type of the client key file (PEM or DER) | ||
| client_key_password: Password for the client key file | ||
| ignore_client_authentication_requests: Whether to ignore client auth requests | ||
|
|
||
| Raises: | ||
| OpenSSL3UnavailableError: If OpenSSL 3 support is not available | ||
| """ | ||
| if not _OPENSSL3_AVAILABLE: | ||
| raise OpenSSL3UnavailableError( | ||
| "OpenSSL 3 support is not available. Make sure the _nassl3 extension was built." | ||
| ) | ||
|
|
||
| # Store configuration | ||
| self._ssl_version = ssl_version | ||
| self._ssl_verify = ssl_verify | ||
| self._ssl_verify_locations = ssl_verify_locations | ||
| self._client_certchain_file = client_certchain_file | ||
| self._client_key_file = client_key_file | ||
| self._client_key_type = client_key_type | ||
| self._client_key_password = client_key_password | ||
| self._ignore_client_authentication_requests = ignore_client_authentication_requests | ||
|
|
||
| # Initialize SSL context and connection objects | ||
| self._ssl_ctx = None | ||
| self._ssl = None | ||
| self._socket = None | ||
|
|
||
| self._init_ssl_ctx() | ||
|
|
||
| def _init_ssl_ctx(self) -> None: | ||
| """Initialize the SSL context with OpenSSL 3.""" | ||
| # Create SSL context - use the enum value directly since our local enums are just integers | ||
| ssl_version_value = self._ssl_version if isinstance(self._ssl_version, int) else self._ssl_version.value | ||
| self._ssl_ctx = nassl3_module.SSL_CTX(ssl_version_value) | ||
|
|
||
| # Set verification mode | ||
| ssl_verify_value = self._ssl_verify if isinstance(self._ssl_verify, int) else self._ssl_verify.value | ||
| self._ssl_ctx.set_verify(ssl_verify_value) | ||
|
|
||
| # Set CA certificate locations if provided | ||
| if self._ssl_verify_locations: | ||
| self._ssl_ctx.load_verify_locations(str(self._ssl_verify_locations)) | ||
|
|
||
| # Set client certificate and key if provided | ||
| if self._client_certchain_file: | ||
| self._ssl_ctx.use_certificate_chain_file(str(self._client_certchain_file)) | ||
|
|
||
| if self._client_key_file: | ||
| self._ssl_ctx.use_PrivateKey_file( | ||
| str(self._client_key_file), | ||
| self._client_key_type | ||
| ) | ||
|
|
||
| def set_underlying_socket(self, sock: socket.socket) -> None: | ||
| """Set the underlying socket for the SSL connection.""" | ||
| self._socket = sock | ||
|
|
||
| # Create SSL object | ||
| self._ssl = nassl3_module.SSL(self._ssl_ctx) | ||
|
|
||
| # Create BIO and set it to the SSL object | ||
| sock_bio = nassl3_module.BIO() | ||
| sock_bio.set_sock(sock) | ||
| self._ssl.set_bio(sock_bio) | ||
|
|
||
| def do_handshake(self) -> None: | ||
| """Perform the SSL handshake.""" | ||
| if not self._ssl: | ||
| raise ValueError("Socket must be set before performing handshake") | ||
| self._ssl.do_handshake() | ||
|
|
||
| def write(self, data: bytes) -> int: | ||
| """Write data to the SSL connection.""" | ||
| if not self._ssl: | ||
| raise ValueError("Socket must be set before writing") | ||
| return self._ssl.write(data) | ||
|
|
||
| def read(self, size: int = 1024) -> bytes: | ||
| """Read data from the SSL connection.""" | ||
| if not self._ssl: | ||
| raise ValueError("Socket must be set before reading") | ||
| return self._ssl.read(size) | ||
|
|
||
| def get_peer_certificate(self): | ||
| """Get the peer's certificate.""" | ||
| if not self._ssl: | ||
| raise ValueError("Socket must be set before getting peer certificate") | ||
| return self._ssl.get_peer_certificate() | ||
|
|
||
| def get_peer_cert_chain(self): | ||
| """Get the peer's certificate chain.""" | ||
| if not self._ssl: | ||
| raise ValueError("Socket must be set before getting peer certificate chain") | ||
| return self._ssl.get_peer_cert_chain() | ||
|
|
||
| def get_current_cipher(self): | ||
| """Get the current cipher being used.""" | ||
| if not self._ssl: | ||
| raise ValueError("Socket must be set before getting current cipher") | ||
| return self._ssl.get_current_cipher() | ||
|
|
||
| def shutdown(self) -> None: | ||
| """Shutdown the SSL connection.""" | ||
| if self._ssl: | ||
| try: | ||
| self._ssl.shutdown() | ||
| except OpenSSLError: | ||
| # Ignore shutdown errors | ||
| pass | ||
|
|
||
| @staticmethod | ||
| def is_available() -> bool: | ||
| """Check if OpenSSL 3 support is available.""" | ||
| return _OPENSSL3_AVAILABLE | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
given the experimental project status, maybe it makes since to move this into modern ssl and move the current modern ssl to legacy (but this would remove support for the existing legacy client)