tls_version: QUIC -> QUICv1 (#7201)

* tls_version: QUIC -> QUICv1

this aligns us with what OpenSSL is returning

* tests: add quic dumpfile
This commit is contained in:
Maximilian Hils 2024-09-21 16:22:09 +02:00 committed by GitHub
parent 8964deda8a
commit 358fca3e72
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 2290 additions and 9 deletions

View File

@ -37,6 +37,8 @@
([#7130](https://github.com/mitmproxy/mitmproxy/pull/7130), @catap)
- Fix of measurement unit in HAR import, duration is in milliseconds
([#7179](https://github.com/mitmproxy/mitmproxy/pull/7179), @dstd)
- `Connection.tls_version` now is `QUICv1` instead of `QUIC` for QUIC.
([#7201](https://github.com/mitmproxy/mitmproxy/pull/7201), @mhils)
- Add support for full mTLS with client certs between client and mitmproxy.
([#7175](https://github.com/mitmproxy/mitmproxy/pull/7175), @Kriechi)

View File

@ -347,7 +347,7 @@ class Dumper:
if self.match(f):
message = f.messages[-1]
direction = "->" if message.from_client else "<-"
if f.client_conn.tls_version == "QUIC":
if f.client_conn.tls_version == "QUICv1":
if f.type == "tcp":
quic_type = "stream"
else:

View File

@ -27,6 +27,18 @@ class ConnectionState(Flag):
TransportProtocol = Literal["tcp", "udp"]
# https://docs.openssl.org/master/man3/SSL_get_version/#return-values
TlsVersion = Literal[
"SSLv3",
"TLSv1",
"TLSv1.1",
"TLSv1.2",
"TLSv1.3",
"DTLSv0.9",
"DTLSv1",
"DTLSv1.2",
"QUICv1",
]
# practically speaking we may have IPv6 addresses with flowinfo and scope_id,
# but type checking isn't good enough to properly handle tuple unions.
@ -104,7 +116,7 @@ class Connection(serializable.SerializableDataclass, metaclass=ABCMeta):
"""The active cipher name as returned by OpenSSL's `SSL_CIPHER_get_name`."""
cipher_list: Sequence[str] = ()
"""Ciphers accepted by the proxy server on this connection."""
tls_version: str | None = None
tls_version: TlsVersion | None = None
"""The active TLS version."""
sni: str | None = None
"""

View File

@ -424,6 +424,15 @@ def convert_19_20(data):
return data
def convert_20_21(data):
data["version"] = 21
if data["client_conn"]["tls_version"] == "QUIC":
data["client_conn"]["tls_version"] = "QUICv1"
if data["server_conn"]["tls_version"] == "QUIC":
data["server_conn"]["tls_version"] = "QUICv1"
return data
def _convert_dict_keys(o: Any) -> Any:
if isinstance(o, dict):
return {strutils.always_str(k): _convert_dict_keys(v) for k, v in o.items()}
@ -488,6 +497,7 @@ converters = {
17: convert_17_18,
18: convert_18_19,
19: convert_19_20,
20: convert_20_21,
}

View File

@ -216,7 +216,7 @@ class QuicLayer(tunnel.TunnelLayer):
self.conn.certificate_list = [certs.Cert(cert) for cert in all_certs]
assert self.quic.tls.key_schedule
self.conn.cipher = self.quic.tls.key_schedule.cipher_suite.name
self.conn.tls_version = "QUIC"
self.conn.tls_version = "QUICv1"
# log the result and report the success to addons
if self.debug:

View File

@ -1,5 +1,6 @@
import struct
import time
import typing
from collections.abc import Iterator
from dataclasses import dataclass
from logging import DEBUG
@ -11,6 +12,7 @@ from OpenSSL import SSL
from mitmproxy import certs
from mitmproxy import connection
from mitmproxy.connection import TlsVersion
from mitmproxy.net.tls import starts_like_dtls_record
from mitmproxy.net.tls import starts_like_tls_record
from mitmproxy.proxy import commands
@ -377,7 +379,9 @@ class TLSLayer(tunnel.TunnelLayer):
self.conn.timestamp_tls_setup = time.time()
self.conn.alpn = self.tls.get_alpn_proto_negotiated()
self.conn.cipher = self.tls.get_cipher_name()
self.conn.tls_version = self.tls.get_protocol_version_name()
self.conn.tls_version = typing.cast(
TlsVersion, self.tls.get_protocol_version_name()
)
if self.debug:
yield commands.Log(
f"{self.debug}[tls] tls established: {self.conn}", DEBUG

View File

@ -762,7 +762,7 @@ def format_flow(
duration = f.messages[-1].timestamp - f.client_conn.timestamp_start
else:
duration = None
if f.client_conn.tls_version == "QUIC":
if f.client_conn.tls_version == "QUICv1":
protocol = "quic"
else:
protocol = f.type

View File

@ -7,7 +7,7 @@ MITMPROXY = "mitmproxy " + VERSION
# Serialization format version. This is displayed nowhere, it just needs to be incremented by one
# for each change in the file format.
FLOW_FORMAT_VERSION = 20
FLOW_FORMAT_VERSION = 21
def get_dev_version() -> str:

View File

@ -306,7 +306,7 @@ def test_quic():
d = dumper.Dumper(sio)
with taddons.context(d):
f = tflow.ttcpflow()
f.client_conn.tls_version = "QUIC"
f.client_conn.tls_version = "QUICv1"
# TODO: This should not be metadata, this should be typed attributes.
f.metadata["quic_stream_id_client"] = 1
f.metadata["quic_stream_id_server"] = 1
@ -314,7 +314,7 @@ def test_quic():
assert "quic stream 1" in sio.getvalue()
f2 = tflow.tudpflow()
f2.client_conn.tls_version = "QUIC"
f2.client_conn.tls_version = "QUICv1"
# TODO: This should not be metadata, this should be typed attributes.
f2.metadata["quic_stream_id_client"] = 1
f2.metadata["quic_stream_id_server"] = 1

File diff suppressed because one or more lines are too long

View File

@ -13,6 +13,7 @@ from mitmproxy import io
["dumpfile-7-websocket.mitm", "https://echo.websocket.org/", 6],
["dumpfile-7.mitm", "https://example.com/", 2],
["dumpfile-10.mitm", "https://example.com/", 1],
["dumpfile-19.mitm", "https://cloudflare-quic.com/", 1],
],
)
def test_load(tdata, dumpfile, url, count):

View File

@ -193,7 +193,7 @@ export const canReplay = (flow: Flow): boolean => {
export const getIcon = (flow: Flow): string => {
if (flow.type !== "http") {
if (flow.client_conn.tls_version === "QUIC") {
if (flow.client_conn.tls_version === "QUICv1") {
return `resource-icon-quic`;
}
return `resource-icon-${flow.type}`;