tigrcorn-security 0.3.16.dev12__tar.gz → 0.3.17.dev1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {tigrcorn_security-0.3.16.dev12 → tigrcorn_security-0.3.17.dev1}/PKG-INFO +3 -3
- {tigrcorn_security-0.3.16.dev12 → tigrcorn_security-0.3.17.dev1}/pyproject.toml +3 -3
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/tls/__init__.py +34 -0
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/tls/certificates.py +56 -0
- tigrcorn_security-0.3.16.dev12/src/tigrcorn_security/tls.py → tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/tls/connection.py +6 -289
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/tls/context.py +38 -0
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/tls/imports.py +66 -0
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/tls/models.py +31 -0
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/tls/records.py +50 -0
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/tls/ssl_object.py +40 -0
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/tls/wrapping.py +16 -0
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/tls13/extensions/__init__.py +18 -0
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/tls13/extensions/cipher_suites.py +36 -0
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/tls13/extensions/common.py +116 -0
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/tls13/extensions/imports.py +31 -0
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/tls13/extensions/key_share.py +66 -0
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/tls13/extensions/models.py +267 -0
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/tls13/extensions/psk.py +83 -0
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/tls13/extensions/quic.py +14 -0
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/tls13/extensions/registry.py +129 -0
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/tls13/extensions/vectors.py +74 -0
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/tls13/handshake/__init__.py +29 -0
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/tls13/handshake/alerts.py +34 -0
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/tls13/handshake/base.py +314 -0
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/tls13/handshake/certificates.py +54 -0
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/tls13/handshake/client.py +216 -0
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/tls13/handshake/driver.py +18 -0
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/tls13/handshake/imports.py +100 -0
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/tls13/handshake/io.py +159 -0
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/tls13/handshake/key_exchange.py +62 -0
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/tls13/handshake/models.py +102 -0
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/tls13/handshake/replay.py +22 -0
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/tls13/handshake/server.py +269 -0
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/tls13/handshake/signatures.py +76 -0
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/tls13/handshake/state_table.py +8 -0
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/tls13/handshake/tickets.py +78 -0
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/tls13/handshake/utils.py +42 -0
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/tls13/messages/__init__.py +31 -0
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/tls13/messages/base.py +17 -0
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/tls13/messages/certificates.py +95 -0
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/tls13/messages/decode.py +44 -0
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/tls13/messages/finished.py +87 -0
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/tls13/messages/hello.py +110 -0
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/tls13/messages/imports.py +23 -0
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/tls13/messages/types.py +22 -0
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/tls13/messages/vectors.py +75 -0
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/x509/path/__init__.py +29 -0
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/x509/path/chain.py +81 -0
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/x509/path/constraints.py +171 -0
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/x509/path/hostname.py +82 -0
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/x509/path/imports.py +59 -0
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/x509/path/loading.py +65 -0
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/x509/path/models.py +144 -0
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/x509/path/revocation_fetch.py +315 -0
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/x509/path/revocation_material.py +205 -0
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/x509/path/revocation_policy.py +142 -0
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security/x509/path/time.py +68 -0
- {tigrcorn_security-0.3.16.dev12 → tigrcorn_security-0.3.17.dev1}/src/tigrcorn_security.egg-info/PKG-INFO +3 -3
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security.egg-info/SOURCES.txt +73 -0
- tigrcorn_security-0.3.17.dev1/src/tigrcorn_security.egg-info/requires.txt +5 -0
- tigrcorn_security-0.3.16.dev12/src/tigrcorn_security/tls13/extensions.py +0 -759
- tigrcorn_security-0.3.16.dev12/src/tigrcorn_security/tls13/handshake.py +0 -1411
- tigrcorn_security-0.3.16.dev12/src/tigrcorn_security/tls13/messages.py +0 -428
- tigrcorn_security-0.3.16.dev12/src/tigrcorn_security/x509/path.py +0 -1284
- tigrcorn_security-0.3.16.dev12/src/tigrcorn_security.egg-info/SOURCES.txt +0 -23
- tigrcorn_security-0.3.16.dev12/src/tigrcorn_security.egg-info/requires.txt +0 -5
- {tigrcorn_security-0.3.16.dev12 → tigrcorn_security-0.3.17.dev1}/LICENSE +0 -0
- {tigrcorn_security-0.3.16.dev12 → tigrcorn_security-0.3.17.dev1}/README.md +0 -0
- {tigrcorn_security-0.3.16.dev12 → tigrcorn_security-0.3.17.dev1}/setup.cfg +0 -0
- {tigrcorn_security-0.3.16.dev12 → tigrcorn_security-0.3.17.dev1}/src/tigrcorn_security/__init__.py +0 -0
- {tigrcorn_security-0.3.16.dev12 → tigrcorn_security-0.3.17.dev1}/src/tigrcorn_security/alpn.py +0 -0
- {tigrcorn_security-0.3.16.dev12 → tigrcorn_security-0.3.17.dev1}/src/tigrcorn_security/certs.py +0 -0
- {tigrcorn_security-0.3.16.dev12 → tigrcorn_security-0.3.17.dev1}/src/tigrcorn_security/policies.py +0 -0
- {tigrcorn_security-0.3.16.dev12 → tigrcorn_security-0.3.17.dev1}/src/tigrcorn_security/py.typed +0 -0
- {tigrcorn_security-0.3.16.dev12 → tigrcorn_security-0.3.17.dev1}/src/tigrcorn_security/tls13/__init__.py +0 -0
- {tigrcorn_security-0.3.16.dev12 → tigrcorn_security-0.3.17.dev1}/src/tigrcorn_security/tls13/key_schedule.py +0 -0
- {tigrcorn_security-0.3.16.dev12 → tigrcorn_security-0.3.17.dev1}/src/tigrcorn_security/tls13/transcript.py +0 -0
- {tigrcorn_security-0.3.16.dev12 → tigrcorn_security-0.3.17.dev1}/src/tigrcorn_security/tls_cipher_policy.py +0 -0
- {tigrcorn_security-0.3.16.dev12 → tigrcorn_security-0.3.17.dev1}/src/tigrcorn_security/x509/__init__.py +0 -0
- {tigrcorn_security-0.3.16.dev12 → tigrcorn_security-0.3.17.dev1}/src/tigrcorn_security.egg-info/dependency_links.txt +0 -0
- {tigrcorn_security-0.3.16.dev12 → tigrcorn_security-0.3.17.dev1}/src/tigrcorn_security.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tigrcorn-security
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.17.dev1
|
|
4
4
|
Summary: TLS 1.3, mTLS, X.509, ALPN, OCSP, CRL, and certificate policy surfaces for the Tigrcorn Python web server.
|
|
5
5
|
Author-email: Jacob Stewart <jacob@swarmauri.com>
|
|
6
6
|
License: Apache License
|
|
@@ -187,8 +187,8 @@ Classifier: Typing :: Typed
|
|
|
187
187
|
Requires-Python: <3.15,>=3.10
|
|
188
188
|
Description-Content-Type: text/markdown
|
|
189
189
|
License-File: LICENSE
|
|
190
|
-
Requires-Dist: tigrcorn-core==0.3.
|
|
191
|
-
Requires-Dist: tigrcorn-config==0.3.
|
|
190
|
+
Requires-Dist: tigrcorn-core==0.3.17.dev1
|
|
191
|
+
Requires-Dist: tigrcorn-config==0.3.17.dev1
|
|
192
192
|
Provides-Extra: x509
|
|
193
193
|
Requires-Dist: cryptography>=46.0.0; extra == "x509"
|
|
194
194
|
Dynamic: license-file
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "tigrcorn-security"
|
|
7
|
-
version = "0.3.
|
|
7
|
+
version = "0.3.17.dev1"
|
|
8
8
|
description = "TLS 1.3, mTLS, X.509, ALPN, OCSP, CRL, and certificate policy surfaces for the Tigrcorn Python web server."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10,<3.15"
|
|
@@ -30,8 +30,8 @@ classifiers = [
|
|
|
30
30
|
"Typing :: Typed",
|
|
31
31
|
]
|
|
32
32
|
dependencies = [
|
|
33
|
-
"tigrcorn-core==0.3.
|
|
34
|
-
"tigrcorn-config==0.3.
|
|
33
|
+
"tigrcorn-core==0.3.17.dev1",
|
|
34
|
+
"tigrcorn-config==0.3.17.dev1",
|
|
35
35
|
]
|
|
36
36
|
|
|
37
37
|
[project.optional-dependencies]
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from .imports import *
|
|
4
|
+
from .models import *
|
|
5
|
+
from .ssl_object import *
|
|
6
|
+
from .records import *
|
|
7
|
+
from .certificates import *
|
|
8
|
+
from .connection import *
|
|
9
|
+
from .context import *
|
|
10
|
+
from .wrapping import *
|
|
11
|
+
|
|
12
|
+
build_server_tls_context = build_server_ssl_context
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
'PackageOwnedSSLObject',
|
|
16
|
+
'PackageOwnedTLSConnection',
|
|
17
|
+
'ServerTLSContext',
|
|
18
|
+
'build_server_ssl_context',
|
|
19
|
+
'build_server_tls_context',
|
|
20
|
+
'wrap_server_tls_connection',
|
|
21
|
+
'tls_extension_payload',
|
|
22
|
+
'CertificatePurpose',
|
|
23
|
+
'CertificateValidationPolicy',
|
|
24
|
+
'RevocationCache',
|
|
25
|
+
'RevocationCacheEntry',
|
|
26
|
+
'RevocationFetchPolicy',
|
|
27
|
+
'RevocationFreshnessPolicy',
|
|
28
|
+
'RevocationMaterial',
|
|
29
|
+
'RevocationMode',
|
|
30
|
+
'load_pem_certificates',
|
|
31
|
+
'verify_certificate_validity',
|
|
32
|
+
'verify_certificate_hostname',
|
|
33
|
+
'verify_certificate_chain',
|
|
34
|
+
]
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from .imports import *
|
|
4
|
+
|
|
5
|
+
def describe_peer_certificate(certificate: x509.Certificate) -> dict[str, Any]:
|
|
6
|
+
return {
|
|
7
|
+
'subject': certificate.subject.rfc4514_string(),
|
|
8
|
+
'issuer': certificate.issuer.rfc4514_string(),
|
|
9
|
+
'serial_number': hex(certificate.serial_number),
|
|
10
|
+
'not_valid_before': _iso_utc(
|
|
11
|
+
certificate.not_valid_before_utc if hasattr(certificate, 'not_valid_before_utc') else certificate.not_valid_before
|
|
12
|
+
),
|
|
13
|
+
'not_valid_after': _iso_utc(
|
|
14
|
+
certificate.not_valid_after_utc if hasattr(certificate, 'not_valid_after_utc') else certificate.not_valid_after
|
|
15
|
+
),
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def tls_extension_payload(writer: Any) -> dict[str, Any] | None:
|
|
20
|
+
ssl_object = getattr(writer, 'get_extra_info', lambda *args, **kwargs: None)('ssl_object')
|
|
21
|
+
if ssl_object is None:
|
|
22
|
+
return None
|
|
23
|
+
payload: dict[str, Any] = {}
|
|
24
|
+
selected_alpn = getattr(ssl_object, 'selected_alpn_protocol', lambda: None)()
|
|
25
|
+
if selected_alpn is not None:
|
|
26
|
+
payload['selected_alpn_protocol'] = selected_alpn
|
|
27
|
+
getpeercert = getattr(ssl_object, 'getpeercert', None)
|
|
28
|
+
if callable(getpeercert):
|
|
29
|
+
peer_cert = getpeercert(binary_form=False)
|
|
30
|
+
if peer_cert is not None:
|
|
31
|
+
payload['peer_cert'] = peer_cert
|
|
32
|
+
return payload or None
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def verify_certificate_chain(
|
|
36
|
+
chain_pems: Iterable[bytes],
|
|
37
|
+
trust_roots_pems: Iterable[bytes],
|
|
38
|
+
*,
|
|
39
|
+
server_name: str = '',
|
|
40
|
+
moment: datetime | None = None,
|
|
41
|
+
policy: CertificateValidationPolicy | None = None,
|
|
42
|
+
) -> x509.Certificate:
|
|
43
|
+
return _verify_certificate_chain(
|
|
44
|
+
chain_pems,
|
|
45
|
+
trust_roots_pems,
|
|
46
|
+
server_name=server_name,
|
|
47
|
+
moment=moment,
|
|
48
|
+
policy=policy,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
def _iso_utc(moment: datetime) -> str:
|
|
52
|
+
if moment.tzinfo is None:
|
|
53
|
+
moment = moment.replace(tzinfo=timezone.utc)
|
|
54
|
+
return moment.astimezone(timezone.utc).isoformat().replace('+00:00', 'Z')
|
|
55
|
+
|
|
56
|
+
__all__ = [name for name in globals() if not name.startswith('__')]
|
|
@@ -1,128 +1,10 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
from
|
|
7
|
-
from
|
|
8
|
-
from pathlib import Path
|
|
9
|
-
from typing import Any, Iterable
|
|
10
|
-
|
|
11
|
-
class _MissingDependencyProxy:
|
|
12
|
-
def __init__(self, package: str) -> None:
|
|
13
|
-
self._package = package
|
|
14
|
-
|
|
15
|
-
def __getattr__(self, name: str):
|
|
16
|
-
raise ModuleNotFoundError(
|
|
17
|
-
f"{self._package} is required for this TLS/X.509 operation; install tigrcorn[tls-x509]"
|
|
18
|
-
)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
try:
|
|
22
|
-
from cryptography import x509
|
|
23
|
-
from cryptography.hazmat.primitives import serialization
|
|
24
|
-
except ModuleNotFoundError: # pragma: no cover - exercised in dependency-light environments
|
|
25
|
-
x509 = _MissingDependencyProxy("cryptography") # type: ignore[assignment]
|
|
26
|
-
serialization = _MissingDependencyProxy("cryptography") # type: ignore[assignment]
|
|
27
|
-
|
|
28
|
-
from tigrcorn_config.model import ListenerConfig
|
|
29
|
-
from tigrcorn_core.errors import ProtocolError
|
|
30
|
-
from tigrcorn_security.tls13.handshake import QuicTlsHandshakeDriver, TlsAlertError
|
|
31
|
-
from tigrcorn_security.tls13.key_schedule import Tls13KeySchedule
|
|
32
|
-
from tigrcorn_security.tls13.messages import decode_handshake_message
|
|
33
|
-
from tigrcorn_security.policies import build_validation_policy_for_listener
|
|
34
|
-
from tigrcorn_security.x509.path import (
|
|
35
|
-
CertificatePurpose,
|
|
36
|
-
CertificateValidationPolicy,
|
|
37
|
-
RevocationCache,
|
|
38
|
-
RevocationCacheEntry,
|
|
39
|
-
RevocationFetchPolicy,
|
|
40
|
-
RevocationFreshnessPolicy,
|
|
41
|
-
RevocationMaterial,
|
|
42
|
-
RevocationMode,
|
|
43
|
-
load_pem_certificates,
|
|
44
|
-
verify_certificate_chain as _verify_certificate_chain,
|
|
45
|
-
verify_certificate_hostname,
|
|
46
|
-
verify_certificate_validity,
|
|
47
|
-
)
|
|
48
|
-
from tigrcorn_transports.quic.crypto import aes_gcm_decrypt, aes_gcm_encrypt
|
|
49
|
-
|
|
50
|
-
_TLS_CONTENT_CHANGE_CIPHER_SPEC = 20
|
|
51
|
-
_TLS_CONTENT_ALERT = 21
|
|
52
|
-
_TLS_CONTENT_HANDSHAKE = 22
|
|
53
|
-
_TLS_CONTENT_APPLICATION_DATA = 23
|
|
54
|
-
_TLS_LEGACY_RECORD_VERSION = 0x0303
|
|
55
|
-
_TLS_MAX_PLAINTEXT = 16384
|
|
56
|
-
_TLS_ALERT_LEVEL_FATAL = 2
|
|
57
|
-
_TLS_ALERT_CLOSE_NOTIFY = 0
|
|
58
|
-
|
|
59
|
-
_CIPHER_NAMES = {
|
|
60
|
-
0x1301: ('TLS_AES_128_GCM_SHA256', 128),
|
|
61
|
-
0x1302: ('TLS_AES_256_GCM_SHA384', 256),
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
@dataclass(frozen=True, slots=True)
|
|
66
|
-
class ServerTLSContext:
|
|
67
|
-
certificate_pem: bytes
|
|
68
|
-
private_key_pem: bytes
|
|
69
|
-
private_key_password: bytes | None
|
|
70
|
-
trusted_certificates: tuple[bytes, ...]
|
|
71
|
-
alpn_protocols: tuple[str, ...]
|
|
72
|
-
require_client_certificate: bool
|
|
73
|
-
validation_policy: CertificateValidationPolicy
|
|
74
|
-
cipher_suites: tuple[int, ...] = (0x1302, 0x1301)
|
|
75
|
-
server_name: str = 'localhost'
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
@dataclass(slots=True)
|
|
79
|
-
class _RecordProtectionState:
|
|
80
|
-
key: bytes
|
|
81
|
-
iv: bytes
|
|
82
|
-
sequence_number: int = 0
|
|
83
|
-
|
|
84
|
-
def next_nonce(self) -> bytes:
|
|
85
|
-
sequence = self.sequence_number.to_bytes(8, 'big')
|
|
86
|
-
padded = b'\x00' * (len(self.iv) - len(sequence)) + sequence
|
|
87
|
-
nonce = bytes(left ^ right for left, right in zip(self.iv, padded))
|
|
88
|
-
self.sequence_number += 1
|
|
89
|
-
return nonce
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
class PackageOwnedSSLObject:
|
|
93
|
-
def __init__(
|
|
94
|
-
self,
|
|
95
|
-
*,
|
|
96
|
-
selected_alpn_protocol: str | None,
|
|
97
|
-
cipher_suite: int,
|
|
98
|
-
peer_certificate: x509.Certificate | None,
|
|
99
|
-
) -> None:
|
|
100
|
-
self._selected_alpn_protocol = selected_alpn_protocol
|
|
101
|
-
self._cipher_suite = cipher_suite
|
|
102
|
-
self._peer_certificate = peer_certificate
|
|
103
|
-
self._peer_certificate_der = (
|
|
104
|
-
peer_certificate.public_bytes(serialization.Encoding.DER)
|
|
105
|
-
if peer_certificate is not None
|
|
106
|
-
else None
|
|
107
|
-
)
|
|
108
|
-
|
|
109
|
-
def selected_alpn_protocol(self) -> str | None:
|
|
110
|
-
return self._selected_alpn_protocol
|
|
111
|
-
|
|
112
|
-
def version(self) -> str:
|
|
113
|
-
return 'TLSv1.3'
|
|
114
|
-
|
|
115
|
-
def cipher(self) -> tuple[str, str, int]:
|
|
116
|
-
name, bits = _CIPHER_NAMES.get(self._cipher_suite, ('TLS_UNKNOWN', 0))
|
|
117
|
-
return name, 'TLSv1.3', bits
|
|
118
|
-
|
|
119
|
-
def getpeercert(self, binary_form: bool = False) -> dict[str, Any] | bytes | None:
|
|
120
|
-
if self._peer_certificate is None:
|
|
121
|
-
return None
|
|
122
|
-
if binary_form:
|
|
123
|
-
return self._peer_certificate_der
|
|
124
|
-
return describe_peer_certificate(self._peer_certificate)
|
|
125
|
-
|
|
3
|
+
from .imports import *
|
|
4
|
+
from .models import *
|
|
5
|
+
from .ssl_object import *
|
|
6
|
+
from .records import *
|
|
7
|
+
from .certificates import *
|
|
126
8
|
|
|
127
9
|
class PackageOwnedTLSConnection:
|
|
128
10
|
def __init__(
|
|
@@ -415,169 +297,4 @@ class PackageOwnedTLSConnection:
|
|
|
415
297
|
)
|
|
416
298
|
await self._raw_writer.drain()
|
|
417
299
|
|
|
418
|
-
|
|
419
|
-
def _build_record_state(secret: bytes, *, key_length: int, iv_length: int, hash_name: str) -> _RecordProtectionState:
|
|
420
|
-
schedule = Tls13KeySchedule(hash_name=hash_name)
|
|
421
|
-
return _RecordProtectionState(
|
|
422
|
-
key=schedule.expand_label(secret, 'key', b'', key_length),
|
|
423
|
-
iv=schedule.expand_label(secret, 'iv', b'', iv_length),
|
|
424
|
-
)
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
def _encode_plain_record(content_type: int, payload: bytes) -> bytes:
|
|
428
|
-
return bytes([content_type]) + _TLS_LEGACY_RECORD_VERSION.to_bytes(2, 'big') + len(payload).to_bytes(2, 'big') + payload
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
def _encrypt_record(payload: bytes, inner_content_type: int, state: _RecordProtectionState) -> bytes:
|
|
432
|
-
inner = payload + bytes([inner_content_type])
|
|
433
|
-
nonce = state.next_nonce()
|
|
434
|
-
body_length = len(inner) + 16
|
|
435
|
-
header = (
|
|
436
|
-
bytes([_TLS_CONTENT_APPLICATION_DATA])
|
|
437
|
-
+ _TLS_LEGACY_RECORD_VERSION.to_bytes(2, 'big')
|
|
438
|
-
+ body_length.to_bytes(2, 'big')
|
|
439
|
-
)
|
|
440
|
-
ciphertext, tag = aes_gcm_encrypt(state.key, nonce, inner, aad=header)
|
|
441
|
-
return header + ciphertext + tag
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
def _decrypt_record(payload: bytes, state: _RecordProtectionState) -> tuple[bytes, int]:
|
|
445
|
-
if len(payload) < 16:
|
|
446
|
-
raise ProtocolError('truncated TLS application-data record')
|
|
447
|
-
header = (
|
|
448
|
-
bytes([_TLS_CONTENT_APPLICATION_DATA])
|
|
449
|
-
+ _TLS_LEGACY_RECORD_VERSION.to_bytes(2, 'big')
|
|
450
|
-
+ len(payload).to_bytes(2, 'big')
|
|
451
|
-
)
|
|
452
|
-
ciphertext = payload[:-16]
|
|
453
|
-
tag = payload[-16:]
|
|
454
|
-
nonce = state.next_nonce()
|
|
455
|
-
plaintext = aes_gcm_decrypt(state.key, nonce, ciphertext, tag, aad=header)
|
|
456
|
-
index = len(plaintext) - 1
|
|
457
|
-
while index >= 0 and plaintext[index] == 0:
|
|
458
|
-
index -= 1
|
|
459
|
-
if index < 0:
|
|
460
|
-
raise ProtocolError('TLS inner plaintext is missing a content type')
|
|
461
|
-
return plaintext[:index], plaintext[index]
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
def describe_peer_certificate(certificate: x509.Certificate) -> dict[str, Any]:
|
|
465
|
-
return {
|
|
466
|
-
'subject': certificate.subject.rfc4514_string(),
|
|
467
|
-
'issuer': certificate.issuer.rfc4514_string(),
|
|
468
|
-
'serial_number': hex(certificate.serial_number),
|
|
469
|
-
'not_valid_before': _iso_utc(
|
|
470
|
-
certificate.not_valid_before_utc if hasattr(certificate, 'not_valid_before_utc') else certificate.not_valid_before
|
|
471
|
-
),
|
|
472
|
-
'not_valid_after': _iso_utc(
|
|
473
|
-
certificate.not_valid_after_utc if hasattr(certificate, 'not_valid_after_utc') else certificate.not_valid_after
|
|
474
|
-
),
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
def tls_extension_payload(writer: Any) -> dict[str, Any] | None:
|
|
479
|
-
ssl_object = getattr(writer, 'get_extra_info', lambda *args, **kwargs: None)('ssl_object')
|
|
480
|
-
if ssl_object is None:
|
|
481
|
-
return None
|
|
482
|
-
payload: dict[str, Any] = {}
|
|
483
|
-
selected_alpn = getattr(ssl_object, 'selected_alpn_protocol', lambda: None)()
|
|
484
|
-
if selected_alpn is not None:
|
|
485
|
-
payload['selected_alpn_protocol'] = selected_alpn
|
|
486
|
-
getpeercert = getattr(ssl_object, 'getpeercert', None)
|
|
487
|
-
if callable(getpeercert):
|
|
488
|
-
peer_cert = getpeercert(binary_form=False)
|
|
489
|
-
if peer_cert is not None:
|
|
490
|
-
payload['peer_cert'] = peer_cert
|
|
491
|
-
return payload or None
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
def build_server_ssl_context(listener: ListenerConfig) -> ServerTLSContext | None:
|
|
495
|
-
if not listener.ssl_enabled:
|
|
496
|
-
return None
|
|
497
|
-
assert listener.ssl_certfile is not None
|
|
498
|
-
assert listener.ssl_keyfile is not None
|
|
499
|
-
certificate_pem = Path(listener.ssl_certfile).read_bytes()
|
|
500
|
-
private_key_pem = Path(listener.ssl_keyfile).read_bytes()
|
|
501
|
-
private_key_password = getattr(listener, 'ssl_keyfile_password', None)
|
|
502
|
-
if private_key_password is not None and not isinstance(private_key_password, bytes):
|
|
503
|
-
private_key_password = str(private_key_password).encode('utf-8')
|
|
504
|
-
trusted = (Path(listener.ssl_ca_certs).read_bytes(),) if listener.ssl_ca_certs else ()
|
|
505
|
-
validation_policy = build_validation_policy_for_listener(listener)
|
|
506
|
-
server_name = _listener_server_name(listener)
|
|
507
|
-
return ServerTLSContext(
|
|
508
|
-
certificate_pem=certificate_pem,
|
|
509
|
-
private_key_pem=private_key_pem,
|
|
510
|
-
private_key_password=private_key_password,
|
|
511
|
-
trusted_certificates=trusted,
|
|
512
|
-
alpn_protocols=tuple(listener.alpn_protocols),
|
|
513
|
-
require_client_certificate=listener.ssl_require_client_cert,
|
|
514
|
-
validation_policy=validation_policy,
|
|
515
|
-
cipher_suites=tuple(int(item) for item in (getattr(listener, 'resolved_cipher_suites', ()) or (0x1302, 0x1301))),
|
|
516
|
-
server_name=server_name,
|
|
517
|
-
)
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
async def wrap_server_tls_connection(
|
|
521
|
-
raw_reader: asyncio.StreamReader,
|
|
522
|
-
raw_writer: asyncio.StreamWriter,
|
|
523
|
-
context: ServerTLSContext,
|
|
524
|
-
) -> PackageOwnedTLSConnection:
|
|
525
|
-
connection = PackageOwnedTLSConnection(raw_reader, raw_writer, context)
|
|
526
|
-
await connection.handshake()
|
|
527
|
-
return connection
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
build_server_tls_context = build_server_ssl_context
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
def verify_certificate_chain(
|
|
534
|
-
chain_pems: Iterable[bytes],
|
|
535
|
-
trust_roots_pems: Iterable[bytes],
|
|
536
|
-
*,
|
|
537
|
-
server_name: str = '',
|
|
538
|
-
moment: datetime | None = None,
|
|
539
|
-
policy: CertificateValidationPolicy | None = None,
|
|
540
|
-
) -> x509.Certificate:
|
|
541
|
-
return _verify_certificate_chain(
|
|
542
|
-
chain_pems,
|
|
543
|
-
trust_roots_pems,
|
|
544
|
-
server_name=server_name,
|
|
545
|
-
moment=moment,
|
|
546
|
-
policy=policy,
|
|
547
|
-
)
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
def _listener_server_name(listener: ListenerConfig) -> str:
|
|
551
|
-
host = listener.host or 'localhost'
|
|
552
|
-
if host in {'0.0.0.0', '::', ''}:
|
|
553
|
-
return 'localhost'
|
|
554
|
-
return host
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
def _iso_utc(moment: datetime) -> str:
|
|
558
|
-
if moment.tzinfo is None:
|
|
559
|
-
moment = moment.replace(tzinfo=timezone.utc)
|
|
560
|
-
return moment.astimezone(timezone.utc).isoformat().replace('+00:00', 'Z')
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
__all__ = [
|
|
564
|
-
'PackageOwnedSSLObject',
|
|
565
|
-
'PackageOwnedTLSConnection',
|
|
566
|
-
'ServerTLSContext',
|
|
567
|
-
'build_server_ssl_context',
|
|
568
|
-
'build_server_tls_context',
|
|
569
|
-
'wrap_server_tls_connection',
|
|
570
|
-
'tls_extension_payload',
|
|
571
|
-
'CertificatePurpose',
|
|
572
|
-
'CertificateValidationPolicy',
|
|
573
|
-
'RevocationCache',
|
|
574
|
-
'RevocationCacheEntry',
|
|
575
|
-
'RevocationFetchPolicy',
|
|
576
|
-
'RevocationFreshnessPolicy',
|
|
577
|
-
'RevocationMaterial',
|
|
578
|
-
'RevocationMode',
|
|
579
|
-
'load_pem_certificates',
|
|
580
|
-
'verify_certificate_validity',
|
|
581
|
-
'verify_certificate_hostname',
|
|
582
|
-
'verify_certificate_chain',
|
|
583
|
-
]
|
|
300
|
+
__all__ = [name for name in globals() if not name.startswith('__')]
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from .imports import *
|
|
4
|
+
from .models import *
|
|
5
|
+
|
|
6
|
+
def build_server_ssl_context(listener: ListenerConfig) -> ServerTLSContext | None:
|
|
7
|
+
if not listener.ssl_enabled:
|
|
8
|
+
return None
|
|
9
|
+
assert listener.ssl_certfile is not None
|
|
10
|
+
assert listener.ssl_keyfile is not None
|
|
11
|
+
certificate_pem = Path(listener.ssl_certfile).read_bytes()
|
|
12
|
+
private_key_pem = Path(listener.ssl_keyfile).read_bytes()
|
|
13
|
+
private_key_password = getattr(listener, 'ssl_keyfile_password', None)
|
|
14
|
+
if private_key_password is not None and not isinstance(private_key_password, bytes):
|
|
15
|
+
private_key_password = str(private_key_password).encode('utf-8')
|
|
16
|
+
trusted = (Path(listener.ssl_ca_certs).read_bytes(),) if listener.ssl_ca_certs else ()
|
|
17
|
+
validation_policy = build_validation_policy_for_listener(listener)
|
|
18
|
+
server_name = _listener_server_name(listener)
|
|
19
|
+
return ServerTLSContext(
|
|
20
|
+
certificate_pem=certificate_pem,
|
|
21
|
+
private_key_pem=private_key_pem,
|
|
22
|
+
private_key_password=private_key_password,
|
|
23
|
+
trusted_certificates=trusted,
|
|
24
|
+
alpn_protocols=tuple(listener.alpn_protocols),
|
|
25
|
+
require_client_certificate=listener.ssl_require_client_cert,
|
|
26
|
+
validation_policy=validation_policy,
|
|
27
|
+
cipher_suites=tuple(int(item) for item in (getattr(listener, 'resolved_cipher_suites', ()) or (0x1302, 0x1301))),
|
|
28
|
+
server_name=server_name,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _listener_server_name(listener: ListenerConfig) -> str:
|
|
33
|
+
host = listener.host or 'localhost'
|
|
34
|
+
if host in {'0.0.0.0', '::', ''}:
|
|
35
|
+
return 'localhost'
|
|
36
|
+
return host
|
|
37
|
+
|
|
38
|
+
__all__ = [name for name in globals() if not name.startswith('__')]
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import contextlib
|
|
7
|
+
import threading
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
from datetime import datetime, timezone
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Any, Iterable
|
|
12
|
+
|
|
13
|
+
class _MissingDependencyProxy:
|
|
14
|
+
def __init__(self, package: str) -> None:
|
|
15
|
+
self._package = package
|
|
16
|
+
|
|
17
|
+
def __getattr__(self, name: str):
|
|
18
|
+
raise ModuleNotFoundError(
|
|
19
|
+
f"{self._package} is required for this TLS/X.509 operation; install tigrcorn[tls-x509]"
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
try:
|
|
24
|
+
from cryptography import x509
|
|
25
|
+
from cryptography.hazmat.primitives import serialization
|
|
26
|
+
except ModuleNotFoundError: # pragma: no cover - exercised in dependency-light environments
|
|
27
|
+
x509 = _MissingDependencyProxy("cryptography") # type: ignore[assignment]
|
|
28
|
+
serialization = _MissingDependencyProxy("cryptography") # type: ignore[assignment]
|
|
29
|
+
|
|
30
|
+
from tigrcorn_config.model import ListenerConfig
|
|
31
|
+
from tigrcorn_core.errors import ProtocolError
|
|
32
|
+
from tigrcorn_security.tls13.handshake import QuicTlsHandshakeDriver, TlsAlertError
|
|
33
|
+
from tigrcorn_security.tls13.key_schedule import Tls13KeySchedule
|
|
34
|
+
from tigrcorn_security.tls13.messages import decode_handshake_message
|
|
35
|
+
from tigrcorn_security.policies import build_validation_policy_for_listener
|
|
36
|
+
from tigrcorn_security.x509.path import (
|
|
37
|
+
CertificatePurpose,
|
|
38
|
+
CertificateValidationPolicy,
|
|
39
|
+
RevocationCache,
|
|
40
|
+
RevocationCacheEntry,
|
|
41
|
+
RevocationFetchPolicy,
|
|
42
|
+
RevocationFreshnessPolicy,
|
|
43
|
+
RevocationMaterial,
|
|
44
|
+
RevocationMode,
|
|
45
|
+
load_pem_certificates,
|
|
46
|
+
verify_certificate_chain as _verify_certificate_chain,
|
|
47
|
+
verify_certificate_hostname,
|
|
48
|
+
verify_certificate_validity,
|
|
49
|
+
)
|
|
50
|
+
from tigrcorn_transports.quic.crypto import aes_gcm_decrypt, aes_gcm_encrypt
|
|
51
|
+
|
|
52
|
+
_TLS_CONTENT_CHANGE_CIPHER_SPEC = 20
|
|
53
|
+
_TLS_CONTENT_ALERT = 21
|
|
54
|
+
_TLS_CONTENT_HANDSHAKE = 22
|
|
55
|
+
_TLS_CONTENT_APPLICATION_DATA = 23
|
|
56
|
+
_TLS_LEGACY_RECORD_VERSION = 0x0303
|
|
57
|
+
_TLS_MAX_PLAINTEXT = 16384
|
|
58
|
+
_TLS_ALERT_LEVEL_FATAL = 2
|
|
59
|
+
_TLS_ALERT_CLOSE_NOTIFY = 0
|
|
60
|
+
|
|
61
|
+
_CIPHER_NAMES = {
|
|
62
|
+
0x1301: ('TLS_AES_128_GCM_SHA256', 128),
|
|
63
|
+
0x1302: ('TLS_AES_256_GCM_SHA384', 256),
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
__all__ = [name for name in globals() if not name.startswith('__')]
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from .imports import *
|
|
4
|
+
|
|
5
|
+
@dataclass(frozen=True, slots=True)
|
|
6
|
+
class ServerTLSContext:
|
|
7
|
+
certificate_pem: bytes
|
|
8
|
+
private_key_pem: bytes
|
|
9
|
+
private_key_password: bytes | None
|
|
10
|
+
trusted_certificates: tuple[bytes, ...]
|
|
11
|
+
alpn_protocols: tuple[str, ...]
|
|
12
|
+
require_client_certificate: bool
|
|
13
|
+
validation_policy: CertificateValidationPolicy
|
|
14
|
+
cipher_suites: tuple[int, ...] = (0x1302, 0x1301)
|
|
15
|
+
server_name: str = 'localhost'
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass(slots=True)
|
|
19
|
+
class _RecordProtectionState:
|
|
20
|
+
key: bytes
|
|
21
|
+
iv: bytes
|
|
22
|
+
sequence_number: int = 0
|
|
23
|
+
|
|
24
|
+
def next_nonce(self) -> bytes:
|
|
25
|
+
sequence = self.sequence_number.to_bytes(8, 'big')
|
|
26
|
+
padded = b'\x00' * (len(self.iv) - len(sequence)) + sequence
|
|
27
|
+
nonce = bytes(left ^ right for left, right in zip(self.iv, padded))
|
|
28
|
+
self.sequence_number += 1
|
|
29
|
+
return nonce
|
|
30
|
+
|
|
31
|
+
__all__ = [name for name in globals() if not name.startswith('__')]
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from .imports import *
|
|
4
|
+
from .models import *
|
|
5
|
+
|
|
6
|
+
def _build_record_state(secret: bytes, *, key_length: int, iv_length: int, hash_name: str) -> _RecordProtectionState:
|
|
7
|
+
schedule = Tls13KeySchedule(hash_name=hash_name)
|
|
8
|
+
return _RecordProtectionState(
|
|
9
|
+
key=schedule.expand_label(secret, 'key', b'', key_length),
|
|
10
|
+
iv=schedule.expand_label(secret, 'iv', b'', iv_length),
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _encode_plain_record(content_type: int, payload: bytes) -> bytes:
|
|
15
|
+
return bytes([content_type]) + _TLS_LEGACY_RECORD_VERSION.to_bytes(2, 'big') + len(payload).to_bytes(2, 'big') + payload
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _encrypt_record(payload: bytes, inner_content_type: int, state: _RecordProtectionState) -> bytes:
|
|
19
|
+
inner = payload + bytes([inner_content_type])
|
|
20
|
+
nonce = state.next_nonce()
|
|
21
|
+
body_length = len(inner) + 16
|
|
22
|
+
header = (
|
|
23
|
+
bytes([_TLS_CONTENT_APPLICATION_DATA])
|
|
24
|
+
+ _TLS_LEGACY_RECORD_VERSION.to_bytes(2, 'big')
|
|
25
|
+
+ body_length.to_bytes(2, 'big')
|
|
26
|
+
)
|
|
27
|
+
ciphertext, tag = aes_gcm_encrypt(state.key, nonce, inner, aad=header)
|
|
28
|
+
return header + ciphertext + tag
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _decrypt_record(payload: bytes, state: _RecordProtectionState) -> tuple[bytes, int]:
|
|
32
|
+
if len(payload) < 16:
|
|
33
|
+
raise ProtocolError('truncated TLS application-data record')
|
|
34
|
+
header = (
|
|
35
|
+
bytes([_TLS_CONTENT_APPLICATION_DATA])
|
|
36
|
+
+ _TLS_LEGACY_RECORD_VERSION.to_bytes(2, 'big')
|
|
37
|
+
+ len(payload).to_bytes(2, 'big')
|
|
38
|
+
)
|
|
39
|
+
ciphertext = payload[:-16]
|
|
40
|
+
tag = payload[-16:]
|
|
41
|
+
nonce = state.next_nonce()
|
|
42
|
+
plaintext = aes_gcm_decrypt(state.key, nonce, ciphertext, tag, aad=header)
|
|
43
|
+
index = len(plaintext) - 1
|
|
44
|
+
while index >= 0 and plaintext[index] == 0:
|
|
45
|
+
index -= 1
|
|
46
|
+
if index < 0:
|
|
47
|
+
raise ProtocolError('TLS inner plaintext is missing a content type')
|
|
48
|
+
return plaintext[:index], plaintext[index]
|
|
49
|
+
|
|
50
|
+
__all__ = [name for name in globals() if not name.startswith('__')]
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from .imports import *
|
|
4
|
+
from .models import *
|
|
5
|
+
|
|
6
|
+
class PackageOwnedSSLObject:
|
|
7
|
+
def __init__(
|
|
8
|
+
self,
|
|
9
|
+
*,
|
|
10
|
+
selected_alpn_protocol: str | None,
|
|
11
|
+
cipher_suite: int,
|
|
12
|
+
peer_certificate: x509.Certificate | None,
|
|
13
|
+
) -> None:
|
|
14
|
+
self._selected_alpn_protocol = selected_alpn_protocol
|
|
15
|
+
self._cipher_suite = cipher_suite
|
|
16
|
+
self._peer_certificate = peer_certificate
|
|
17
|
+
self._peer_certificate_der = (
|
|
18
|
+
peer_certificate.public_bytes(serialization.Encoding.DER)
|
|
19
|
+
if peer_certificate is not None
|
|
20
|
+
else None
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
def selected_alpn_protocol(self) -> str | None:
|
|
24
|
+
return self._selected_alpn_protocol
|
|
25
|
+
|
|
26
|
+
def version(self) -> str:
|
|
27
|
+
return 'TLSv1.3'
|
|
28
|
+
|
|
29
|
+
def cipher(self) -> tuple[str, str, int]:
|
|
30
|
+
name, bits = _CIPHER_NAMES.get(self._cipher_suite, ('TLS_UNKNOWN', 0))
|
|
31
|
+
return name, 'TLSv1.3', bits
|
|
32
|
+
|
|
33
|
+
def getpeercert(self, binary_form: bool = False) -> dict[str, Any] | bytes | None:
|
|
34
|
+
if self._peer_certificate is None:
|
|
35
|
+
return None
|
|
36
|
+
if binary_form:
|
|
37
|
+
return self._peer_certificate_der
|
|
38
|
+
return describe_peer_certificate(self._peer_certificate)
|
|
39
|
+
|
|
40
|
+
__all__ = [name for name in globals() if not name.startswith('__')]
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from .imports import *
|
|
4
|
+
from .models import *
|
|
5
|
+
from .connection import *
|
|
6
|
+
|
|
7
|
+
async def wrap_server_tls_connection(
|
|
8
|
+
raw_reader: asyncio.StreamReader,
|
|
9
|
+
raw_writer: asyncio.StreamWriter,
|
|
10
|
+
context: ServerTLSContext,
|
|
11
|
+
) -> PackageOwnedTLSConnection:
|
|
12
|
+
connection = PackageOwnedTLSConnection(raw_reader, raw_writer, context)
|
|
13
|
+
await connection.handshake()
|
|
14
|
+
return connection
|
|
15
|
+
|
|
16
|
+
__all__ = [name for name in globals() if not name.startswith('__')]
|