add support for client TLS certificates

This commit is contained in:
David Schultz 2021-12-05 18:28:52 -06:00
parent ac4c144d58
commit 7a7af60be7
No known key found for this signature in database
GPG key ID: F6DED672FFFD5E5E
6 changed files with 45 additions and 20 deletions

View file

@ -1,5 +1,5 @@
from .bot import Bot from .bot import Bot
from .server import Server from .server import Server
from .params import (ConnectionParams, SASLUserPass, SASLExternal, SASLSCRAM, from .params import (ConnectionParams, ClientTLSCertificate, SASLUserPass, SASLExternal, SASLSCRAM,
STSPolicy, ResumePolicy) STSPolicy, ResumePolicy)
from .ircv3 import Capability from .ircv3 import Capability

View file

@ -5,7 +5,7 @@ from enum import IntEnum
from ircstates import Server, Emit from ircstates import Server, Emit
from irctokens import Line, Hostmask from irctokens import Line, Hostmask
from .params import ConnectionParams, SASLParams, STSPolicy, ResumePolicy from .params import ConnectionParams, SASLParams, STSPolicy, ResumePolicy, ClientTLSCertificate
class ITCPReader(object): class ITCPReader(object):
async def read(self, byte_count: int): async def read(self, byte_count: int):
@ -28,6 +28,7 @@ class ITCPTransport(object):
port: int, port: int,
tls: bool, tls: bool,
tls_verify: bool=True, tls_verify: bool=True,
certificate: Optional[ClientTLSCertificate]=None,
bindhost: Optional[str]=None bindhost: Optional[str]=None
) -> Tuple[ITCPReader, ITCPWriter]: ) -> Tuple[ITCPReader, ITCPWriter]:
pass pass

View file

@ -28,6 +28,12 @@ class ResumePolicy(object):
address: str address: str
token: str token: str
@dataclass
class ClientTLSCertificate(object):
certfile: str
keyfile: Optional[str] = None
password: Optional[str] = None
@dataclass @dataclass
class ConnectionParams(object): class ConnectionParams(object):
nickname: str nickname: str
@ -42,6 +48,7 @@ class ConnectionParams(object):
password: Optional[str] = None password: Optional[str] = None
tls_verify: bool = True tls_verify: bool = True
sasl: Optional[SASLParams] = None sasl: Optional[SASLParams] = None
certificate: Optional[ClientTLSCertificate] = None
sts: Optional[STSPolicy] = None sts: Optional[STSPolicy] = None
resume: Optional[ResumePolicy] = None resume: Optional[ResumePolicy] = None

View file

@ -1,6 +1,13 @@
import ssl import ssl
def tls_context(verify: bool=True) -> ssl.SSLContext: from typing import Optional
from .params import ClientTLSCertificate
def tls_context(
verify: bool=True,
certificate: Optional[ClientTLSCertificate]=None
) -> ssl.SSLContext:
context = ssl.SSLContext(ssl.PROTOCOL_TLS) context = ssl.SSLContext(ssl.PROTOCOL_TLS)
context.options |= ssl.OP_NO_SSLv2 context.options |= ssl.OP_NO_SSLv2
context.options |= ssl.OP_NO_SSLv3 context.options |= ssl.OP_NO_SSLv3
@ -10,4 +17,11 @@ def tls_context(verify: bool=True) -> ssl.SSLContext:
if verify: if verify:
context.verify_mode = ssl.CERT_REQUIRED context.verify_mode = ssl.CERT_REQUIRED
if certificate is not None:
context.load_cert_chain(
certificate.certfile,
certificate.keyfile,
certificate.password
)
return context return context

View file

@ -126,6 +126,7 @@ class Server(IServer):
params.port, params.port,
tls =params.tls, tls =params.tls,
tls_verify =params.tls_verify, tls_verify =params.tls_verify,
certificate=params.certificate,
bindhost =params.bindhost) bindhost =params.bindhost)
self._reader = reader self._reader = reader

View file

@ -4,6 +4,7 @@ from asyncio import StreamReader, StreamWriter
from async_stagger import open_connection from async_stagger import open_connection
from .interface import ITCPTransport, ITCPReader, ITCPWriter from .interface import ITCPTransport, ITCPReader, ITCPWriter
from .params import ClientTLSCertificate
from .security import tls_context from .security import tls_context
class TCPReader(ITCPReader): class TCPReader(ITCPReader):
@ -36,12 +37,13 @@ class TCPTransport(ITCPTransport):
port: int, port: int,
tls: bool, tls: bool,
tls_verify: bool=True, tls_verify: bool=True,
certificate: Optional[ClientTLSCertificate]=None,
bindhost: Optional[str]=None bindhost: Optional[str]=None
) -> Tuple[ITCPReader, ITCPWriter]: ) -> Tuple[ITCPReader, ITCPWriter]:
cur_ssl: Optional[SSLContext] = None cur_ssl: Optional[SSLContext] = None
if tls: if tls:
cur_ssl = tls_context(tls_verify) cur_ssl = tls_context(tls_verify, certificate)
local_addr: Optional[Tuple[str, int]] = None local_addr: Optional[Tuple[str, int]] = None
if not bindhost is None: if not bindhost is None: