Skip to content

Commit

Permalink
🔖 Release 1.0.2 (#2)
Browse files Browse the repository at this point in the history
### Added
- Function `register_ca` so that user may register their own custom CA (PEM, and DER accepted) in addition to the system trust store.

### Fixed
- Overrule `SSL_CERT_FILE` environment variable so that system CA is always returned.

### Changed
- Function `create_default_ssl_context` now instantiate an `SSLContext` with the Mozilla Recommended Cipher Suite, instead of your system default.
  • Loading branch information
Ousret authored Nov 11, 2023
1 parent 8398d6c commit f668ede
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 5 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,17 @@
All notable changes to wassima will be documented in this file. This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

## 1.0.2 (2023-11-11)

### Added
- Function `register_ca` so that user may register their own custom CA (PEM, and DER accepted) in addition to the system trust store.

### Fixed
- Overrule `SSL_CERT_FILE` environment variable so that system CA is always returned.

### Changed
- Function `create_default_ssl_context` now instantiate an `SSLContext` with the Mozilla Recommended Cipher Suite, instead of your system default.

## 1.0.1 (2023-09-26)

### Added
Expand Down
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,14 @@ bundle = wassima.generate_ca_bundle()
# ... It contains a string with all of your root CAs!
# It is not a path but the file content itself.
```

*C) Register your own CA in addition to the system's*

```python
import wassima

wassima.register_ca(open("./myrootca.pem", "r").read())
bundle = wassima.generate_ca_bundle()
# ... It contains a string with all of your root CAs, PLUS your own 'myrootca.pem'.
# It is not a path but the file content itself.
```
49 changes: 44 additions & 5 deletions wassima/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,38 @@

import ssl
from functools import lru_cache
from os import environ
from ssl import DER_cert_to_PEM_cert
from threading import RLock

from ._version import VERSION, __version__

#: Determine if we could load correctly the non-native rust module.
RUSTLS_LOADED: bool
# Mozilla TLS recommendations for ciphers
# General-purpose servers with a variety of clients, recommended for almost all systems.
MOZ_INTERMEDIATE_CIPHERS: str = "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305"
#: Contain user custom CAs
_MANUALLY_REGISTERED_CA: list[bytes] = []
#: Lock for shared register-ca
_USER_APPEND_CA_LOCK = RLock()

try:
from ._rustls import root_der_certificates
from ._rustls import root_der_certificates as _root_der_certificates

@lru_cache()
def root_der_certificates() -> list[bytes]:
try:
bck = environ.pop("SSL_CERT_FILE")
except KeyError:
bck = None

try:
with _USER_APPEND_CA_LOCK:
return _root_der_certificates() + _MANUALLY_REGISTERED_CA
finally:
if bck is not None:
environ["SSL_CERT_FILE"] = bck

RUSTLS_LOADED = True
except ImportError:
Expand All @@ -33,9 +56,9 @@
warnings.warn(
f"""Unable to access your system root CAs. Your particular interpreter and/or
operating system ({platform.python_implementation()}, {platform.uname()}, {platform.python_version()})
is not be supported. While it is not ideal, you may circumvent that warning by having certifi
is not supported. While it is not ideal, you may circumvent that warning by having certifi
installed in your environment. Run `python -m pip install certifi`.
You may also open an issue at https://github.com/jawah/wassima/issues to get your platform compatible.""",
You may also open an issue at https://github.com/jawah/wassima/issues to get your platform supported.""",
RuntimeWarning,
)

Expand Down Expand Up @@ -75,18 +98,33 @@ def generate_ca_bundle() -> str:
return "\n\n".join(root_pem_certificates())


def register_ca(pem_or_der_certificate: bytes | str) -> None:
"""
You may register your own CA certificate in addition to your system trust store.
"""
with _USER_APPEND_CA_LOCK:
if isinstance(pem_or_der_certificate, str):
pem_or_der_certificate = PEM_cert_to_DER_cert(pem_or_der_certificate)

if pem_or_der_certificate not in _MANUALLY_REGISTERED_CA:
_MANUALLY_REGISTERED_CA.append(pem_or_der_certificate)

root_pem_certificates.cache_clear()
root_der_certificates.cache_clear()


def create_default_ssl_context() -> ssl.SSLContext:
"""
Instantiate a native SSLContext (client purposes) that ships with your system root CAs.
In addition to that, assign it the default OpenSSL ciphers suite and set
TLS 1.2 as the minimum supported version. Also disable commonName check and enforce
hostname altName verification.
hostname altName verification. The Mozilla Recommended Cipher Suite is used instead of system default.
"""
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)

ctx.load_verify_locations(cadata=generate_ca_bundle())
ctx.minimum_version = ssl.TLSVersion.TLSv1_2
ctx.set_ciphers("DEFAULT")
ctx.set_ciphers(MOZ_INTERMEDIATE_CIPHERS)
ctx.verify_mode = ssl.CERT_REQUIRED

try:
Expand All @@ -107,6 +145,7 @@ def create_default_ssl_context() -> ssl.SSLContext:
"root_pem_certificates",
"generate_ca_bundle",
"create_default_ssl_context",
"register_ca",
"__version__",
"VERSION",
"RUSTLS_LOADED",
Expand Down

0 comments on commit f668ede

Please sign in to comment.