-
Notifications
You must be signed in to change notification settings - Fork 28
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
Need threaded DTLS server example #20
Comments
Hi Roman, Putting the example from the readme in an infinite loop should do. Let me see if I can find a simple example. Regards, |
Hi Mathias. By the way, tls.(D)TLSConfiguration live only and only in local namespace. |
Hi, thank you for your feedback. I will have to look into the Here are not-so-short but tested examples of a DTLS server and a DTLS client that work on my machine. The code is probably not that great but this is what I have used to implement DTLS. Listening on "0.0.0.0" for the server is important. It will not work if you only listen on "127.0.0.1" for example. This is because DTLS This is also what happens in #!/usr/bin/env python
"""Example DTLS server"""
import datetime as dt
import socket
import struct
import mbedtls.hash as hashlib
from mbedtls.pk import RSA, ECC
from mbedtls.x509 import BasicConstraints, CRT, CSR
from mbedtls.tls import *
from mbedtls.tls import _enable_debug_output, _set_debug_level
now = dt.datetime.utcnow()
digestmod = hashlib.sha256
ca0_key = RSA()
ca0_key.generate()
ca1_key = ECC()
ca1_key.generate()
ee0_key = ECC()
ee0_key.generate()
ca0_crt = CRT.selfsign(
CSR.new(ca0_key, "CN=Trusted CA", digestmod()),
ca0_key,
not_before=now,
not_after=now + dt.timedelta(days=90),
serial_number=0x123456,
basic_constraints=BasicConstraints(True, -1),
)
ca1_crt = ca0_crt.sign(
CSR.new(ca1_key, "CN=Intermediate CA", digestmod()),
ca0_key,
not_before=now,
not_after=now + dt.timedelta(days=90),
serial_number=0x234567,
basic_constraints=BasicConstraints(True, -1),
)
ee0_crt = ca1_crt.sign(
CSR.new(ee0_key, "CN=End Entity", digestmod()),
ca1_key,
not_before=now,
not_after=now + dt.timedelta(days=90),
serial_number=0x345678,
)
with open("ca0.crt", "wt") as ca:
ca.write(ca0_crt.to_PEM())
trust_store = TrustStore()
trust_store.add(ca0_crt)
def block(cb, *args, **kwargs):
while True:
try:
result = cb(*args, **kwargs)
except (WantReadError, WantWriteError):
print(" .", cb.__name__)
else:
print(" .", "done", cb.__name__, result)
return result
conf = DTLSConfiguration(
trust_store=trust_store,
certificate_chain=([ee0_crt, ca1_crt], ee0_key),
validate_certificates=False,
)
_enable_debug_output(conf)
_set_debug_level(1)
def echo_until(sock, end):
cli0, cli_address0 = sock.accept()
cli0.setcookieparam(cli_address0[0].encode("ascii"))
try:
block(cli0.do_handshake)
except HelloVerifyRequest:
print("HVR")
cli1, cli_address1 = cli0.accept()
cli0.close()
cli1.setcookieparam(cli_address1[0].encode("ascii"))
block(cli1.do_handshake)
print(" .", "handshake", cli1.negotiated_tls_version())
cli = cli1
while True:
data = block(cli.recv, 4096)
print(" .", "R", data)
nn = block(cli.send, data)
print(" .", "S", nn, len(data))
if data == end:
break
print(" .", "done")
print(cli)
cli.close()
address = ("0.0.0.0", 4433)
host, port = address
ctx = ServerContext(conf)
srv = ctx.wrap_socket(
socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
)
print(" .", "bind", srv, address)
srv.bind(address)
while True:
print(" .", ">>>")
echo_until(srv, b"\0")
print(" .", "<<<") #!/usr/bin/env python
"""Example DTLS client"""
import socket
import struct
from mbedtls.x509 import CRT
from mbedtls.tls import *
from mbedtls.tls import _enable_debug_output, _set_debug_level
with open("ca0.crt", "rt") as ca:
ca0_crt = CRT.from_PEM(ca.read())
trust_store = TrustStore()
trust_store.add(ca0_crt)
conf = DTLSConfiguration(trust_store=trust_store, validate_certificates=False)
_enable_debug_output(conf)
_set_debug_level(1)
address = ("127.0.0.1", 4433)
host, port = address
ctx = ClientContext(conf)
cli = ctx.wrap_socket(
socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP),
server_hostname="localhost",
)
print(" .", "connect", address)
cli.connect(address)
def block(cb, *args, **kwargs):
while True:
try:
result = cb(*args, **kwargs)
except (WantReadError, WantWriteError):
print(" .", cb.__name__)
else:
print(" .", "done", cb.__name__, result)
return result
block(cli.do_handshake)
print(" .", "handshake", cli.negotiated_tls_version())
msg = b"hello"
for _ in range(1):
nn = block(cli.send, msg)
print(" .", "S", nn, len(msg))
data, addr = block(cli.recvfrom, 4096)
print(" .", "R", nn, data)
else:
block(cli.send, b"\0")
block(cli.recvfrom, 4096)
print(cli)
cli.close() |
I cannot reproduce this behaviour. Could you give me a short example reproducing the bug? |
Here: import socket
from mbedtls import tls
import datetime as dt
from mbedtls import hash as hashlib
from mbedtls import pk
from mbedtls import x509
from uuid import uuid4
class DTLSCerts:
def __init__(self):
now = dt.datetime.utcnow()
self.ca0_key = pk.RSA()
_ = self.ca0_key.generate()
ca0_csr = x509.CSR.new(self.ca0_key, 'CN=thrusted CA', hashlib.sha256())
self.ca0_crt = x509.CRT.selfsign(
ca0_csr, self.ca0_key,
not_before=now, not_after=now + dt.timedelta(days=3650),
serial_number=0x123456,
basic_constraints=x509.BasicConstraints(True, 1))
self.ca1_key = pk.ECC()
_ = self.ca1_key.generate()
ca1_csr = x509.CSR.new(self.ca1_key, 'CN=intermediate CA', hashlib.sha256())
self.ca1_crt = self.ca0_crt.sign(
ca1_csr, self.ca0_key, now, now + dt.timedelta(days=3650), 0x123456,
basic_constraints=x509.BasicConstraints(ca=True, max_path_length=3))
self.srv_crt, self.srv_key = self.server_cert()
def server_cert(self):
now = dt.datetime.utcnow()
ee0_key = pk.ECC()
_ = ee0_key.generate()
ee0_csr = x509.CSR.new(ee0_key, f'CN=peer [{uuid4().hex}]', hashlib.sha256())
ee0_crt = self.ca1_crt.sign(
ee0_csr, self.ca1_key, now, now + dt.timedelta(days=3650), 0x987654)
return ee0_crt, ee0_key
dtls_certs = DTLSCerts()
trust_store = tls.TrustStore()
trust_store.add(dtls_certs.ca0_crt)
class DTLSServer:
def __init__(self, listener_timeout = 0.5, reuse_addr = True):
srv_crt, srv_key = dtls_certs.server_cert()
dtls_srv_ctx = tls.ServerContext(tls.DTLSConfiguration(
trust_store=trust_store,
certificate_chain=([srv_crt, dtls_certs.ca1_crt], srv_key),
validate_certificates=False,
))
dtls_srv = dtls_srv_ctx.wrap_socket(socket.socket(socket.AF_INET, socket.SOCK_DGRAM))
dtls_srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
dtls_srv.bind(('', 6060))
self.listener = dtls_srv
print(self.listener.context.configuration)
print(self.listener.context.configuration.certificate_chain)
def test(self):
print(self.listener.context.configuration)
print(self.listener.context.configuration.certificate_chain)
>>> s = DTLSServer()
DTLSConfiguration(validate_certificates=False, certificate_chain=((<mbedtls.x509.CRT object at 0x560ea30fc768>, <mbedtls.x509.CRT object at 0x560ea31057f8>), <mbedtls.pk.ECC object at 0x7f136a9b0508>), ciphers=[], inner_protocols=(), lowest_supported_version=<DTLSVersion.DTLSv1_0: 2>, highest_supported_version=<DTLSVersion.DTLSv1_2: 3>, trust_store=TrustStore([<mbedtls.x509.CRT object at 0x560ea3105a68>]), sni_callback=None)
((<mbedtls.x509.CRT object at 0x560ea30fc768>, <mbedtls.x509.CRT object at 0x560ea31057f8>), <mbedtls.pk.ECC object at 0x7f136a9b0508>)
>>> s.test()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 16, in test
File "src/mbedtls/tls.pyx", line 352, in mbedtls.tls._BaseConfiguration.__repr__
File "src/mbedtls/tls.pyx", line 406, in mbedtls.tls._BaseConfiguration.certificate_chain.__get__
File "src/mbedtls/x509.pyx", line 409, in mbedtls.x509.CRT.from_DER
IndexError: Out of bounds on buffer access (axis 0)
>>> print(s.listener.context)
<mbedtls.tls.ServerContext object at 0x560ea310d2f8>
>>> print(s.listener.context.configuration.certificate_chain)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "src/mbedtls/tls.pyx", line 406, in mbedtls.tls._BaseConfiguration.certificate_chain.__get__
File "src/mbedtls/x509.pyx", line 409, in mbedtls.x509.CRT.from_DER
IndexError: Out of bounds on buffer access (axis 0)
>>> print(s.listener.context.configuration.)
s.listener.context.configuration.anti_replay s.listener.context.configuration.lowest_supported_version
s.listener.context.configuration.certificate_chain s.listener.context.configuration.sni_callback
s.listener.context.configuration.ciphers s.listener.context.configuration.trust_store
s.listener.context.configuration.highest_supported_version s.listener.context.configuration.update(
s.listener.context.configuration.inner_protocols s.listener.context.configuration.validate_certificates
>>> print(s.listener.context.configuration.highest_supported_version)
DTLSVersion.DTLSv1_2 |
Therefore, on handshake server can't find own certificate and raise exception with message that there is no suitable ciphers. |
Indeed, I will look into it. Thank you. |
The very last problem probably came from here. You've defined |
|
This is correct, there is an ownership problem and I am not always reporting errors in an understandable way. I will be working on it for the next release. Thank you for your help! |
Last question. DTLS don't allow partial recv? |
This is not related with encryption but with the underlying protocols. UDP works with datagrams. You should pretty much always try to receive a full datagram or consider it lost. You can always try to pass the MSG_PEEK flag to the socket for a partial read... or use TCP. |
I thought that in DTLS integrity is monitored and something similar to the stream is provided. |
Store chain on tls config for the ref count. This closes Issue #20.
These bugs should be fixed on the master branch now. I'll release sometime this week or on the week end now. Thank you for reporting bugs! Do not hesitate to give some more feedback if you can. |
In this part of code: def echo_until(sock, end):
cli0, cli_address0 = sock.accept()
cli0.setcookieparam(cli_address0[0].encode("ascii"))
try:
block(cli0.do_handshake)
except HelloVerifyRequest:
print("HVR")
cli1, cli_address1 = cli0.accept()
cli0.close()
cli1.setcookieparam(cli_address1[0].encode("ascii"))
block(cli1.do_handshake)
print(" .", "handshake", cli1.negotiated_tls_version()) we are suddenly seeing
This MUST be documented. |
You can reuse the name def echo_until(sock, end):
cli, cli_address = sock.accept()
cli.setcookieparam(cli_address[0].encode("ascii"))
try:
block(cli.do_handshake)
except HelloVerifyRequest:
print("HVR")
cli, cli_address = cli.accept()
cli.setcookieparam(cli_address[0].encode("ascii"))
block(cli.do_handshake)
print(" .", "handshake", cli.negotiated_tls_version()) and let the ref count take care of the first connection. This is what is done in the README ( I could try to clean up my example programs and distribute them as well, like libmbedtls does. My problem with that is that extra example programs are not easy to test and I am afraid they go out of sync without notice. DTLS is tough because UDP offers so little guarantees. Datagrams may even be lost or reordered during the handshake. This makes the handshake difficult or easily abused, see RFC4347. libmbedtls makes the cookie optional (i.e. the first connection ( There really is not much I can do about it I am afraid. |
Hi.
Very need threaded server example for DTLS.
Thanks.
The text was updated successfully, but these errors were encountered: