57 lines
2.4 KiB
Python
57 lines
2.4 KiB
Python
"""The other side: the two evasions a client uses, both given.
|
|
|
|
Each evasion takes one record and returns a new record: the same visit, sent the
|
|
way an evading client would send it. They copy the record rather than change it,
|
|
and they change only what the client controls. The site is still the same site,
|
|
so experiments.py still counts the visit as targeted.
|
|
|
|
hide_sni is Encrypted Client Hello and use_doh is DNS over HTTPS; via_proxy is
|
|
the third, for the escalation part. Read them before you measure: part C asks what
|
|
each one hides.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import corpus
|
|
|
|
def hide_sni(record):
|
|
"""Encrypted Client Hello (ECH): hide the real server name.
|
|
|
|
With ECH the ClientHello's visible server name is the provider's public
|
|
name, and the real one is encrypted inside. Only providers that offer ECH
|
|
can do this: corpus.ECH_PUBLIC_NAME maps an address to its provider's
|
|
public name. Plain HTTP (port 80) has no ClientHello, and a site whose
|
|
provider does not offer ECH cannot use it; in both cases return an
|
|
unchanged copy.
|
|
|
|
Everything else about the record stays as it was.
|
|
"""
|
|
out = dict(record)
|
|
if record["port"] == 443 and record["ip"] in corpus.ECH_PUBLIC_NAME:
|
|
out["sni"] = corpus.ECH_PUBLIC_NAME[record["ip"]]
|
|
return out
|
|
|
|
def use_doh(record):
|
|
"""DNS over HTTPS: look the name up inside an encrypted connection to a public resolver.
|
|
|
|
The censor no longer sees a DNS query for this visit. Everything else stays
|
|
as it was.
|
|
"""
|
|
out = dict(record)
|
|
out["dns"] = None
|
|
return out
|
|
|
|
def via_proxy(record, proxy=corpus.PROXY):
|
|
"""Send the visit through an encrypted proxy: a relay the censor has not blocked.
|
|
|
|
To the censor the connection now looks like an ordinary HTTPS visit to the
|
|
proxy (proxy["host"] at proxy["ip"]): that is the name looked up, the
|
|
address, and the server name. The whole original visit, readable or not,
|
|
travels encrypted inside, so the payload must no longer be readable.
|
|
The new record keeps `id` and follows the port 443 format in corpus.py.
|
|
"""
|
|
size = len(record["payload"]) // 2 + 40
|
|
return {"id": record["id"], "port": 443, "dns": proxy["host"], "ip": proxy["ip"], "sni": proxy["host"],
|
|
"hello_bytes": record["hello_bytes"] or 512, "payload": "0" * (2 * size)}
|
|
|
|
EVASIONS = {"hide_sni": hide_sni, "use_doh": use_doh, "via_proxy": via_proxy}
|